1 Commits

Author SHA1 Message Date
1d683b8519 Initial work to support magazines 2025-11-23 17:32:24 +01:00
5 changed files with 357 additions and 4 deletions

View File

@@ -8,7 +8,11 @@ from adminsortable2.admin import SortableAdminBase, SortableInlineAdminMixin
from ram.admin import publish, unpublish
from ram.utils import generate_csv
from portal.utils import get_site_conf
from repository.models import BookDocument, CatalogDocument
from repository.models import (
BookDocument,
CatalogDocument,
MagazineIssueDocument
)
from bookshelf.models import (
BaseBookProperty,
BaseBookImage,
@@ -16,6 +20,8 @@ from bookshelf.models import (
Author,
Publisher,
Catalog,
Magazine,
MagazineIssue,
)
@@ -48,6 +54,10 @@ class CatalogDocInline(BookDocInline):
model = CatalogDocument
class MagazineIssueDocInline(BookDocInline):
model = MagazineIssueDocument
@admin.register(Book)
class BookAdmin(SortableAdminBase, admin.ModelAdmin):
inlines = (
@@ -344,3 +354,47 @@ class CatalogAdmin(SortableAdminBase, admin.ModelAdmin):
download_csv.short_description = "Download selected items as CSV"
actions = [publish, unpublish, download_csv]
@admin.register(Issue)
class MagazineIssueAdmin(admin.ModelAdmin):
inlines = (
BookPropertyInline,
BookImageInline,
MagazineIssueDocInline,
)
list_display = (
"__str__",
"issue_number",
"published",
)
# autocomplete_fields = ("publisher",)
# readonly_fields = ("creation_time", "updated_time")
# search_fields = ("title", "publisher__name")
# list_filter = ("publisher__name", "language")
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
return {}
actions = [publish, unpublish]
@admin.register(Magazine)
class MagazineAdmin(admin.ModelAdmin):
inlines = (
MagazineIssueInline,
)
list_display = (
"__str__",
"publisher",
"published",
)
autocomplete_fields = ("publisher",)
readonly_fields = ("creation_time", "updated_time")
search_fields = ("name", "publisher__name")
list_filter = ("publisher__name", "language")
actions = [publish, unpublish]

View File

@@ -0,0 +1,226 @@
# Generated by Django 5.2.8 on 2025-11-13 23:01
import bookshelf.models
import django.db.models.deletion
import ram.utils
import tinymce.models
import uuid
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("bookshelf", "0023_delete_basebookdocument"),
("metadata", "0025_alter_company_options_alter_manufacturer_options_and_more"),
]
operations = [
migrations.CreateModel(
name="Issue",
fields=[
(
"basebook_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="bookshelf.basebook",
),
),
("issue_number", models.CharField(max_length=100)),
],
options={
"abstract": False,
},
bases=("bookshelf.basebook",),
),
migrations.CreateModel(
name="Magazine",
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)),
("name", models.CharField(max_length=200)),
(
"image",
models.ImageField(
blank=True,
storage=ram.utils.DeduplicatedStorage,
upload_to=bookshelf.models.magazine_image_upload,
),
),
(
"language",
models.CharField(
choices=[
("af", "Afrikaans"),
("ar", "Arabic"),
("ar-dz", "Algerian Arabic"),
("ast", "Asturian"),
("az", "Azerbaijani"),
("bg", "Bulgarian"),
("be", "Belarusian"),
("bn", "Bengali"),
("br", "Breton"),
("bs", "Bosnian"),
("ca", "Catalan"),
("ckb", "Central Kurdish (Sorani)"),
("cs", "Czech"),
("cy", "Welsh"),
("da", "Danish"),
("de", "German"),
("dsb", "Lower Sorbian"),
("el", "Greek"),
("en", "English"),
("en-au", "Australian English"),
("en-gb", "British English"),
("eo", "Esperanto"),
("es", "Spanish"),
("es-ar", "Argentinian Spanish"),
("es-co", "Colombian Spanish"),
("es-mx", "Mexican Spanish"),
("es-ni", "Nicaraguan Spanish"),
("es-ve", "Venezuelan Spanish"),
("et", "Estonian"),
("eu", "Basque"),
("fa", "Persian"),
("fi", "Finnish"),
("fr", "French"),
("fy", "Frisian"),
("ga", "Irish"),
("gd", "Scottish Gaelic"),
("gl", "Galician"),
("he", "Hebrew"),
("hi", "Hindi"),
("hr", "Croatian"),
("hsb", "Upper Sorbian"),
("hu", "Hungarian"),
("hy", "Armenian"),
("ia", "Interlingua"),
("id", "Indonesian"),
("ig", "Igbo"),
("io", "Ido"),
("is", "Icelandic"),
("it", "Italian"),
("ja", "Japanese"),
("ka", "Georgian"),
("kab", "Kabyle"),
("kk", "Kazakh"),
("km", "Khmer"),
("kn", "Kannada"),
("ko", "Korean"),
("ky", "Kyrgyz"),
("lb", "Luxembourgish"),
("lt", "Lithuanian"),
("lv", "Latvian"),
("mk", "Macedonian"),
("ml", "Malayalam"),
("mn", "Mongolian"),
("mr", "Marathi"),
("ms", "Malay"),
("my", "Burmese"),
("nb", "Norwegian Bokmål"),
("ne", "Nepali"),
("nl", "Dutch"),
("nn", "Norwegian Nynorsk"),
("os", "Ossetic"),
("pa", "Punjabi"),
("pl", "Polish"),
("pt", "Portuguese"),
("pt-br", "Brazilian Portuguese"),
("ro", "Romanian"),
("ru", "Russian"),
("sk", "Slovak"),
("sl", "Slovenian"),
("sq", "Albanian"),
("sr", "Serbian"),
("sr-latn", "Serbian Latin"),
("sv", "Swedish"),
("sw", "Swahili"),
("ta", "Tamil"),
("te", "Telugu"),
("tg", "Tajik"),
("th", "Thai"),
("tk", "Turkmen"),
("tr", "Turkish"),
("tt", "Tatar"),
("udm", "Udmurt"),
("ug", "Uyghur"),
("uk", "Ukrainian"),
("ur", "Urdu"),
("uz", "Uzbek"),
("vi", "Vietnamese"),
("zh-hans", "Simplified Chinese"),
("zh-hant", "Traditional Chinese"),
],
default="en",
max_length=7,
),
),
(
"publisher",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="bookshelf.publisher",
),
),
(
"tags",
models.ManyToManyField(
blank=True, related_name="magazine", to="metadata.tag"
),
),
],
options={
"ordering": ["name"],
},
),
migrations.CreateModel(
name="MagazineIssue",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"issue",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="magazine_issue",
to="bookshelf.issue",
),
),
(
"magazine",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="magazine_issue",
to="bookshelf.magazine",
),
),
],
options={
"ordering": ["magazine", "issue"],
"unique_together": {("magazine", "issue")},
},
),
]

