mirror of
https://github.com/daniviga/django-ram.git
synced 2026-02-04 18:10:38 +01:00
Compare commits
7 Commits
magazine
...
fb17dc2a7c
| Author | SHA1 | Date | |
|---|---|---|---|
|
fb17dc2a7c
|
|||
|
5a71dc36fa
|
|||
|
c539255bf9
|
|||
|
fc527d5cd1
|
|||
|
f45d754c91
|
|||
|
e9c9ede357
|
|||
| 39b0a9378b |
@@ -475,6 +475,7 @@ class MagazineAdmin(SortableAdminBase, admin.ModelAdmin):
|
|||||||
"fields": (
|
"fields": (
|
||||||
"published",
|
"published",
|
||||||
"name",
|
"name",
|
||||||
|
"website",
|
||||||
"publisher",
|
"publisher",
|
||||||
"ISBN",
|
"ISBN",
|
||||||
"language",
|
"language",
|
||||||
|
|||||||
18
ram/bookshelf/migrations/0027_magazine_website.py
Normal file
18
ram/bookshelf/migrations/0027_magazine_website.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 6.0 on 2025-12-12 14:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookshelf", "0026_alter_basebook_language_alter_magazine_image_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="magazine",
|
||||||
|
name="website",
|
||||||
|
field=models.URLField(blank=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 6.0 on 2025-12-21 21:56
|
||||||
|
|
||||||
|
import django.db.models.functions.text
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("bookshelf", "0027_magazine_website"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="magazine",
|
||||||
|
options={"ordering": [django.db.models.functions.text.Lower("name")]},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="magazineissue",
|
||||||
|
options={
|
||||||
|
"ordering": [
|
||||||
|
"magazine",
|
||||||
|
"publication_year",
|
||||||
|
"publication_month",
|
||||||
|
"issue_number",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
from urllib.parse import urlparse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.dates import MONTHS
|
from django.utils.dates import MONTHS
|
||||||
|
from django.db.models.functions import Lower
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django_countries.fields import CountryField
|
from django_countries.fields import CountryField
|
||||||
|
|
||||||
@@ -58,36 +60,24 @@ class BaseBook(BaseModel):
|
|||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
purchase_date = models.DateField(null=True, blank=True)
|
purchase_date = models.DateField(null=True, blank=True)
|
||||||
tags = models.ManyToManyField(
|
tags = models.ManyToManyField(Tag, related_name="bookshelf", blank=True)
|
||||||
Tag, related_name="bookshelf", blank=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
shutil.rmtree(
|
shutil.rmtree(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
settings.MEDIA_ROOT, "images", "books", str(self.uuid)
|
settings.MEDIA_ROOT, "images", "books", str(self.uuid)
|
||||||
),
|
),
|
||||||
ignore_errors=True
|
ignore_errors=True,
|
||||||
)
|
)
|
||||||
super(BaseBook, self).delete(*args, **kwargs)
|
super(BaseBook, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def book_image_upload(instance, filename):
|
def book_image_upload(instance, filename):
|
||||||
return os.path.join(
|
return os.path.join("images", "books", str(instance.book.uuid), filename)
|
||||||
"images",
|
|
||||||
"books",
|
|
||||||
str(instance.book.uuid),
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def magazine_image_upload(instance, filename):
|
def magazine_image_upload(instance, filename):
|
||||||
return os.path.join(
|
return os.path.join("images", "magazines", str(instance.uuid), filename)
|
||||||
"images",
|
|
||||||
"magazines",
|
|
||||||
str(instance.uuid),
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseBookImage(Image):
|
class BaseBookImage(Image):
|
||||||
@@ -131,8 +121,7 @@ class Book(BaseBook):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
"bookshelf_item",
|
"bookshelf_item", kwargs={"selector": "book", "uuid": self.uuid}
|
||||||
kwargs={"selector": "book", "uuid": self.uuid}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -157,18 +146,19 @@ class Catalog(BaseBook):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
"bookshelf_item",
|
"bookshelf_item", kwargs={"selector": "catalog", "uuid": self.uuid}
|
||||||
kwargs={"selector": "catalog", "uuid": self.uuid}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_scales(self):
|
def get_scales(self):
|
||||||
return "/".join([s.scale for s in self.scales.all()])
|
return "/".join([s.scale for s in self.scales.all()])
|
||||||
|
|
||||||
get_scales.short_description = "Scales"
|
get_scales.short_description = "Scales"
|
||||||
|
|
||||||
|
|
||||||
class Magazine(BaseModel):
|
class Magazine(BaseModel):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
|
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
|
||||||
|
website = models.URLField(blank=True)
|
||||||
ISBN = models.CharField(max_length=17, blank=True) # 13 + dashes
|
ISBN = models.CharField(max_length=17, blank=True) # 13 + dashes
|
||||||
image = models.ImageField(
|
image = models.ImageField(
|
||||||
blank=True,
|
blank=True,
|
||||||
@@ -178,32 +168,31 @@ class Magazine(BaseModel):
|
|||||||
language = models.CharField(
|
language = models.CharField(
|
||||||
max_length=7,
|
max_length=7,
|
||||||
choices=sorted(settings.LANGUAGES, key=lambda s: s[1]),
|
choices=sorted(settings.LANGUAGES, key=lambda s: s[1]),
|
||||||
default='en'
|
default="en",
|
||||||
)
|
|
||||||
tags = models.ManyToManyField(
|
|
||||||
Tag, related_name="magazine", blank=True
|
|
||||||
)
|
)
|
||||||
|
tags = models.ManyToManyField(Tag, related_name="magazine", blank=True)
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
shutil.rmtree(
|
shutil.rmtree(
|
||||||
os.path.join(
|
os.path.join(
|
||||||
settings.MEDIA_ROOT, "images", "magazines", str(self.uuid)
|
settings.MEDIA_ROOT, "images", "magazines", str(self.uuid)
|
||||||
),
|
),
|
||||||
ignore_errors=True
|
ignore_errors=True,
|
||||||
)
|
)
|
||||||
super(Magazine, self).delete(*args, **kwargs)
|
super(Magazine, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ["name"]
|
ordering = [Lower("name")]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse("magazine", kwargs={"uuid": self.uuid})
|
||||||
"magazine",
|
|
||||||
kwargs={"uuid": self.uuid}
|
def website_short(self):
|
||||||
)
|
if self.website:
|
||||||
|
return urlparse(self.website).netloc.replace("www.", "")
|
||||||
|
|
||||||
|
|
||||||
class MagazineIssue(BaseBook):
|
class MagazineIssue(BaseBook):
|
||||||
@@ -212,14 +201,17 @@ class MagazineIssue(BaseBook):
|
|||||||
)
|
)
|
||||||
issue_number = models.CharField(max_length=100)
|
issue_number = models.CharField(max_length=100)
|
||||||
publication_month = models.SmallIntegerField(
|
publication_month = models.SmallIntegerField(
|
||||||
null=True,
|
null=True, blank=True, choices=MONTHS.items()
|
||||||
blank=True,
|
|
||||||
choices=MONTHS.items()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ("magazine", "issue_number")
|
unique_together = ("magazine", "issue_number")
|
||||||
ordering = ["magazine", "issue_number"]
|
ordering = [
|
||||||
|
"magazine",
|
||||||
|
"publication_year",
|
||||||
|
"publication_month",
|
||||||
|
"issue_number",
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.magazine.name} - {self.issue_number}"
|
return f"{self.magazine.name} - {self.issue_number}"
|
||||||
@@ -240,9 +232,5 @@ class MagazineIssue(BaseBook):
|
|||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return reverse(
|
||||||
"issue",
|
"issue", kwargs={"uuid": self.uuid, "magazine": self.magazine.uuid}
|
||||||
kwargs={
|
|
||||||
"uuid": self.uuid,
|
|
||||||
"magazine": self.magazine.uuid
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,3 +49,5 @@ class CatalogSerializer(serializers.ModelSerializer):
|
|||||||
"price",
|
"price",
|
||||||
)
|
)
|
||||||
read_only_fields = ("creation_time", "updated_time")
|
read_only_fields = ("creation_time", "updated_time")
|
||||||
|
|
||||||
|
# FIXME: add Magazine and MagazineIssue serializers
|
||||||
|
|||||||
@@ -38,3 +38,5 @@ class CatalogGet(RetrieveAPIView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return Book.objects.get_published(self.request.user)
|
return Book.objects.get_published(self.request.user)
|
||||||
|
|
||||||
|
# FIXME: add Magazine and MagazineIssue views
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
from urllib.parse import urlparse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -57,6 +58,10 @@ class Manufacturer(models.Model):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def website_short(self):
|
||||||
|
if self.website:
|
||||||
|
return urlparse(self.website).netloc.replace("www.", "")
|
||||||
|
|
||||||
def logo_thumbnail(self):
|
def logo_thumbnail(self):
|
||||||
return get_image_preview(self.logo.url)
|
return get_image_preview(self.logo.url)
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,7 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">
|
<th colspan="2" scope="row">
|
||||||
{% if type == "catalog" %}Catalog
|
{{ label|capfirst }}
|
||||||
{% elif type == "book" %}Book{% endif %}
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -70,7 +69,9 @@
|
|||||||
{% if type == "catalog" %}
|
{% if type == "catalog" %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Manufacturer</th>
|
<th class="w-33" scope="row">Manufacturer</th>
|
||||||
<td>{{ book.manufacturer }}</td>
|
<td>
|
||||||
|
<a href="{% url 'filtered' _filter="manufacturer" search=book.manufacturer.slug %}">{{ book.manufacturer }}{% if book.manufacturer.website %}</a> <a href="{{ book.manufacturer.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Scales</th>
|
<th class="w-33" scope="row">Scales</th>
|
||||||
@@ -97,7 +98,10 @@
|
|||||||
{% elif type == "magazineissue" %}
|
{% elif type == "magazineissue" %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Magazine</th>
|
<th class="w-33" scope="row">Magazine</th>
|
||||||
<td><a href="{% url 'magazine' book.magazine.pk %}">{{ book.magazine }}</a></td>
|
<td>
|
||||||
|
<a href="{% url 'magazine' book.magazine.pk %}">{{ book.magazine }}</a>
|
||||||
|
{% if book.magazine.website %} <a href="{{ book.magazine.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Publisher</th>
|
<th class="w-33" scope="row">Publisher</th>
|
||||||
@@ -112,7 +116,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Date</th>
|
<th class="w-33" scope="row">Date</th>
|
||||||
<td>{{ book.publication_year|default:"-" }} / {{ book.publication_month|default:"-" }}</td>
|
<td>{{ book.publication_year|default:"-" }} / {{ book.get_publication_month_display|default:"-" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -13,18 +13,18 @@
|
|||||||
<strong>{{ d.item }}</strong>
|
<strong>{{ d.item }}</strong>
|
||||||
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
||||||
</p>
|
</p>
|
||||||
{% if d.item.tags.all %}
|
|
||||||
<p class="card-text"><small>Tags:</small>
|
<p class="card-text"><small>Tags:</small>
|
||||||
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
||||||
{{ t.name }}</a>{# new line is required #}
|
{{ t.name }}</a>{# new line is required #}
|
||||||
|
{% empty %}
|
||||||
|
<span class="badge rounded-pill bg-secondary"><i class="bi bi-ban"></i></span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">
|
<th colspan="2" scope="row">
|
||||||
{{ d.type | capfirst }}
|
{{ d.label|capfirst }}
|
||||||
<div class="float-end">
|
<div class="float-end">
|
||||||
{% if not d.item.published %}
|
{% if not d.item.published %}
|
||||||
<span class="badge text-bg-warning">Unpublished</span>
|
<span class="badge text-bg-warning">Unpublished</span>
|
||||||
@@ -37,7 +37,9 @@
|
|||||||
{% if d.type == "catalog" %}
|
{% if d.type == "catalog" %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Manufacturer</th>
|
<th class="w-33" scope="row">Manufacturer</th>
|
||||||
<td>{{ d.item.manufacturer }}</td>
|
<td>
|
||||||
|
<a href="{% url 'filtered' _filter="manufacturer" search=d.item.manufacturer.slug %}">{{ d.item.manufacturer }}{% if d.item.manufacturer.website %}</a> <a href="{{ d.item.manufacturer.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Scales</th>
|
<th class="w-33" scope="row">Scales</th>
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
<strong>{{ d.item }}</strong>
|
<strong>{{ d.item }}</strong>
|
||||||
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
||||||
</p>
|
</p>
|
||||||
{% if d.item.tags.all %}
|
|
||||||
<p class="card-text"><small>Tags:</small>
|
<p class="card-text"><small>Tags:</small>
|
||||||
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
||||||
{{ t.name }}</a>{# new line is required #}
|
{{ t.name }}</a>{# new line is required #}
|
||||||
|
{% empty %}
|
||||||
|
<span class="badge rounded-pill bg-secondary"><i class="bi bi-ban"></i></span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% load static %}
|
{% load static %}
|
||||||
|
{% load dynamic_url %}
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
{% if d.type == "magazine" %}
|
{% if d.type == "magazine" %}
|
||||||
@@ -30,18 +31,19 @@
|
|||||||
<strong>{{ d.item }}</strong>
|
<strong>{{ d.item }}</strong>
|
||||||
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
||||||
</p>
|
</p>
|
||||||
{% if d.item.tags.all %}
|
|
||||||
<p class="card-text"><small>Tags:</small>
|
<p class="card-text"><small>Tags:</small>
|
||||||
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
||||||
{{ t.name }}</a>{# new line is required #}
|
{{ t.name }}</a>{# new line is required #}
|
||||||
|
{% empty %}
|
||||||
|
<span class="badge rounded-pill bg-secondary"><i class="bi bi-ban"></i></span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" scope="row">
|
<th colspan="2" scope="row">
|
||||||
{{ d.type | capfirst }}
|
{{ d.label|capfirst }}
|
||||||
|
|
||||||
<div class="float-end">
|
<div class="float-end">
|
||||||
{% if not d.item.published %}
|
{% if not d.item.published %}
|
||||||
<span class="badge text-bg-warning">Unpublished</span>
|
<span class="badge text-bg-warning">Unpublished</span>
|
||||||
@@ -56,6 +58,11 @@
|
|||||||
<th class="w-33" scope="row">Magazine</th>
|
<th class="w-33" scope="row">Magazine</th>
|
||||||
<td>{{ d.item.magazine }}</td>
|
<td>{{ d.item.magazine }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<th class="w-33" scope="row">Website</th>
|
||||||
|
<td>{% if d.item.website %}<a href="{{ d.item.website }}" target="_blank">{{ d.item.website_short }}</td>{% else %}-{% endif %}</td>
|
||||||
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Publisher</th>
|
<th class="w-33" scope="row">Publisher</th>
|
||||||
@@ -71,7 +78,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Date</th>
|
<th class="w-33" scope="row">Date</th>
|
||||||
<td>{{ d.item.publication_year|default:"-" }} / {{ d.item.publication_month|default:"-" }}</td>
|
<td>{{ d.item.publication_year|default:"-" }} / {{ d.item.get_publication_month_display|default:"-" }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Pages</th>
|
<th class="w-33" scope="row">Pages</th>
|
||||||
@@ -90,7 +97,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<a class="btn btn-sm btn-outline-primary" href="{{ d.item.get_absolute_url }}">Show all data</a>
|
<a class="btn btn-sm btn-outline-primary" href="{{ d.item.get_absolute_url }}">Show all data</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% url 'admin:bookshelf_magazineissue_change' d.item.pk %}">Edit</a>{% endif %}
|
{% if request.user.is_staff %}<a class="btn btn-sm btn-outline-danger" href="{% dynamic_admin_url 'bookshelf' d.type d.item.pk %}">Edit</a>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,12 +17,10 @@
|
|||||||
<td><img class="logo" src="{{ d.item.logo.url }}" alt="{{ d.item.name }} logo"></td>
|
<td><img class="logo" src="{{ d.item.logo.url }}" alt="{{ d.item.name }} logo"></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if d.item.website %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Website</th>
|
<th class="w-33" scope="row">Website</th>
|
||||||
<td><a href="{{ d.item.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a></td>
|
<td>{% if d.item.website %}<a href="{{ d.item.website }}" target="_blank">{{ d.item.website_short }}</td>{% else %}-{% endif %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-33" scope="row">Category</th>
|
<th class="w-33" scope="row">Category</th>
|
||||||
<td>{{ d.item.category | title }}</td>
|
<td>{{ d.item.category | title }}</td>
|
||||||
|
|||||||
@@ -14,13 +14,13 @@
|
|||||||
<strong>{{ d.item }}</strong>
|
<strong>{{ d.item }}</strong>
|
||||||
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
<a class="stretched-link" href="{{ d.item.get_absolute_url }}"></a>
|
||||||
</p>
|
</p>
|
||||||
{% if d.item.tags.all %}
|
|
||||||
<p class="card-text"><small>Tags:</small>
|
<p class="card-text"><small>Tags:</small>
|
||||||
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
{% for t in d.item.tags.all %}<a href="{% url 'filtered' _filter="tag" search=t.slug %}" class="badge rounded-pill bg-primary">
|
||||||
{{ t.name }}</a>{# new line is required #}
|
{{ t.name }}</a>{# new line is required #}
|
||||||
|
{% empty %}
|
||||||
|
<span class="badge rounded-pill bg-secondary"><i class="bi bi-ban"></i></span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -97,6 +97,14 @@
|
|||||||
{% if magazine.publisher.website %} <a href="{{ magazine.publisher.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>{% endif %}
|
{% if magazine.publisher.website %} <a href="{{ magazine.publisher.website }}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="w-33" scope="row">Website</th>
|
||||||
|
<td>{% if magazine.website %}<a href="{{ magazine.website }}" target="_blank">{{ magazine.website_short }}</td>{% else %}-{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th class="w-33" scope="row">Language</th>
|
||||||
|
<td>{{ magazine.get_language_display }}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">ISBN</th>
|
<th scope="row">ISBN</th>
|
||||||
<td>{{ magazine.ISBN | default:"-" }}</td>
|
<td>{{ magazine.ISBN | default:"-" }}</td>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from django.views import View
|
|||||||
from django.http import Http404, HttpResponseBadRequest
|
from django.http import Http404, HttpResponseBadRequest
|
||||||
from django.db.utils import OperationalError, ProgrammingError
|
from django.db.utils import OperationalError, ProgrammingError
|
||||||
from django.db.models import F, Q, Count
|
from django.db.models import F, Q, Count
|
||||||
|
from django.db.models.functions import Lower
|
||||||
from django.shortcuts import render, get_object_or_404, get_list_or_404
|
from django.shortcuts import render, get_object_or_404, get_list_or_404
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
@@ -73,11 +74,16 @@ class GetData(View):
|
|||||||
.filter(self.filter)
|
.filter(self.filter)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get(self, request, filter=Q(), page=1):
|
def get(self, request, page=1):
|
||||||
self.filter = filter
|
|
||||||
data = []
|
data = []
|
||||||
for item in self.get_data(request):
|
for item in self.get_data(request):
|
||||||
data.append({"type": self.item_type, "item": item})
|
data.append(
|
||||||
|
{
|
||||||
|
"type": self.item_type,
|
||||||
|
"label": self.item_type.capitalize(),
|
||||||
|
"item": item,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, get_items_per_page())
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
@@ -177,16 +183,50 @@ class SearchObjects(View):
|
|||||||
data.append({"type": "consist", "item": item})
|
data.append({"type": "consist", "item": item})
|
||||||
books = (
|
books = (
|
||||||
Book.objects.get_published(request.user)
|
Book.objects.get_published(request.user)
|
||||||
.filter(title__icontains=search)
|
.filter(
|
||||||
|
Q(
|
||||||
|
Q(title__icontains=search)
|
||||||
|
| Q(description__icontains=search)
|
||||||
|
)
|
||||||
|
)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.filter(manufacturer__name__icontains=search)
|
.filter(
|
||||||
|
Q(
|
||||||
|
Q(manufacturer__name__icontains=search)
|
||||||
|
| Q(description__icontains=search)
|
||||||
|
)
|
||||||
|
)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
for item in list(chain(books, catalogs)):
|
for item in list(chain(books, catalogs)):
|
||||||
data.append({"type": "book", "item": item})
|
data.append(
|
||||||
|
{
|
||||||
|
"type": "book",
|
||||||
|
"label": item._meta.object_name,
|
||||||
|
"item": item,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
magazine_issues = (
|
||||||
|
MagazineIssue.objects.get_published(request.user)
|
||||||
|
.filter(
|
||||||
|
Q(
|
||||||
|
Q(magazine__name__icontains=search)
|
||||||
|
| Q(description__icontains=search)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
|
for item in magazine_issues:
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"type": "book",
|
||||||
|
"label": "Magazine Issue",
|
||||||
|
"item": item,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
paginator = Paginator(data, get_items_per_page())
|
paginator = Paginator(data, get_items_per_page())
|
||||||
data = paginator.get_page(page)
|
data = paginator.get_page(page)
|
||||||
@@ -344,15 +384,32 @@ class GetObjectsFiltered(View):
|
|||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
for item in books:
|
|
||||||
data.append({"type": "book", "item": item})
|
|
||||||
catalogs = (
|
catalogs = (
|
||||||
Catalog.objects.get_published(request.user)
|
Catalog.objects.get_published(request.user)
|
||||||
.filter(query_2nd)
|
.filter(query_2nd)
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
)
|
||||||
for item in catalogs:
|
for item in list(chain(books, catalogs)):
|
||||||
data.append({"type": "catalog", "item": item})
|
data.append(
|
||||||
|
{
|
||||||
|
"type": "book",
|
||||||
|
"label": item._meta.object_name,
|
||||||
|
"item": item,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
magazine_issues = (
|
||||||
|
MagazineIssue.objects.get_published(request.user)
|
||||||
|
.filter(query_2nd)
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
|
for item in magazine_issues:
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"type": "book",
|
||||||
|
"label": "Magazine Issue",
|
||||||
|
"item": item,
|
||||||
|
}
|
||||||
|
)
|
||||||
except NameError:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -644,7 +701,7 @@ class Magazines(GetData):
|
|||||||
def get_data(self, request):
|
def get_data(self, request):
|
||||||
return (
|
return (
|
||||||
Magazine.objects.get_published(request.user)
|
Magazine.objects.get_published(request.user)
|
||||||
.all()
|
.order_by(Lower("name"))
|
||||||
.annotate(
|
.annotate(
|
||||||
issues=Count(
|
issues=Count(
|
||||||
"issue",
|
"issue",
|
||||||
@@ -669,6 +726,7 @@ class GetMagazine(View):
|
|||||||
data = [
|
data = [
|
||||||
{
|
{
|
||||||
"type": "magazineissue",
|
"type": "magazineissue",
|
||||||
|
"label": "Magazine issue",
|
||||||
"item": i,
|
"item": i,
|
||||||
}
|
}
|
||||||
for i in magazine.issue.get_published(request.user).all()
|
for i in magazine.issue.get_published(request.user).all()
|
||||||
@@ -712,6 +770,7 @@ class GetMagazineIssue(View):
|
|||||||
"documents": documents,
|
"documents": documents,
|
||||||
"properties": properties,
|
"properties": properties,
|
||||||
"type": "magazineissue",
|
"type": "magazineissue",
|
||||||
|
"label": "Magazine issue",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -742,6 +801,7 @@ class GetBookCatalog(View):
|
|||||||
"documents": documents,
|
"documents": documents,
|
||||||
"properties": properties,
|
"properties": properties,
|
||||||
"type": selector,
|
"type": selector,
|
||||||
|
"label": selector.capitalize(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from ram.utils import git_suffix
|
from ram.utils import git_suffix
|
||||||
|
|
||||||
__version__ = "0.18.1"
|
__version__ = "0.18.6"
|
||||||
__version__ += git_suffix(__file__)
|
__version__ += git_suffix(__file__)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
sample_data/cards/background_860px.png
Normal file
BIN
sample_data/cards/background_860px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.7 KiB |
12
sample_data/cards/generate_card.sh
Executable file
12
sample_data/cards/generate_card.sh
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
mkdir -p output
|
||||||
|
|
||||||
|
for img in input/*.{jpg,png}; do
|
||||||
|
[ -e "$img" ] || continue # skip if no files
|
||||||
|
name=$(basename "${img%.*}").jpg
|
||||||
|
magick convert background.png \
|
||||||
|
\( "$img" -resize x820 \) \
|
||||||
|
-gravity center -composite \
|
||||||
|
-quality 85 -sampling-factor 4:4:4 \
|
||||||
|
"output/$name"
|
||||||
|
done
|
||||||
Reference in New Issue
Block a user