| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- from __future__ import annotations
- from typing import Optional
- from django.contrib.auth import get_user_model
- from django.db.models import Sum, F, Max, Q, Window
- from django.db.models.functions import Rank
- from django.utils import timezone
- from api.models import BenefitEvent
- User = get_user_model()
- # Default weights per action kind
- BENEFIT_WEIGHTS = {
- BenefitEvent.KIND_ACCEPTED_ANSWER: 10,
- BenefitEvent.KIND_REVIEW_HELPFUL: 3,
- BenefitEvent.KIND_REFERRAL: 20,
- BenefitEvent.KIND_RECOMMENDATION: 5,
- }
- def log_benefit_event(
- *,
- benefactor: User,
- beneficiary: User,
- kind: str,
- points: Optional[int] = None,
- meta: Optional[dict] = None,
- ) -> BenefitEvent:
- if points is None:
- points = BENEFIT_WEIGHTS.get(kind, 1)
- event = BenefitEvent(
- benefactor=benefactor,
- beneficiary=beneficiary,
- kind=kind,
- points=points,
- meta=meta or {},
- )
- event.save()
- return event
- def _period_start(period: str):
- period = (period or "").lower()
- if period in ("all", "all_time", "alltime", ""):
- return None
- now = timezone.now()
- if period in ("week", "weekly", "7d"):
- return now - timezone.timedelta(days=7)
- if period in ("month", "monthly", "30d"):
- return now - timezone.timedelta(days=30)
- # default fallback: weekly
- return now - timezone.timedelta(days=7)
- def build_leaderboard_queryset(period: str = "weekly"):
- start = _period_start(period)
- pf = Q()
- if start is not None:
- pf = Q(benefit_given__created_at__gte=start)
- qs = (
- User.objects
- .annotate(
- points_given=Sum('benefit_given__points', filter=pf),
- last_help_at=Max('benefit_given__created_at', filter=pf),
- )
- )
- if start is not None:
- qs = qs.filter(benefit_given__created_at__gte=start)
- qs = (
- qs.filter(points_given__gt=0)
- .annotate(
- rank=Window(
- expression=Rank(),
- order_by=(
- F('points_given').desc(nulls_last=True),
- F('last_help_at').asc(nulls_last=True),
- F('id').asc(),
- ),
- )
- )
- .order_by('rank')
- )
- return qs
|