from django.db import models from django.utils.translation import gettext_lazy as _ from django.conf import settings from filer.fields.image import FilerImageField from dj_language.field import LanguageField from utils import generate_slug_for_model from utils.slug import generate_smart_slug from apps.account.models import User class BookCollection(models.Model): class DisplayPosition(models.TextChoices): PINNED = 'pinned', _('Pinned') MIDDLE = 'middle', _('Middle Section') # BOTTOM = 'bottom', _('Bottom Section') title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('could be null')) pin_top = models.BooleanField(_('pin top'), default=True) display_position = models.CharField( max_length=20, choices=DisplayPosition.choices, default=DisplayPosition.PINNED, verbose_name=_('Display Position') ) status = models.BooleanField(_('status'), default=True) order = models.IntegerField(default=0, verbose_name=_('order')) books = models.ManyToManyField('library.Book', related_name='related_collections_books',through="library.Book_collections" ,verbose_name=_('Books'), blank=True) def __str__(self): return f'Collection #{self.id}/{self.title}' class Meta: verbose_name = _('Book Collection') verbose_name_plural = _('Book Collections') def save(self, *args, **kwargs): if not self.slug or not self.slug.strip(): self.slug = generate_smart_slug(self.title, BookCollection, instance=self) super().save(*args, **kwargs) class PinnedBookCollection(BookCollection): """ Proxy model for pinned book collections """ class Meta: proxy = True verbose_name = _('Pinned Book Collection') verbose_name_plural = _('Pinned Book Collections') class MiddleBookCollection(BookCollection): """ Proxy model for middle section book collections """ class Meta: proxy = True verbose_name = _('Middle Section Book Collection') verbose_name_plural = _('Middle Section Book Collections') class Category(models.Model): title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) status = models.BooleanField(default=True, verbose_name=_('status')) # books = models.ManyToManyField('library.Book', related_name='related_categories_books',through="library.Book_categories" ,verbose_name=_('Books'), blank=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return self.title def save(self, *args, **kwargs): if not self.slug or not self.slug.strip(): self.slug = generate_smart_slug(self.title, Category, instance=self) super().save(*args, **kwargs) @property def books_count(self): """Return the number of books in this category""" return self.related_categories.count() class Meta: verbose_name = _('Category') verbose_name_plural = _('Categories') class Book(models.Model): class FileType(models.TextChoices): pdf = 'pdf', 'Pdf' epub = 'epub', 'Epub' docx = 'docx', 'Docx' title = models.CharField(max_length=255) slug = models.SlugField(max_length=255, unique=True) slogan = models.CharField(max_length=300, blank=True, null=True) summary_title = models.CharField(max_length=512, null=True, blank=True, help_text=_('Summary Title')) summary = models.CharField(max_length=512, null=True, blank=True, help_text=_('Summary')) description = models.TextField(null=True, blank=True, help_text=_('could be null')) thumbnail = models.ImageField(upload_to='book_thumbnails/', null=True, blank=True, help_text=_('image allowed')) publisher = models.CharField(max_length=655, null=True, blank=True) year_of_publication = models.CharField(max_length=255, null=True, blank=True) author = models.CharField(max_length=255, null=True, blank=True) isbn = models.CharField(max_length=255, null=True, blank=True) numnber_of_volume = models.CharField(max_length=255, null=True, blank=True) # Language, themes and notable works language = LanguageField(verbose_name=_('Language'), null=True, blank=True) main_themes = models.JSONField(verbose_name=_('Main Themes'), default=list, blank=True, help_text=_('List of main themes')) notable_works = models.JSONField(verbose_name=_('Notable Works'), default=list, blank=True, help_text=_('List of notable works')) pages_count = models.CharField(verbose_name=_('Number of Pages'), max_length=255, help_text=_('eg. 34'), null=True) status = models.BooleanField(default=True, verbose_name=_('status')) pin = models.BooleanField(default=True, verbose_name=_('Pin to top')) categories = models.ManyToManyField(Category, related_name='related_categories', verbose_name=_('categories'), blank=True) collections = models.ManyToManyField(BookCollection, related_name='related_collections', verbose_name=_('collections'), blank=True) view_count = models.PositiveBigIntegerField(default=0, verbose_name=_('view count')) download_count = models.PositiveBigIntegerField(default=0, verbose_name=_('view count')) # seo_fields = SeoGenericRelation(verbose_name=_('soe fields')) file_type = models.CharField(verbose_name=_('File Type'), choices=FileType.choices, default=FileType.pdf, max_length=16) book_file = models.FileField(null=True, blank=True, max_length=550, upload_to='books', verbose_name='Book File') created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def __str__(self): return f'<{self.id}>-{self.title}' @property def share_link(self): if self.slug: return f"{settings.DOVODI_DOMAIN}/library/{self.slug}" return None def save(self, *args, **kwargs): if not self.slug or not self.slug.strip(): self.slug = generate_smart_slug(self.title, Book, instance=self) super().save(*args, **kwargs) def increment_view_count(self): """Increment the view count by 1 and save the model""" self.view_count += 1 self.save(update_fields=['view_count']) class Meta: verbose_name = _('Book') verbose_name_plural = _('Books') class BookDownload(models.Model): """ Model to track book downloads by users """ user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='book_downloads', verbose_name=_('user')) book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name='downloads', verbose_name=_('book')) status = models.BooleanField(default=True, verbose_name=_('status')) 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: verbose_name = _('Book Download') verbose_name_plural = _('Book Downloads') ordering = ('-created_at',) def __str__(self): return f"{self.user} - {self.book}"