from django.db import models from django.contrib.auth.models import User from django.core.exceptions import ValidationError class Group(models.Model): GROUP_TYPE_CHOICES = [ ('squad', 'Squad (4–12 members)'), ('community', 'Community (Open)'), ('mastermind', 'Pro Squad (Structured)'), ('tournament', 'Tournament Team'), ('private', 'Private Invite'), ] INTENT_TYPE_CHOICES = [ ('ranked', 'Ranked Squad'), ('duo', 'Duo'), ('casual', 'Casual'), ('tournament', 'Tournament'), ('practice', 'Practice'), ] VISIBILITY_CHOICES = [ ('public_members', 'Public (Members Only)'), ('private_request', 'Private (Request to Join)'), ('invite_only', 'Invite Only'), ] LOCATION_SCOPE_CHOICES = [ ('local', 'Local'), ('virtual', 'Virtual'), ] name = models.CharField(max_length=255) description = models.TextField() group_type = models.CharField(max_length=50, choices=GROUP_TYPE_CHOICES) intent_type = models.CharField(max_length=50, choices=INTENT_TYPE_CHOICES) focus_game = models.CharField(max_length=100, blank=True, null=True) visibility = models.CharField(max_length=50, choices=VISIBILITY_CHOICES, default='public_members') capacity = models.PositiveIntegerField(null=True, blank=True) location_scope = models.CharField(max_length=20, choices=LOCATION_SCOPE_CHOICES, default='local') city = models.CharField(max_length=100, blank=True, null=True) state = models.CharField(max_length=100, blank=True, null=True) recurring_schedule = models.TextField(blank=True, null=True, help_text="Required for Mastermind groups. Use structured text or JSON.") created_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='created_groups_v2') created_at = models.DateTimeField(auto_now_add=True) cover_image = models.ImageField(upload_to='groups/covers/', blank=True, null=True) def __str__(self): return str(self.name) def clean(self): super().clean() if self.group_type == 'circle': if self.capacity and self.capacity > 12: raise ValidationError({'capacity': 'Circles cannot have more than 12 members.'}) if not self.capacity: self.capacity = 12 if self.group_type == 'mastermind' and not self.recurring_schedule: raise ValidationError({'recurring_schedule': 'Mastermind groups require a structured schedule.'}) def save(self, *args, **kwargs): self.full_clean() super().save(*args, **kwargs) class GroupMember(models.Model): ROLE_CHOICES = [ ('member', 'Member'), ('moderator', 'Moderator'), ('owner', 'Owner'), ] STATUS_CHOICES = [ ('active', 'Active'), ('pending', 'Pending'), ] group = models.ForeignKey(Group, on_delete=models.CASCADE, related_name='memberships') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='group_memberships') role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='member') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active') joined_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ('group', 'user') def __str__(self): return f"{self.user.username} in {self.group.name}" def save(self, *args, **kwargs): self.full_clean() super().save(*args, **kwargs) def clean(self): super().clean() if self.status == 'active': # Use filter().count() instead of exclude() for new objects current_count = GroupMember.objects.filter(group=self.group, status='active').exclude(id=self.id).count() if self.group.capacity and current_count >= self.group.capacity: raise ValidationError('This group has reached its maximum capacity.')