61 lines
2.7 KiB
Python
61 lines
2.7 KiB
Python
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."))
|