from datetime import date, timedelta from typing import Optional from uuid import uuid4 from django.db import models, IntegrityError from django.utils import timezone import library class PersonQuerySet(models.QuerySet): """ Manager for people. """ def employees(self) -> models.QuerySet: """Get only employees.""" return self.filter(role=Person.Role.EMPLOYEE) def clients(self) -> models.QuerySet: """Get only clients.""" return self.filter(role=Person.Role.CLIENT) class Person(models.Model): """ Base class for people, employees and clients. """ class Role(models.IntegerChoices): EMPLOYEE = 0, "employee" CLIENT = 1, "client" role = models.PositiveSmallIntegerField(default=0, choices=Role.choices, db_index=True, verbose_name="role") user = models.OneToOneField("auth.User", on_delete=models.SET_NULL, null=True, blank=True, related_name="person") uuid = models.UUIDField(default=uuid4, db_index=True, verbose_name="UUID") first_name = models.CharField(max_length=64, blank=False, verbose_name="first name") last_name = models.CharField(max_length=64, blank=False, verbose_name="last name") birth_date = models.DateField(default=date(2000, 1, 1), verbose_name="birth date") creation_date = models.DateTimeField(auto_now_add=True, verbose_name="creation date") picture = models.ImageField(max_length=256, null=True, blank=True, upload_to="pictures", verbose_name="picture") objects = PersonQuerySet.as_manager() class Meta: verbose_name = "person" verbose_name_plural = "people" def __str__(self): return f"{self.get_full_name()} ({self.get_role_display()})" def get_full_name(self): return f"{self.first_name} {self.last_name}" def borrow(self, book: "library.models.Book", duration: int = 7) -> Optional["library.models.Loan"]: """ Borrow a book. Args: book: book instance to borrow duration: expected duration of loan in days Returns: If the book can be borrowed, return the new `Loan` object. If not, return `None`. """ try: deadline = timezone.now() + timedelta(days=duration) loan = self.loans.create(person=self, book=book, expected_return=deadline) return loan except IntegrityError: return None