diff --git a/apps/gamification/rules.py b/apps/gamification/rules.py index 614e9d8..b6437e3 100644 --- a/apps/gamification/rules.py +++ b/apps/gamification/rules.py @@ -24,19 +24,22 @@ ACTIVITY_BADGES = { {"threshold": 25, "label": "چهره آشنای رویدادها", "description": "حضور تأییدشده در بیست‌وپنج رویداد"}, ), "posts": ( - {"threshold": 1, "label": "نویسنده انجمن", "description": "انتشار یا هم‌نویسندگی یک نوشته"}, - {"threshold": 5, "label": "نویسنده فعال", "description": "انتشار یا هم‌نویسندگی پنج نوشته"}, - {"threshold": 10, "label": "نویسنده برجسته", "description": "انتشار یا هم‌نویسندگی ده نوشته"}, + {"threshold": 1, "label": "نویسنده انجمن", "description": "۱ نوشته منتشرشده"}, + {"threshold": 5, "label": "نویسنده فعال", "description": "۵ نوشته منتشرشده"}, + {"threshold": 10, "label": "نویسنده برجسته", "description": "۱۰ نوشته منتشرشده"}, + {"threshold": 25, "label": "نویسنده پیشرو", "description": "۲۵ نوشته منتشرشده"}, ), "comments": ( + {"threshold": 1, "label": "آغاز گفت‌وگو", "description": "۱ دیدگاه یا پاسخ تأییدشده"}, {"threshold": 5, "label": "اهل گفت‌وگو", "description": "پنج دیدگاه یا پاسخ تأییدشده"}, {"threshold": 20, "label": "همراه گفت‌وگو", "description": "بیست دیدگاه یا پاسخ تأییدشده"}, {"threshold": 50, "label": "صدای اثرگذار", "description": "پنجاه دیدگاه یا پاسخ تأییدشده"}, ), "likes": ( - {"threshold": 10, "label": "دست‌گرم", "description": "ده پسند داده‌شده یا دریافت‌شده"}, - {"threshold": 50, "label": "محبوب انجمن", "description": "پنجاه پسند داده‌شده یا دریافت‌شده"}, - {"threshold": 100, "label": "الهام‌بخش", "description": "صد پسند داده‌شده یا دریافت‌شده"}, + {"threshold": 1, "label": "نخستین لایک", "description": "نخستین لایک روی یک نوشته"}, + {"threshold": 10, "label": "دست‌گرم", "description": "ده لایک روی نوشته‌ها"}, + {"threshold": 50, "label": "محبوب انجمن", "description": "پنجاه لایک روی نوشته‌ها"}, + {"threshold": 100, "label": "الهام‌بخش", "description": "صد لایک روی نوشته‌ها"}, ), } @@ -86,7 +89,7 @@ def earned_badges(total_points: int, activity_counts: dict[str, int]) -> list[di for category, levels in ACTIVITY_BADGES.items(): count = max(0, int(activity_counts.get(category, 0))) - badge_prefix = "post" if category == "posts" else category + badge_prefix = {"posts": "post", "comments": "comment"}.get(category, category) for badge_level in levels: threshold = badge_level["threshold"] if count >= threshold: diff --git a/apps/gamification/services.py b/apps/gamification/services.py index 5ab90e7..a6c0fc4 100644 --- a/apps/gamification/services.py +++ b/apps/gamification/services.py @@ -155,7 +155,7 @@ def summary_for_user(user): ) activity_counts = Counter() active_effects = ( - rows.values("effect_key", "activity") + rows.values("effect_key", "activity", "role") .annotate(net_points=Sum("points")) .filter(net_points__gt=0) ) @@ -166,7 +166,7 @@ def summary_for_user(user): activity_counts["posts"] += 1 elif effect["activity"] == "comment": activity_counts["comments"] += 1 - elif effect["activity"] == "like": + elif effect["activity"] == "like" and effect["role"] == "actor": activity_counts["likes"] += 1 points_total = totals["points_total"] or 0 diff --git a/apps/gamification/tests/integration/test_api.py b/apps/gamification/tests/integration/test_api.py index bca1bbe..7d50748 100644 --- a/apps/gamification/tests/integration/test_api.py +++ b/apps/gamification/tests/integration/test_api.py @@ -26,7 +26,12 @@ def test_authenticated_summary_returns_progress_and_activity_breakdown(client, u assert summary["activity_counts"]["attendance"] == 1 assert "attendance-1" in {badge["id"] for badge in summary["badges"]} assert summary["points_per_activity"] == {"attendance": 30, "post": 20, "comment": 5, "like": 1, "save": 1} - assert summary["badge_thresholds"]["likes"] == [10, 50, 100] + assert summary["badge_thresholds"] == { + "attendance": [1, 5, 10, 25], + "posts": [1, 5, 10, 25], + "comments": [1, 5, 20, 50], + "likes": [1, 10, 50, 100], + } def test_public_profile_exposes_badges_but_never_points_even_when_details_are_hidden( diff --git a/apps/gamification/tests/integration/test_blog.py b/apps/gamification/tests/integration/test_blog.py index f0b0551..c5e9669 100644 --- a/apps/gamification/tests/integration/test_blog.py +++ b/apps/gamification/tests/integration/test_blog.py @@ -30,7 +30,8 @@ def test_publish_coauthor_like_and_save_award_expected_contributions(user_factor assert summary_for_user(coauthor)["points_total"] == 22 assert summary_for_user(actor)["points_total"] == 2 assert summary_for_user(actor)["activity_counts"]["likes"] == 1 - assert summary_for_user(coauthor)["activity_counts"]["likes"] == 1 + assert summary_for_user(author)["activity_counts"]["likes"] == 0 + assert summary_for_user(coauthor)["activity_counts"]["likes"] == 0 post.writers.remove(coauthor) post.writers.remove(author) diff --git a/apps/gamification/tests/unit/test_rules.py b/apps/gamification/tests/unit/test_rules.py index 5442f50..bbf6f0d 100644 --- a/apps/gamification/tests/unit/test_rules.py +++ b/apps/gamification/tests/unit/test_rules.py @@ -44,11 +44,22 @@ def test_highest_level_has_no_next_level_or_remaining_progress(): def test_earned_badges_include_every_reached_global_and_activity_milestone(): badges = earned_badges( total_points=300, - activity_counts={"attendance": 5, "posts": 1, "comments": 4, "likes": 50}, + activity_counts={"attendance": 5, "posts": 25, "comments": 4, "likes": 50}, ) badge_ids = {badge["id"] for badge in badges} assert {"level-companion", "level-contributor", "level-ally"} <= badge_ids - assert {"attendance-1", "attendance-5", "post-1", "likes-10", "likes-50"} <= badge_ids + assert { + "attendance-1", + "attendance-5", + "post-1", + "post-5", + "post-10", + "post-25", + "comment-1", + "likes-1", + "likes-10", + "likes-50", + } <= badge_ids assert "comment-5" not in badge_ids assert not any(badge_id.startswith("save-") for badge_id in badge_ids)