from django.db.models import ExpressionWrapper, F, FloatField, Q, Sum, Value, Window from django.db.models.functions import Coalesce, Power, Rank from events.models import Event # points = 1000 * (1 / (remaining_seconds + 0.5)) ** 2, computed per press and # summed per player. Never stored - always derived live from Press rows. POINTS_BASE = 1000.0 POINTS_OFFSET = 0.5 POINTS_EXPONENT = -2.0 def _press_points_expression(): base = ExpressionWrapper( F("clock_presses__remaining_before_press") + Value(POINTS_OFFSET), output_field=FloatField(), ) return ExpressionWrapper( Value(POINTS_BASE) * Power(base, Value(POINTS_EXPONENT)), output_field=FloatField(), ) def ranked_players(event: Event): """All users annotated with their live total `clock_points` (not `points`, that one is the market balance field on CustomUser) for this event and DB-computed RANK(). Points only ever come from presses made in `event` - a player's history in another event never leaks into this leaderboard.""" from accounts.models import CustomUser points = Coalesce( Sum(_press_points_expression(), filter=Q(clock_presses__event=event)), Value(0.0), output_field=FloatField(), ) return CustomUser.objects.annotate(clock_points=points).annotate( rank=Window(expression=Rank(), order_by=F("clock_points").desc()) )