feat: add community contribution ranks
This commit is contained in:
1
apps/gamification/management/commands/__init__.py
Normal file
1
apps/gamification/management/commands/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
from collections import Counter
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import transaction
|
||||
from django.utils import timezone
|
||||
|
||||
from apps.events.models import Registration
|
||||
from apps.gamification.rules import ACTIVITY_BADGES, LEVELS, POINTS, level_for_points
|
||||
from apps.gamification.services import sync_registration_attendance
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Report or backfill lifetime points from existing attended event registrations."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--apply",
|
||||
action="store_true",
|
||||
help="Write historical attendance provenance and ledger awards; default is read-only.",
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
registrations = list(
|
||||
Registration.objects.filter(status=Registration.StatusChoices.ATTENDED)
|
||||
.select_related("user")
|
||||
.order_by("user_id", "id")
|
||||
)
|
||||
attendance_counts = Counter(registration.user_id for registration in registrations)
|
||||
level_counts = Counter()
|
||||
for count in attendance_counts.values():
|
||||
level_counts[level_for_points(count * POINTS["attendance"])["id"]] += 1
|
||||
|
||||
self.stdout.write(f"Attended registrations eligible: {len(registrations)}")
|
||||
self.stdout.write(f"Users with attended registrations: {len(attendance_counts)}")
|
||||
self.stdout.write("Projected level distribution (event history only):")
|
||||
for level in LEVELS:
|
||||
self.stdout.write(f" {level['id']}: {level_counts[level['id']]}")
|
||||
self.stdout.write("Projected attendance badges (threshold -> users):")
|
||||
for badge in ACTIVITY_BADGES["attendance"]:
|
||||
members = sum(count >= badge["threshold"] for count in attendance_counts.values())
|
||||
self.stdout.write(f" {badge['threshold']}: {members}")
|
||||
|
||||
if not options["apply"]:
|
||||
self.stdout.write(self.style.WARNING("Dry run only; no records were changed. Pass --apply to backfill."))
|
||||
return
|
||||
|
||||
verified_at = timezone.now()
|
||||
with transaction.atomic():
|
||||
for registration in registrations:
|
||||
if not registration.attendance_verified_at:
|
||||
registration.attendance_verified_at = verified_at
|
||||
registration.attendance_verification_source = Registration.AttendanceVerificationSource.HISTORICAL
|
||||
registration.save(update_fields=[
|
||||
"attendance_verified_at",
|
||||
"attendance_verification_source",
|
||||
"updated_at",
|
||||
])
|
||||
sync_registration_attendance(registration, trust_legacy=True)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(f"Backfilled {len(registrations)} attended registrations."))
|
||||
Reference in New Issue
Block a user