from django.db import models from django.core.exceptions import ValidationError from accounts.models import BaseModel, IAmPrincipal from django.db import transaction from taggit.managers import TaggableManager class FreeUsageFeatureLimit(models.Model): category_limit = models.PositiveIntegerField( default=3, help_text="The maximum number of categories that free app users can select." ) class Meta: db_table = "free_usage_feature_limit" verbose_name = "Free Usage Feature Limit" verbose_name_plural = "Free Usage Feature Limits" def __str__(self): return f"Free usage limit: {self.category_limit} categories" def save(self, *args, **kwargs): if not self.pk and FreeUsageFeatureLimit.objects.exists(): raise ValidationError("There can only be one FreeUsageFeatureLimit instance.") return super().save(*args, **kwargs) @classmethod def get_category_limit(cls): return cls.objects.values_list('category_limit', flat=True).first() class EventCategory(BaseModel): title = models.CharField(max_length=255) image = models.ImageField(upload_to="event_category", null=True, blank=True) description = models.TextField(null=True, blank=True) video_url = models.URLField(max_length=200, blank=True, null=True) def __str__(self): return self.title class Venue(BaseModel): principal = models.ForeignKey(IAmPrincipal, related_name="venues_principal", on_delete=models.CASCADE, null=True) title = models.CharField(max_length=255) description = models.TextField(null=True, blank=True) address = models.TextField(null=True, blank=True) image = models.ImageField(upload_to="venue", null=True, blank=True) url = models.URLField(max_length=200, blank=True, null=True) latitude = models.DecimalField( max_digits=14, decimal_places=8, blank=True, null=True ) longitude = models.DecimalField( max_digits=14, decimal_places=8, blank=True, null=True ) postcode = models.CharField(max_length=20, blank=True, null=True) def __str__(self): return self.title class AgeGroups(BaseModel): name = models.CharField(max_length=10, unique=True) class Meta: db_table = "age_group" def __str__(self): return self.name class EventStatus(models.TextChoices): UPCOMING = "upcoming", "Upcoming" LIVE = "live", "Live" POST = "post", "Post" ARCHIVE = "archive", "Archive" class EventMaster(BaseModel): title = models.CharField(max_length=255) description = models.TextField(blank=True, null=True) event_category = models.ForeignKey(EventCategory, on_delete=models.CASCADE) image = models.ImageField(upload_to="brand", null=True, blank=True) def __str__(self): return self.title class Meta: db_table = "brand" class Event(BaseModel): ENTRY_TYPE_CHOICES = [ ("free", "Free"), ("paid", "Paid"), ] principal = models.ForeignKey(IAmPrincipal, related_name="events_principal", on_delete=models.CASCADE, null=True) title = models.CharField(max_length=255) category = models.ForeignKey(EventCategory, on_delete=models.CASCADE) event_master = models.ForeignKey( EventMaster, on_delete=models.SET_NULL, null=True, blank=True ) description = models.TextField(blank=True, null=True) image = models.ImageField(upload_to="event", null=True, blank=True) status = models.CharField( max_length=10, choices=EventStatus.choices, blank=True, null=True ) start_date = models.DateField() end_date = models.DateField() from_time = models.TimeField() to_time = models.TimeField() venue = models.ForeignKey(Venue, on_delete=models.CASCADE) venue_capacity = models.IntegerField() video_url = models.URLField(max_length=200, blank=True, null=True) entry_type = models.CharField( max_length=10, choices=ENTRY_TYPE_CHOICES, ) entry_fee = models.DecimalField( max_digits=14, decimal_places=2, default=0.00 ) key_guest = models.TextField(blank=True, null=True) tags = TaggableManager(blank=True) age_group = models.CharField(max_length=100, blank=True, null=True) draft = models.BooleanField(default=False) social_media_shares_count = models.IntegerField(default=0) coupon_code = models.CharField(max_length=255, blank=True, null=True) coupon_description = models.TextField(blank=True, null=True) link = models.URLField(max_length=255, blank=True, null=True) def increment_shares(self): self.social_media_shares_count += 1 self.save() def set_key_guests(self, guests): """Set the key guests as a comma-seperated string.""" if isinstance(guests, list): self.key_guest = ",".join(guests) elif isinstance(guests, str): self.key_guest = guests else: raise ValueError("Guests must be a comma-seperated string") def get_key_guests(self): """Return the key guests as a list of strings.""" return self.key_guest.split(",") if self.key_guest else [] def __str__(self): return self.title class Meta: db_table = "event" # Optional: Specify custom table name class EventInteractionType(models.TextChoices): GOING = "going", "Going" INTERESTED = "interested", "Interested" class EventImage(models.Model): event = models.ForeignKey( Event, related_name="event_images", on_delete=models.CASCADE ) image = models.ImageField(upload_to="event_images/") def __str__(self): return f"Image for event: {self.event.title}" class EventPrincipalInteraction(models.Model): principal = models.ForeignKey( IAmPrincipal, related_name="principal_event_interaction", on_delete=models.CASCADE, ) event = models.ForeignKey( Event, related_name="interaction_event", on_delete=models.CASCADE ) status = models.CharField( max_length=10, choices=EventInteractionType.choices, ) class Meta: unique_together = ("principal", "event") class PrincipalPreference(BaseModel): principal = models.OneToOneField(IAmPrincipal, on_delete=models.CASCADE) preferred_categories = models.ManyToManyField( EventCategory, related_name="preferred_by_users" ) def __str__(self): return str(self.preferred_categories.name) class Meta: db_table = "user_preference" class Favorites(BaseModel): principal = models.ForeignKey( IAmPrincipal, on_delete=models.CASCADE, related_name="favorited_by" ) event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="favorites") class Meta: unique_together = ("principal", "event") def __str__(self): return f"{self.principal}'s favorite: {self.event.title}" class EventReview(BaseModel): event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="reviews") principal = models.ForeignKey( IAmPrincipal, on_delete=models.CASCADE, related_name="event_reviews" ) review_text = models.TextField() rating = models.PositiveSmallIntegerField( choices=[ (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), ] ) class Meta: # sort reviews, to show latest first by default: ordering = ["-created_on"] def __str__(self): return f"Review by {self.principal} on {self.event}" class EventView(BaseModel): event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="views") principal = models.ForeignKey( IAmPrincipal, on_delete=models.CASCADE, related_name="event_views" ) view_date = models.DateTimeField(auto_now_add=True) location = models.CharField( max_length=255, blank=True, null=True ) # Or use a more complex field for location data def __str__(self): return f"{self.principal.email} viewed {self.event.title} from {self.location}" class EventShare(BaseModel): event = models.ForeignKey( Event, on_delete=models.CASCADE, related_name="social_media_shares" ) principal = models.ForeignKey( IAmPrincipal, on_delete=models.CASCADE, related_name="event_shares" )