# ============================================ # apps/users/models.py - Gestion Utilisateurs # ============================================ from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.db import models from apps.core.models import TimestampMixin, SoftDeleteMixin, VersionedFieldMixin class UserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): if not email: raise ValueError('L\'adresse email est obligatoire') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) extra_fields.setdefault('role', 'ADMIN') return self.create_user(email, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin, TimestampMixin, SoftDeleteMixin): """Modèle utilisateur personnalisé""" ROLE_CHOICES = [ ('STUDENT', 'Étudiant'), ('MENTOR', 'Mentor'), ('ADMIN', 'Administrateur'), ] email = models.EmailField(unique=True, db_index=True) role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='STUDENT', db_index=True) points = models.IntegerField(default=0, db_index=True) is_staff = models.BooleanField(default=False) email_verified = models.BooleanField(default=False) last_login_ip = models.GenericIPAddressField(null=True, blank=True) objects = UserManager() USERNAME_FIELD = 'email' REQUIRED_FIELDS = [] class Meta: db_table = 'users' verbose_name = 'Utilisateur' verbose_name_plural = 'Utilisateurs' indexes = [ models.Index(fields=['email', 'is_active']), models.Index(fields=['role', 'is_active']), models.Index(fields=['points'], name='idx_user_points'), ] def __str__(self): return self.email def add_points(self, points, reason=''): """Ajouter des points avec traçabilité""" from apps.gamification.models import UserPointsHistory previous_total = self.points self.points += points self.save() UserPointsHistory.objects.create( user=self, points_change=points, previous_total=previous_total, new_total=self.points, reason=reason ) return self.points class UserProfile(TimestampMixin, VersionedFieldMixin): """Profil utilisateur versionné""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='profiles') name = models.CharField(max_length=255) public_key = models.TextField(null=True, blank=True) encrypted_private_key = models.TextField(null=True, blank=True) class Meta: db_table = 'user_profiles' verbose_name = 'Profil Utilisateur' verbose_name_plural = 'Profils Utilisateurs' indexes = [ models.Index(fields=['user', 'is_current']), ] def __str__(self): return f"{self.name} ({self.user.email})" def save(self, *args, **kwargs): if self.is_current: UserProfile.objects.filter(user=self.user, is_current=True).update(is_current=False) super().save(*args, **kwargs) class UserAvatar(TimestampMixin, VersionedFieldMixin): profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='avatars') avatar_url = models.URLField(max_length=500) class Meta: db_table = 'user_avatars' class UserCountry(TimestampMixin, VersionedFieldMixin): profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='countries') country = models.CharField(max_length=100) class Meta: db_table = 'user_countries' class UserUniversity(TimestampMixin, VersionedFieldMixin): profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='universities') university = models.CharField(max_length=255) class Meta: db_table = 'user_universities'