102 lines
4.6 KiB
Python
102 lines
4.6 KiB
Python
"""Stable scoring weights and derived lifetime rank/badge rules."""
|
||
|
||
POINTS = {
|
||
"attendance": 30,
|
||
"post": 20,
|
||
"comment": 5,
|
||
"like": 1,
|
||
"save": 1,
|
||
}
|
||
|
||
LEVELS = (
|
||
{"id": "companion", "label": "همراه", "minimum_points": 0},
|
||
{"id": "contributor", "label": "مشارکتگر", "minimum_points": 30},
|
||
{"id": "ally", "label": "همیار", "minimum_points": 150},
|
||
{"id": "influencer", "label": "اثرگذار", "minimum_points": 750},
|
||
{"id": "pioneer", "label": "پیشرو", "minimum_points": 1500},
|
||
)
|
||
|
||
ACTIVITY_BADGES = {
|
||
"attendance": (
|
||
{"threshold": 1, "label": "حضور اول", "description": "حضور تأییدشده در یک رویداد"},
|
||
{"threshold": 5, "label": "همراه رویدادها", "description": "حضور تأییدشده در پنج رویداد"},
|
||
{"threshold": 10, "label": "همیار رویدادها", "description": "حضور تأییدشده در ده رویداد"},
|
||
{"threshold": 25, "label": "چهره آشنای رویدادها", "description": "حضور تأییدشده در بیستوپنج رویداد"},
|
||
),
|
||
"posts": (
|
||
{"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": 1, "label": "نخستین لایک", "description": "نخستین لایک روی یک نوشته"},
|
||
{"threshold": 10, "label": "دستگرم", "description": "ده لایک روی نوشتهها"},
|
||
{"threshold": 50, "label": "محبوب انجمن", "description": "پنجاه لایک روی نوشتهها"},
|
||
{"threshold": 100, "label": "الهامبخش", "description": "صد لایک روی نوشتهها"},
|
||
),
|
||
}
|
||
|
||
|
||
def level_for_points(points: int) -> dict:
|
||
points = max(0, int(points))
|
||
return next(level for level in reversed(LEVELS) if points >= level["minimum_points"])
|
||
|
||
|
||
def level_progress(points: int) -> dict:
|
||
points = max(0, int(points))
|
||
current_index = max(
|
||
index for index, level in enumerate(LEVELS) if points >= level["minimum_points"]
|
||
)
|
||
current = LEVELS[current_index]
|
||
next_level = LEVELS[current_index + 1] if current_index + 1 < len(LEVELS) else None
|
||
|
||
if next_level is None:
|
||
return {
|
||
"current_level": current,
|
||
"next_level": None,
|
||
"points_to_next": 0,
|
||
"progress_percent": 100,
|
||
}
|
||
|
||
span = next_level["minimum_points"] - current["minimum_points"]
|
||
progress_points = points - current["minimum_points"]
|
||
return {
|
||
"current_level": current,
|
||
"next_level": next_level,
|
||
"points_to_next": next_level["minimum_points"] - points,
|
||
"progress_percent": min(100, int(progress_points * 100 / span)),
|
||
}
|
||
|
||
|
||
def earned_badges(total_points: int, activity_counts: dict[str, int]) -> list[dict]:
|
||
badges = []
|
||
for level in LEVELS:
|
||
if total_points >= level["minimum_points"]:
|
||
badges.append({
|
||
"id": f"level-{level['id']}",
|
||
"category": "level",
|
||
"label": level["label"],
|
||
"description": f"سطح کلی با {level['minimum_points']} امتیاز",
|
||
"threshold": level["minimum_points"],
|
||
})
|
||
|
||
for category, levels in ACTIVITY_BADGES.items():
|
||
count = max(0, int(activity_counts.get(category, 0)))
|
||
badge_prefix = {"posts": "post", "comments": "comment"}.get(category, category)
|
||
for badge_level in levels:
|
||
threshold = badge_level["threshold"]
|
||
if count >= threshold:
|
||
badges.append({
|
||
"id": f"{badge_prefix}-{threshold}",
|
||
"category": category,
|
||
**badge_level,
|
||
})
|
||
return badges
|