Add documentation and source
Added documentation, source and extra files.
This commit is contained in:
5
source/orm/library/models/__init__.py
Normal file
5
source/orm/library/models/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from .author import Author
|
||||
from .book import Book
|
||||
from .genre import Genre
|
||||
from .loan import Loan
|
||||
from .person import Person
|
28
source/orm/library/models/author.py
Normal file
28
source/orm/library/models/author.py
Normal file
@ -0,0 +1,28 @@
|
||||
from datetime import date
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Author(models.Model):
|
||||
"""
|
||||
Model for book authors.
|
||||
|
||||
"""
|
||||
|
||||
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")
|
||||
description = models.TextField(blank=True, verbose_name="description")
|
||||
birth_date = models.DateField(default=date(2000, 1, 1), verbose_name="birth date")
|
||||
registration_date = models.DateTimeField(auto_now_add=True, verbose_name="registration date")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "book author"
|
||||
verbose_name_plural = "book authors"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_full_name()} ({self.uuid})"
|
||||
|
||||
def get_full_name(self):
|
||||
return f"{self.first_name} {self.last_name}"
|
70
source/orm/library/models/book.py
Normal file
70
source/orm/library/models/book.py
Normal file
@ -0,0 +1,70 @@
|
||||
import random
|
||||
import string
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
def generate_isbn() -> str:
|
||||
"""
|
||||
Generate a random ISBN number.
|
||||
|
||||
Returns:
|
||||
A 13-digit string.
|
||||
|
||||
"""
|
||||
digits = string.digits
|
||||
return "".join(random.choice(digits) for _ in range(13))
|
||||
|
||||
|
||||
class BookQuerySet(models.QuerySet):
|
||||
"""
|
||||
QuerySet class for books.
|
||||
|
||||
"""
|
||||
|
||||
def available(self) -> models.QuerySet:
|
||||
"""
|
||||
Get books available for a loan.
|
||||
|
||||
Excludes books with at least a loan whose `borrowed` field is `True`.
|
||||
Given the constraints, only one loan for a book can have the `borrowed` status
|
||||
to `True`.
|
||||
|
||||
"""
|
||||
return self.exclude(loans__borrowed=True)
|
||||
|
||||
def late_returns(self) -> models.QuerySet:
|
||||
"""
|
||||
Get books that should have been returned by now.
|
||||
|
||||
"""
|
||||
now = timezone.now()
|
||||
return self.filter(loans__expected_return__lt=now, borrowed=True)
|
||||
|
||||
|
||||
class Book(models.Model):
|
||||
"""
|
||||
Description of a book.
|
||||
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, db_index=True, verbose_name="UUID")
|
||||
isbn = models.CharField(max_length=64, default=generate_isbn, blank=False, verbose_name="ISBN")
|
||||
name = models.CharField(max_length=128, blank=False, verbose_name="name")
|
||||
description = models.TextField(blank=True, verbose_name="description")
|
||||
registration_date = models.DateTimeField(auto_now_add=True, verbose_name="creation date")
|
||||
year = models.PositiveIntegerField(default=1950, null=True, db_index=True, verbose_name="published")
|
||||
authors = models.ManyToManyField("library.Author", related_name="books", verbose_name="authors")
|
||||
genre = models.ForeignKey(
|
||||
"library.Genre", on_delete=models.SET_NULL, null=True, related_name="books", verbose_name="genre"
|
||||
)
|
||||
objects = BookQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "book"
|
||||
verbose_name_plural = "books"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} ({self.isbn})"
|
23
source/orm/library/models/genre.py
Normal file
23
source/orm/library/models/genre.py
Normal file
@ -0,0 +1,23 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Genre(models.Model):
|
||||
"""
|
||||
Book genre.
|
||||
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, db_index=True, verbose_name="UUID")
|
||||
code_name = models.CharField(max_length=64, blank=False, unique=True, verbose_name="code name")
|
||||
name = models.CharField(max_length=64, blank=False, verbose_name="name")
|
||||
description = models.TextField(blank=True, verbose_name="description")
|
||||
creation_date = models.DateTimeField(auto_now_add=True, verbose_name="creation date")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "genre"
|
||||
verbose_name_plural = "genres"
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name}"
|
29
source/orm/library/models/loan.py
Normal file
29
source/orm/library/models/loan.py
Normal file
@ -0,0 +1,29 @@
|
||||
from uuid import uuid4
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Loan(models.Model):
|
||||
"""
|
||||
Model for book loans.
|
||||
|
||||
"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, db_index=True, verbose_name="UUID")
|
||||
person = models.ForeignKey("library.Person", on_delete=models.CASCADE, related_name="loans", verbose_name="person")
|
||||
book = models.ForeignKey("library.Book", on_delete=models.CASCADE, related_name="loans", verbose_name="book")
|
||||
date = models.DateTimeField(auto_now_add=True, verbose_name="date")
|
||||
expected_return = models.DateTimeField(null=True, verbose_name="expected return date")
|
||||
borrowed = models.BooleanField(null=True, default=True, verbose_name="borrowed")
|
||||
|
||||
class Meta:
|
||||
verbose_name = "book loan"
|
||||
verbose_name_plural = "book loans"
|
||||
unique_together = [("book", "borrowed")]
|
||||
|
||||
def __str__(self):
|
||||
return f"Book loan: {self.person} → {self.book}"
|
||||
|
||||
def return_book(self):
|
||||
self.borrowed = None
|
||||
self.save()
|
74
source/orm/library/models/person.py
Normal file
74
source/orm/library/models/person.py
Normal file
@ -0,0 +1,74 @@
|
||||
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
|
Reference in New Issue
Block a user