Files
guilan-ace-backend/apps/gamification/management/commands/backfill_event_attendance.py

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."))