Skip to content

Commit

Permalink
Update branch
Browse files Browse the repository at this point in the history
  • Loading branch information
Pawelooo committed Sep 19, 2024
2 parents d97cb1d + fc9b405 commit 410b9a8
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 35 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,7 @@ docker-compose.override.yml
.idea/

media/

/media/*
!/media/users_avatars/default.png

22 changes: 15 additions & 7 deletions core/management/commands/update_karma_scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@


class Command(BaseCommand):
help = "Updating karma score for every user"
help = "Updating karma scores for every user"

def handle(self: "Command", *_args: str, **_options: str) -> None:
date_limit = timezone.now() - timedelta(days=365)
karma_subquery = (
Post.objects.filter(author_id=OuterRef("user_id"), created_at__gte=date_limit)
.values("author_id")
.annotate(karma_score=Sum(F("up_votes") - F("down_votes")))
.values("karma_score")

def karma_subquery(*, parent_isnull: bool) -> Subquery:
return (
Post.objects.filter(
author_id=OuterRef("user_id"), created_at__gte=date_limit, parent__isnull=parent_isnull
)
.values("author_id")
.annotate(karma_score=Sum(F("up_votes") - F("down_votes")))
.values("karma_score")
)

Profile.objects.update(
post_karma=Coalesce(Subquery(karma_subquery(parent_isnull=True)), 0),
comment_karma=Coalesce(Subquery(karma_subquery(parent_isnull=False)), 0),
)
Profile.objects.update(karma_score=Coalesce(Subquery(karma_subquery), 0))
12 changes: 11 additions & 1 deletion core/templates/core/post-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,18 @@
<div>
<a href="#"
class="link-light link-offset-2 link-underline-opacity-0 link-opacity-75-hover link-underline-opacity-25-hover">
{{ post.author.nickname }}
{{ post.author.nickname }}
</a>
<span class="fw-bold ms-3">
pk: <span class="{% if post.author.profile.post_karma < 0 %}text-danger{% else %}text-success{% endif %}">
{{ post.author.profile.post_karma }}
</span>
</span>
<span class="fw-bold ms-3">
ck: <span class="{% if post.author.profile.comment_karma < 0 %}text-danger{% else %}text-success{% endif %}">
{{ post.author.profile.comment_karma }}
</span>
</span>
</div>
</div>
</div>
Expand Down
75 changes: 53 additions & 22 deletions core/tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,69 +18,100 @@
@pytest.mark.parametrize(
("up_votes", "down_votes", "expected_karma"),
[
(10, 2, 8),
(2, 10, -8),
(10, 2, {"post_karma": 8}), # for post
(2, 10, {"post_karma": -8}), # for post
(5, 1, {"comment_karma": 4}), # for comment
],
)
@freeze_time(FIXED_DATETIME)
def test_calculate_karma_score_one_post(
user: User, community: Community, up_votes: int, down_votes: int, expected_karma: int
def test_calculate_karma_score_post_and_comment(
user: User, community: Community, up_votes: int, down_votes: int, expected_karma: dict
) -> None:
Post.objects.create(
post = Post.objects.create(
author=user,
up_votes=up_votes,
down_votes=down_votes,
community=community,
parent=None,
created_at=timezone.now() - timedelta(days=100),
)

Post.objects.create(
author=user,
up_votes=up_votes,
down_votes=down_votes,
community=community,
parent=post,
created_at=timezone.now() - timedelta(days=50),
)

call_command("update_karma_scores")

profile = Profile.objects.filter(user_id=user.id).get()

assert profile.karma_score == expected_karma
if "post_karma" in expected_karma:
assert profile.post_karma == expected_karma["post_karma"], (
f"Expected post_karma: {expected_karma['post_karma']}, " f"but got {profile.post_karma}"
)

if "comment_karma" in expected_karma:
assert profile.comment_karma == expected_karma["comment_karma"], (
f"Expected comment_karma: {expected_karma['comment_karma']}, " f"but got {profile.comment_karma}"
)


@pytest.mark.django_db()
@freeze_time(FIXED_DATETIME)
def test_calculate_karma_score_two_posts_within_year(user: User, community: Community) -> None:
Post.objects.create(author=user, up_votes=10, down_votes=2, community=community)

Post.objects.create(author=user, up_votes=5, down_votes=0, community=community)
Post.objects.create(author=user, up_votes=10, down_votes=2, community=community, parent=None)
Post.objects.create(author=user, up_votes=5, down_votes=0, community=community, parent=None)

call_command("update_karma_scores")

profile = Profile.objects.filter(user_id=user.id).get()

assert profile.karma_score == 13
assert profile.post_karma == 13
assert profile.comment_karma == 0


@pytest.mark.django_db()
@freeze_time(FIXED_DATETIME)
def test_calculate_karma_score_two_posts_one_older_than_year(user: User, community: Community) -> None:
post1 = Post.objects.create(author=user, up_votes=10, down_votes=2, community=community)
Post.objects.filter(id=post1.id).update(created_at=timezone.now() - timedelta(days=100))

post2 = Post.objects.create(author=user, up_votes=5, down_votes=0, community=community)
Post.objects.filter(id=post2.id).update(created_at=timezone.now() - timedelta(days=500))
def test_calculate_karma_score_one_post_one_comment(user: User, community: Community) -> None:
post = Post.objects.create(author=user, up_votes=10, down_votes=2, community=community, parent=None)
Post.objects.create(author=user, up_votes=3, down_votes=1, community=community, parent=post)

call_command("update_karma_scores")

profile = Profile.objects.filter(user_id=user.id).get()

assert profile.karma_score == 8
assert profile.post_karma == 8
assert profile.comment_karma == 2


@pytest.mark.django_db()
@freeze_time(FIXED_DATETIME)
def test_calculate_karma_score_one_post_expires(user: User, community: Community) -> None:
post = Post.objects.create(author=user, up_votes=10, down_votes=2, community=community)
def test_calculate_karma_score_two_posts_one_comment_one_expires(user: User, community: Community) -> None:
post1 = Post.objects.create(author=user, up_votes=10, down_votes=2, community=community, parent=None)
Post.objects.filter(id=post1.id).update(created_at=timezone.now() - timedelta(days=100))

post2 = Post.objects.create(author=user, up_votes=5, down_votes=0, community=community, parent=None)
Post.objects.filter(id=post2.id).update(created_at=timezone.now() - timedelta(days=500))

comment = Post.objects.create(author=user, up_votes=3, down_votes=1, community=community, parent=post1)

call_command("update_karma_scores")

profile = Profile.objects.filter(user_id=user.id).get()
assert profile.karma_score == 8

Post.objects.filter(id=post.id).update(created_at=timezone.now() - timedelta(days=500))
assert profile.post_karma == 8 # post1 is within the year, post2 is not
assert profile.comment_karma == 2 # comment is within the year

# Now simulate the expiration of post1 and its comment
Post.objects.filter(id=post1.id).update(created_at=timezone.now() - timedelta(days=500))
Post.objects.filter(id=comment.id).update(created_at=timezone.now() - timedelta(days=500))

call_command("update_karma_scores")
profile.refresh_from_db()
assert profile.karma_score == 0

assert profile.post_karma == 0 # both posts are now older than a year
assert profile.comment_karma == 0 # comment is now older than a year
18 changes: 18 additions & 0 deletions core/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,3 +536,21 @@ def test_report_post_harassment(client: Client, user: User, post: Post, report_d
messages = list(get_messages(response.wsgi_request))
assert len(messages) == 1
assert str(messages[0]) == "Your post has been reported."


def test_add_non_existing_moderator(client: Client, community: Community, user: User) -> None:
admin_password = generate_random_password()

admin = User.objects.create_user(email="[email protected]", password=admin_password, nickname="adminnick")

client.force_login(admin)
CommunityMember.objects.create(community=community, user=admin, role=CommunityMember.ADMIN)

url = reverse("community-detail", kwargs={"slug": community.slug})
form_data = {"nickname": "nonexistentuser"}

response = client.post(url, {"action": "add_moderator", **form_data})
assert response.status_code == 200

messages = list(response.context["messages"])
assert any("Invalid user or nickname." in message.message for message in messages)
12 changes: 10 additions & 2 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,18 +188,26 @@ def get_context_data(self: "CommunityDetailView", **kwargs: any) -> dict[str, an
).select_related("user")
return context

def post_add_moderator(self: "CommunityDetailView", request: "HttpRequest") -> any:
def post_add_moderator(self: "CommunityDetailView", request: "HttpRequest", *args: any, **kwargs: any) -> any:
add_moderator_form = AddModeratorForm(request.POST)
if add_moderator_form.is_valid():
user = add_moderator_form.cleaned_data["nickname"]
self.object.add_moderator(user)
else:
messages.error(request, "Invalid user or nickname.")
return self.get(request, *args, **kwargs)

return redirect("community-detail", slug=self.object.slug)

def post_remove_moderator(self: "CommunityDetailView", request: "HttpRequest") -> any:
def post_remove_moderator(self: "CommunityDetailView", request: "HttpRequest", *args: any, **kwargs: any) -> any:
remove_moderator_form = RemoveModeratorForm(request.POST)
if remove_moderator_form.is_valid():
user = remove_moderator_form.cleaned_data["nickname"]
self.object.remove_moderator(user)
else:
messages.error(request, "Invalid user or nickname.")
return self.get(request, *args, **kwargs)

return redirect("community-detail", slug=self.object.slug)

def post(self: "CommunityDetailView", request: "HttpRequest", *args: any, **kwargs: any) -> any:
Expand Down
Binary file added media/users_avatars/default.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2024-09-15 14:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('users', '0017_remove_profile_is_nsfw_visible_profile_is_nsfw'),
]

operations = [
migrations.RenameField(
model_name='profile',
old_name='karma_score',
new_name='comment_karma',
),
migrations.AddField(
model_name='profile',
name='post_karma',
field=models.IntegerField(default=0),
),
]
14 changes: 14 additions & 0 deletions users/migrations/0020_merge_20240919_2146.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Generated by Django 5.1 on 2024-09-19 19:46

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('users', '0018_rename_karma_score_profile_comment_karma_and_more'),
('users', '0019_merge_20240901_1230'),
]

operations = [
]
3 changes: 2 additions & 1 deletion users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class Profile(models.Model):
is_followable = models.BooleanField(default=True)
is_content_visible = models.BooleanField(default=True)
is_communities_visible = models.BooleanField(default=True)
karma_score = models.IntegerField(default=0)
comment_karma = models.IntegerField(default=0)
post_karma = models.IntegerField(default=0)
gender = models.CharField(choices=GENDER_CHOICES, max_length=1)
user = models.OneToOneField("User", on_delete=models.CASCADE, null=False)

Expand Down
2 changes: 1 addition & 1 deletion users/templates/users/edit_profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h2 class="h2">Edit Profile</h2>
</div>
<div class="row">
<div class="col-md-2">
<img src="{{ user.get_avatar_url }}" class="img-thumbnail" alt="User Avatar">
<img src="{{ user.avatar_url }}" class="img-thumbnail" alt="User Avatar">
</div>
</div>

Expand Down
12 changes: 11 additions & 1 deletion users/templates/users/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,21 @@
<h2>User Profile</h2>
<div class="row">
<div class="col-md-4">
<img src="{{ user.get_avatar_url }}" class="img-thumbnail" alt="User Avatar">
<img src="{{ user.avatar_url }}" class="img-thumbnail" alt="User Avatar">
</div>
<div class="col-md-8">
<p><strong>Nickname:</strong> {{ user.nickname }} </p>
<p><strong>Email:</strong> {{ user.email }} </p>
<p>
<strong>Post karma:</strong>
<span class="{% if user.profile.post_karma < 0 %}text-danger{% else %}text-success{% endif %} fw-bold">
{{ user.profile.post_karma }}
</span>
<strong>Comment karma:</strong>
<span class="{% if user.profile.comment_karma < 0 %}text-danger{% else %}text-success{% endif %} fw-bold">
{{ user.profile.comment_karma }}
</span>
</p>
<a href="{% url 'edit_profile' %}" class="btn btn-primary">Edit Profile</a>
</div>
</div>
Expand Down

0 comments on commit 410b9a8

Please sign in to comment.