from django.db import models from django.template.defaultfilters import slugify from django.urls import reverse from django.utils import timezone class Event(models.Model): title = models.CharField(max_length=180) slug = models.SlugField(unique=True, max_length=200, blank=True) summary = models.CharField(max_length=260) description = models.TextField() venue = models.CharField(max_length=180) start_at = models.DateTimeField() end_at = models.DateTimeField() capacity = models.PositiveIntegerField(blank=True, null=True) is_published = models.BooleanField(default=True) registration_opens = models.DateTimeField(blank=True, null=True) registration_closes = models.DateTimeField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['start_at'] def __str__(self): return self.title def save(self, *args, **kwargs): if not self.slug: base_slug = slugify(self.title)[:180] or 'event' slug = base_slug counter = 2 while Event.objects.exclude(pk=self.pk).filter(slug=slug).exists(): slug = f'{base_slug}-{counter}'[:200] counter += 1 self.slug = slug super().save(*args, **kwargs) def get_absolute_url(self): return reverse('event_detail', args=[self.slug]) @property def confirmed_registrations_count(self): return self.registrations.filter(status=Registration.Status.CONFIRMED).count() @property def waitlist_count(self): return self.registrations.filter(status=Registration.Status.WAITLIST).count() @property def spots_remaining(self): if self.capacity is None: return None return max(self.capacity - self.confirmed_registrations_count, 0) @property def registration_is_open(self): now = timezone.now() if not self.is_published: return False if self.registration_opens and now < self.registration_opens: return False if self.registration_closes and now > self.registration_closes: return False return True class Registration(models.Model): class Status(models.TextChoices): CONFIRMED = 'confirmed', 'Confirmed' WAITLIST = 'waitlist', 'Waitlist' event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name='registrations') full_name = models.CharField(max_length=160) email = models.EmailField() company = models.CharField(max_length=160, blank=True) notes = models.TextField(blank=True) status = models.CharField(max_length=24, choices=Status.choices, default=Status.CONFIRMED) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['created_at'] constraints = [ models.UniqueConstraint(fields=['event', 'email'], name='unique_registration_per_event_email'), ] def __str__(self): return f'{self.full_name} ยท {self.event.title}'