101 lines
3.9 KiB
Python
101 lines
3.9 KiB
Python
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.')
|