144 lines
4.1 KiB
Python
144 lines
4.1 KiB
Python
# apps/progression/models.py
|
||
|
||
import uuid
|
||
|
||
from django.conf import settings
|
||
from django.db import models
|
||
|
||
|
||
class TaskCompletion(models.Model):
|
||
class Status(models.TextChoices):
|
||
PENDING = "pending", "En attente"
|
||
ACCEPTED = "accepted", "Acceptée"
|
||
REJECTED = "rejected", "Refusée"
|
||
CANCELLED = "cancelled", "Annulée"
|
||
|
||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||
player = models.ForeignKey(
|
||
settings.AUTH_USER_MODEL,
|
||
on_delete=models.CASCADE,
|
||
related_name="task_completions",
|
||
)
|
||
task = models.ForeignKey(
|
||
"skills.Task",
|
||
on_delete=models.PROTECT,
|
||
related_name="completions",
|
||
)
|
||
|
||
status = models.CharField(
|
||
max_length=16,
|
||
choices=Status.choices,
|
||
default=Status.ACCEPTED,
|
||
)
|
||
|
||
completed_at = models.DateTimeField()
|
||
submitted_at = models.DateTimeField(auto_now_add=True)
|
||
|
||
note = models.TextField(blank=True)
|
||
proof_url = models.URLField(blank=True)
|
||
|
||
# Évite les doubles créations lors d’un retry de l’API.
|
||
idempotency_key = models.CharField(
|
||
max_length=100,
|
||
null=True,
|
||
blank=True,
|
||
)
|
||
|
||
# Snapshot au moment de la validation
|
||
task_name_snapshot = models.CharField(max_length=160)
|
||
xp_snapshot = models.PositiveIntegerField(default=0)
|
||
|
||
validated_by = models.ForeignKey(
|
||
settings.AUTH_USER_MODEL,
|
||
null=True,
|
||
blank=True,
|
||
on_delete=models.SET_NULL,
|
||
related_name="validated_completions",
|
||
)
|
||
validated_at = models.DateTimeField(null=True, blank=True)
|
||
|
||
class Meta:
|
||
constraints = [
|
||
models.UniqueConstraint(
|
||
fields=("player", "idempotency_key"),
|
||
condition=models.Q(idempotency_key__isnull=False),
|
||
name="unique_player_idempotency_key",
|
||
)
|
||
]
|
||
indexes = [
|
||
models.Index(fields=("player", "-completed_at")),
|
||
models.Index(fields=("task", "-completed_at")),
|
||
]
|
||
|
||
class XPTransaction(models.Model):
|
||
class Reason(models.TextChoices):
|
||
TASK = "task", "Réalisation d’une tâche"
|
||
ACHIEVEMENT = "achievement", "Succès"
|
||
BONUS = "bonus", "Bonus"
|
||
CORRECTION = "correction", "Correction"
|
||
REVERSAL = "reversal", "Annulation"
|
||
|
||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||
player = models.ForeignKey(
|
||
settings.AUTH_USER_MODEL,
|
||
on_delete=models.CASCADE,
|
||
related_name="xp_transactions",
|
||
)
|
||
skill = models.ForeignKey(
|
||
"skills.Skill",
|
||
on_delete=models.PROTECT,
|
||
related_name="xp_transactions",
|
||
)
|
||
|
||
# Signé : positif ou négatif
|
||
amount = models.IntegerField()
|
||
reason = models.CharField(max_length=20, choices=Reason.choices)
|
||
|
||
completion = models.ForeignKey(
|
||
TaskCompletion,
|
||
null=True,
|
||
blank=True,
|
||
on_delete=models.PROTECT,
|
||
related_name="xp_transactions",
|
||
)
|
||
reversed_transaction = models.OneToOneField(
|
||
"self",
|
||
null=True,
|
||
blank=True,
|
||
on_delete=models.PROTECT,
|
||
related_name="reversal",
|
||
)
|
||
|
||
metadata = models.JSONField(default=dict, blank=True)
|
||
created_at = models.DateTimeField(auto_now_add=True)
|
||
|
||
class Meta:
|
||
indexes = [
|
||
models.Index(fields=("player", "skill")),
|
||
models.Index(fields=("player", "-created_at")),
|
||
]
|
||
|
||
class PlayerSkillProgress(models.Model):
|
||
player = models.ForeignKey(
|
||
settings.AUTH_USER_MODEL,
|
||
on_delete=models.CASCADE,
|
||
related_name="skill_progress",
|
||
)
|
||
skill = models.ForeignKey(
|
||
"skills.Skill",
|
||
on_delete=models.CASCADE,
|
||
related_name="player_progress",
|
||
)
|
||
xp = models.PositiveBigIntegerField(default=0)
|
||
level = models.PositiveIntegerField(default=0)
|
||
unlocked_at = models.DateTimeField(null=True, blank=True)
|
||
updated_at = models.DateTimeField(auto_now=True)
|
||
|
||
class Meta:
|
||
constraints = [
|
||
models.UniqueConstraint(
|
||
fields=("player", "skill"),
|
||
name="unique_player_skill_progress",
|
||
)
|
||
]
|