From 0880bd0817d5a95a30047ecba39cdea9960060bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniele=20Vigan=C3=B2?= Date: Mon, 29 Dec 2025 12:05:37 +0100 Subject: [PATCH] Initial implemntation of TOC for books et al. --- ram/bookshelf/admin.py | 15 +++++++ ram/bookshelf/migrations/0030_tocentry.py | 53 +++++++++++++++++++++++ ram/bookshelf/models.py | 27 ++++++++++++ ram/ram/__init__.py | 2 +- 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 ram/bookshelf/migrations/0030_tocentry.py diff --git a/ram/bookshelf/admin.py b/ram/bookshelf/admin.py index 2cb980d..8f15b00 100644 --- a/ram/bookshelf/admin.py +++ b/ram/bookshelf/admin.py @@ -22,6 +22,7 @@ from bookshelf.models import ( Catalog, Magazine, MagazineIssue, + TocEntry, ) @@ -363,9 +364,23 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin): actions = [publish, unpublish, download_csv] +class MagazineIssueToc(admin.TabularInline): + model = TocEntry + min_num = 0 + extra = 0 + fields = ( + "title", + "subtitle", + "authors", + "page", + "featured", + ) + + @admin.register(MagazineIssue) class MagazineIssueAdmin(SortableAdminBase, admin.ModelAdmin): inlines = ( + MagazineIssueToc, BookPropertyInline, BookImageInline, MagazineIssueDocInline, diff --git a/ram/bookshelf/migrations/0030_tocentry.py b/ram/bookshelf/migrations/0030_tocentry.py new file mode 100644 index 0000000..ad09c32 --- /dev/null +++ b/ram/bookshelf/migrations/0030_tocentry.py @@ -0,0 +1,53 @@ +# Generated by Django 6.0 on 2025-12-29 11:02 + +import django.db.models.deletion +import tinymce.models +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("bookshelf", "0029_alter_catalog_manufacturer_alter_catalog_scales"), + ] + + operations = [ + migrations.CreateModel( + name="TocEntry", + fields=[ + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ("description", tinymce.models.HTMLField(blank=True)), + ("notes", tinymce.models.HTMLField(blank=True)), + ("creation_time", models.DateTimeField(auto_now_add=True)), + ("updated_time", models.DateTimeField(auto_now=True)), + ("published", models.BooleanField(default=True)), + ("title", models.CharField(max_length=200)), + ("subtitle", models.CharField(blank=True, max_length=200)), + ("authors", models.CharField(blank=True, max_length=256)), + ("page", models.SmallIntegerField()), + ("featured", models.BooleanField(default=False)), + ( + "book", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="toc", + to="bookshelf.basebook", + ), + ), + ], + options={ + "verbose_name": "Table of Contents Entry", + "verbose_name_plural": "Table of Contents Entries", + "ordering": ["page"], + }, + ), + ] diff --git a/ram/bookshelf/models.py b/ram/bookshelf/models.py index 46062c7..3a83b7b 100644 --- a/ram/bookshelf/models.py +++ b/ram/bookshelf/models.py @@ -239,3 +239,30 @@ class MagazineIssue(BaseBook): return reverse( "issue", kwargs={"uuid": self.uuid, "magazine": self.magazine.uuid} ) + + +class TocEntry(BaseModel): + book = models.ForeignKey( + BaseBook, on_delete=models.CASCADE, related_name="toc" + ) + title = models.CharField(max_length=200) + subtitle = models.CharField(max_length=200, blank=True) + authors = models.CharField(max_length=256, blank=True) + page = models.SmallIntegerField() + featured = models.BooleanField( + default=False, + ) + + class Meta: + ordering = ["page"] + verbose_name = "Table of Contents Entry" + verbose_name_plural = "Table of Contents Entries" + + def __str__(self): + return f"{self.title} (p. {self.page})" + + def clean(self): + if self.page > self.book.number_of_pages: + raise ValidationError( + "Page number exceeds the publication's number of pages." + ) diff --git a/ram/ram/__init__.py b/ram/ram/__init__.py index 14781ed..2ef6c1d 100644 --- a/ram/ram/__init__.py +++ b/ram/ram/__init__.py @@ -1,4 +1,4 @@ from ram.utils import git_suffix -__version__ = "0.18.10" +__version__ = "0.19.1" __version__ += git_suffix(__file__)