from django.contrib.auth import get_user_model from django.core import validators from django.db import models class LockerBank(models.Model): """A set of locker units, placed together""" name = models.CharField(max_length=200) location = models.CharField(max_length=200) slug = models.SlugField(unique=True) def __str__(self): return f"{self.name} ({self.units.count()} units)" class LockerUnit(models.Model): """A standalone set of lockers""" bank = models.ForeignKey( LockerBank, on_delete=models.SET_NULL, related_name="units", null=True ) index = models.PositiveIntegerField() first_letter = models.CharField( max_length=1, validators=[validators.RegexValidator("[A-Z]")], unique=True ) first_number = models.PositiveIntegerField() rows = models.PositiveIntegerField(default=5) columns = models.PositiveIntegerField(default=2) class Meta: # TODO: add constraint to check for letter overlaps constraints = [ models.UniqueConstraint(fields=["bank", "index"], name="unique_bank_index") ] ordering = ["index"] def letter_for_column(self, column: int) -> str: return chr(column + ord(self.first_letter)) def number_for_locker(self, column: int, row: int) -> int: return row + self.first_number + column * self.rows @property def iter_doors(self): for row in range(self.rows): for column in range(self.columns): # TODO: filter could be optimized yield ( self.letter_for_column(column), self.number_for_locker(column, row), self.rentals.filter(row=row, column=column), ) # TODO: add check constraint to ensure that column and number are within locker_unit bounds # TODO: add unique constraint on (unit, row, column)? class LockerRental(models.Model): """A rental of a single locker""" locker_unit = models.ForeignKey( LockerUnit, on_delete=models.CASCADE, related_name="rentals" ) column = models.PositiveIntegerField() row = models.PositiveIntegerField() user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) @property def address(self) -> str: letter = self.locker_unit.letter_for_column(self.column) number = self.locker_unit.number_for_locker(self.column, self.row) return f"{self.locker_unit}-{letter}{number}" def __str__(self): return f"{self.user}: {self.address}"