from __future__ import annotations from django.db import models from django.utils.translation import gettext_lazy as _ class ReadingStatus(models.TextChoices): """Enumeration of possible reading statuses for a book.""" NOT_STARTED = "not_started", _("Not Started") READING = "reading", _("Reading") FINISHED = "finished", _("Finished") DNF = "dnf", _("Did Not Finish") class Book(models.Model): """Represents a book in the user's library.""" title = models.CharField(max_length=500, verbose_name=_("Title")) author = models.CharField(max_length=500, verbose_name=_("Author")) genre = models.CharField(max_length=200, blank=True, default="", verbose_name=_("Genre")) description = models.TextField(blank=True, default="", verbose_name=_("Description")) cover_image_url = models.URLField(blank=True, default="", verbose_name=_("Cover Image URL")) isbn = models.CharField(max_length=20, blank=True, default="", verbose_name=_("ISBN")) total_pages = models.PositiveIntegerField(default=0, verbose_name=_("Total Pages")) current_page = models.PositiveIntegerField(default=0, verbose_name=_("Current Page")) reading_status = models.CharField( max_length=20, choices=ReadingStatus.choices, default=ReadingStatus.NOT_STARTED, verbose_name=_("Reading Status"), ) owner = models.ForeignKey( "auth.User", on_delete=models.CASCADE, related_name="books", verbose_name=_("Owner"), ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) class Meta: ordering = ["-updated_at"] verbose_name = _("Book") verbose_name_plural = _("Books") indexes = [ models.Index(fields=["owner", "reading_status"]), models.Index(fields=["owner", "title"]), models.Index(fields=["owner", "author"]), ] def __str__(self) -> str: return f"{self.title} by {self.author}" @property def reading_progress(self) -> float: """Calculate reading progress as a percentage (0.0 - 100.0).""" if self.total_pages == 0: return 0.0 return round((self.current_page / self.total_pages) * 100, 1) def mark_as_finished(self) -> None: """Mark the book as finished, setting progress to 100%.""" self.reading_status = ReadingStatus.FINISHED self.current_page = self.total_pages self.save(update_fields=["reading_status", "current_page", "updated_at"])