View File

@@ -3,6 +3,7 @@ import shutil
from django.db import models
from django.conf import settings
from django.urls import reverse
from django.utils.dates import MONTHS
from django_countries.fields import CountryField
from ram.utils import DeduplicatedStorage
@@ -153,3 +154,62 @@ class Catalog(BaseBook):
def get_scales(self):
return "/".join([s.scale for s in self.scales.all()])
get_scales.short_description = "Scales"
class Magazine(BaseModel):
name = models.CharField(max_length=200)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
ISBN = models.CharField(max_length=17, blank=True) # 13 + dashes
image = models.ImageField(
blank=True,
upload_to=book_image_upload,
storage=DeduplicatedStorage,
)
language = models.CharField(
max_length=7,
choices=settings.LANGUAGES,
default='en'
)
tags = models.ManyToManyField(
Tag, related_name="magazine", blank=True
)
def delete(self, *args, **kwargs):
shutil.rmtree(
os.path.join(
settings.MEDIA_ROOT, "images", "magazines", str(self.uuid)
),
ignore_errors=True
)
super(Magazine, self).delete(*args, **kwargs)
class Meta:
ordering = ["name"]
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse(
"bookshelf_item",
kwargs={"selector": "magazine", "uuid": self.uuid}
)
class MagazineIssue(BaseBook):
magazine = models.ForeignKey(
Magazine, on_delete=models.CASCADE, related_name="issue"
)
issue_number = models.CharField(max_length=100)
publication_month = models.SmallIntegerField(
null=True,
blank=True,
choices=MONTHS.items()
)
class Meta:
unique_together = ("magazine", "issue_number")
ordering = ["magazine", "issue_number"]
def __str__(self):
return f"{self.magazine.name} - {self.issue.issue_number}"

View File

@@ -1,12 +1,11 @@
from django.db import models
from django.core.exceptions import ValidationError
from tinymce import models as tinymce
from ram.models import PrivateDocument
from metadata.models import Decoder, Shop, Tag
from roster.models import RollingStock
from bookshelf.models import Book, Catalog
from bookshelf.models import Book, Catalog, Issue
class GenericDocument(PrivateDocument):
@@ -77,6 +76,20 @@ class CatalogDocument(PrivateDocument):
]
class MagazineIssueDocument(PrivateDocument):
issue = models.ForeignKey(
Issue, on_delete=models.CASCADE, related_name="document"
)
class Meta:
verbose_name_plural = "Magazines documents"
constraints = [
models.UniqueConstraint(
fields=["issue", "file"], name="unique_issue_file"
)
]
class RollingStockDocument(PrivateDocument):
rolling_stock = models.ForeignKey(
RollingStock, on_delete=models.CASCADE, related_name="document"