""" ============================================== MODELS COMPLETS PART 2 - EDUCONNECT AFRICA API ============================================== """ # ============================================ # apps/gamification/models.py - Système Gamification # ============================================ from django.db import models from apps.core.models import TimestampMixin, SoftDeleteMixin, VersionedFieldMixin from apps.users.models import User class Badge(TimestampMixin, SoftDeleteMixin): """Badge déblocable""" code = models.SlugField(unique=True, max_length=50, db_index=True) class Meta: db_table = 'badges' verbose_name = 'Badge' verbose_name_plural = 'Badges' def __str__(self): name = self.names.filter(is_current=True).first() return name.name if name else self.code class BadgeName(TimestampMixin, VersionedFieldMixin): badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name='names') name = models.CharField(max_length=100) class Meta: db_table = 'badge_names' class BadgeDescription(TimestampMixin, VersionedFieldMixin): badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name='descriptions') description = models.TextField() class Meta: db_table = 'badge_descriptions' class BadgeIcon(TimestampMixin, VersionedFieldMixin): badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name='icons') icon = models.CharField(max_length=100) # Emoji ou path class Meta: db_table = 'badge_icons' class BadgeColor(TimestampMixin, VersionedFieldMixin): badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name='colors') color = models.CharField(max_length=50) # CSS class ou hex class Meta: db_table = 'badge_colors' class UserBadge(TimestampMixin, SoftDeleteMixin): """Attribution de badge à un utilisateur""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_badges') badge = models.ForeignKey(Badge, on_delete=models.CASCADE) awarded_at = models.DateTimeField(auto_now_add=True, db_index=True) class Meta: db_table = 'user_badges' verbose_name = 'Badge Utilisateur' verbose_name_plural = 'Badges Utilisateurs' unique_together = ['user', 'badge', 'is_active'] ordering = ['-awarded_at'] def __str__(self): return f"{self.user.email} - {self.badge.code}" class BadgeCriteria(TimestampMixin, SoftDeleteMixin): """Règles d'attribution automatique""" CRITERIA_TYPES = [ ('POINTS_THRESHOLD', 'Seuil de points'), ('FIRST_ACTION', 'Première action'), ('ACTION_COUNT', 'Nombre d\'actions'), ('STREAK', 'Série consécutive'), ] badge = models.ForeignKey(Badge, on_delete=models.CASCADE, related_name='criteria') criteria_type = models.CharField(max_length=50, choices=CRITERIA_TYPES) criteria_value = models.JSONField() # Ex: {"points": 5000} ou {"action": "first_question"} class Meta: db_table = 'badge_criteria' verbose_name = 'Critère Badge' verbose_name_plural = 'Critères Badges' class UserPointsHistory(TimestampMixin): """Historique complet des changements de points""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='points_history') points_change = models.IntegerField() previous_total = models.IntegerField() new_total = models.IntegerField() reason = models.CharField(max_length=255, db_index=True) related_content_type = models.CharField(max_length=50, null=True, blank=True) related_object_id = models.IntegerField(null=True, blank=True) class Meta: db_table = 'user_points_history' verbose_name = 'Historique Points' verbose_name_plural = 'Historiques Points' indexes = [ models.Index(fields=['user', '-created_at']), models.Index(fields=['reason']), ] ordering = ['-created_at']