generated from bisco/codex-bootstrap
95 lines
3.1 KiB
Python
95 lines
3.1 KiB
Python
from django.db import models
|
|
from django.db.models import F, Q, Sum
|
|
|
|
|
|
class TimeStampedModel(models.Model):
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
class Show(TimeStampedModel):
|
|
title = models.CharField(max_length=200)
|
|
slug = models.SlugField(max_length=220, unique=True)
|
|
summary = models.TextField(blank=True)
|
|
description = models.TextField(blank=True)
|
|
poster_image = models.URLField(blank=True)
|
|
uploaded_image = models.ImageField(upload_to="shows/", blank=True)
|
|
is_published = models.BooleanField(default=False, db_index=True)
|
|
|
|
class Meta:
|
|
ordering = ["title"]
|
|
indexes = [
|
|
models.Index(fields=["slug"]),
|
|
models.Index(fields=["is_published"]),
|
|
]
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
def image_url(self, request=None):
|
|
if self.uploaded_image:
|
|
image_url = self.uploaded_image.url
|
|
if request is not None:
|
|
return request.build_absolute_uri(image_url)
|
|
return image_url
|
|
return self.poster_image
|
|
|
|
|
|
class Venue(TimeStampedModel):
|
|
name = models.CharField(max_length=200)
|
|
slug = models.SlugField(max_length=220, unique=True)
|
|
address = models.CharField(max_length=255)
|
|
city = models.CharField(max_length=120)
|
|
notes = models.TextField(blank=True)
|
|
|
|
class Meta:
|
|
ordering = ["name"]
|
|
indexes = [
|
|
models.Index(fields=["slug"]),
|
|
models.Index(fields=["city"]),
|
|
]
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
|
|
class Performance(TimeStampedModel):
|
|
show = models.ForeignKey(Show, on_delete=models.PROTECT, related_name="performances")
|
|
venue = models.ForeignKey(Venue, on_delete=models.PROTECT, related_name="performances")
|
|
starts_at = models.DateTimeField(db_index=True)
|
|
room_capacity = models.PositiveIntegerField()
|
|
manually_occupied_seats = models.PositiveIntegerField(default=0)
|
|
additional_seats = models.PositiveIntegerField(default=0)
|
|
is_booking_enabled = models.BooleanField(default=True, db_index=True)
|
|
|
|
class Meta:
|
|
ordering = ["starts_at"]
|
|
indexes = [
|
|
models.Index(fields=["show", "starts_at"]),
|
|
models.Index(fields=["venue", "starts_at"]),
|
|
models.Index(fields=["is_booking_enabled", "starts_at"]),
|
|
]
|
|
constraints = [
|
|
models.CheckConstraint(
|
|
condition=Q(manually_occupied_seats__lte=F("room_capacity") + F("additional_seats")),
|
|
name="performance_manual_seats_within_capacity",
|
|
),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.show} at {self.starts_at:%Y-%m-%d %H:%M}"
|
|
|
|
@property
|
|
def configured_capacity(self):
|
|
return self.room_capacity + self.additional_seats - self.manually_occupied_seats
|
|
|
|
def confirmed_seats(self):
|
|
result = self.reservations.filter(status="confirmed").aggregate(total=Sum("party_size"))
|
|
return result["total"] or 0
|
|
|
|
def available_seats(self):
|
|
return self.configured_capacity - self.confirmed_seats()
|