import uuid from django.conf import settings from django.db import models from django.db.models import Q class ContributionLedger(models.Model): class Activity(models.TextChoices): ATTENDANCE = "attendance", "Event attendance" POST = "post", "Published post" COMMENT = "comment", "Approved comment or reply" LIKE = "like", "Like given or received" SAVE = "save", "Save given or received" user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="contribution_ledger_entries", ) activity = models.CharField(max_length=20, choices=Activity.choices) source_type = models.CharField(max_length=32) source_id = models.CharField(max_length=80) effect_key = models.CharField(max_length=240, db_index=True) role = models.CharField(max_length=20) points = models.IntegerField() idempotency_key = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) reversal_of = models.ForeignKey( "self", null=True, blank=True, on_delete=models.PROTECT, related_name="reversals", ) reason = models.CharField(max_length=120, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["created_at", "id"] indexes = [ models.Index(fields=["user", "activity"]), models.Index(fields=["source_type", "source_id"]), ] constraints = [ models.CheckConstraint(condition=~Q(points=0), name="gamification_nonzero_ledger_points"), ] class ScoringEpoch(models.Model): """The launch boundary for blog scoring; historical blog rows remain unscored.""" id = models.PositiveSmallIntegerField(primary_key=True, default=1, editable=False) started_at = models.DateTimeField()