Code optimization

This commit is contained in:
2025-02-01 01:21:44 +01:00
parent e68c2eca63
commit ad74ae2c51
14 changed files with 147 additions and 199 deletions

View File

@@ -30,11 +30,6 @@ docker-compose up
### Command Befehle
Erstellt die fehlenden Thumbs für die Alben in der Galerie:
<code>
python3 fet2020/manage.py create_thumbs
</code>
Erstellt alle Searchindexes neu:
<code>
python3 fet2020/manage.py rebuild_index

View File

@@ -15,11 +15,7 @@ class AlbumAdmin(admin.ModelAdmin):
"Der Ordner für die Fotos liegt am Server unter '/mnt/save/fotos/www'. "
"Fette Schriften sind Pflichtfelder."
)
return super().add_view(
request,
form_url,
extra_context=extra_context,
)
return super().add_view(request, form_url, extra_context=extra_context)
def change_view(self, request, object_id, form_url="", extra_context=None):
extra_context = extra_context or {}
@@ -27,12 +23,7 @@ class AlbumAdmin(admin.ModelAdmin):
"Der Ordner für die Fotos liegt am Server unter '/mnt/save/fotos/www'. "
"Fette Schriften sind Pflichtfelder."
)
return super().change_view(
request,
object_id,
form_url,
extra_context=extra_context,
)
return super().change_view(request, object_id, form_url, extra_context=extra_context)
def save_model(self, request, obj, form, change):
obj.author = request.user

View File

@@ -0,0 +1,6 @@
from django.db import models
class Status(models.TextChoices):
DRAFT = "10", "DRAFT"
PUBLIC = "20", "PUBLIC"

View File

@@ -11,11 +11,7 @@ class AlbumAdminForm(forms.ModelForm):
widgets = {"description": CKEditorUploadingWidget(config_name="default")}
labels = {
"slug": "Permalink",
"event_place": "Event Ort",
"description": "Beschreibung",
}
labels = {"slug": "Permalink", "event_place": "Event Ort", "description": "Beschreibung"}
help_texts = {
"folder_name": "Füge den Ordnername (ohne Pfade) ein.",

View File

@@ -1,16 +0,0 @@
import logging
from django.core.management.base import BaseCommand
from gallery.utils import create_thumbs, get_folder_list
logger = logging.getLogger(__name__)
class Command(BaseCommand):
def handle(self, *args, **options):
for folder in get_folder_list():
logger.info("Folder '%s' in process.", folder)
create_thumbs(folder)
logger.info("Command 'create thumbs' ended.")

View File

@@ -0,0 +1,11 @@
from django.db import models
from .choices import Status
class AlbumManager(models.Manager):
def get_queryset(self):
return super().get_queryset().order_by("-event_date")
def public(self):
return self.get_queryset().filter(status=Status.PUBLIC)

View File

@@ -1,45 +1,44 @@
import logging
from random import randint
from django.conf import settings
from django.db import models
from django.urls import reverse
from django.utils import timezone
from django.utils.text import slugify
from .choices import Status
from .managers import AlbumManager
from .utils import get_image_list
logger = logging.getLogger(__name__)
class Album(models.Model):
title = models.CharField(verbose_name="Titel", max_length=200)
slug = models.SlugField(unique=True, null=True, blank=True)
folder_name = models.CharField(verbose_name="Ordner Name", max_length=200)
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, null=True, blank=True)
thumbnail = models.CharField(verbose_name="Thumbnail", max_length=200, blank=True, default="")
event_date = models.DateField(
verbose_name="Event Datum",
null=True,
blank=True,
default=timezone.now,
verbose_name="Event Datum", null=True, blank=True, default=timezone.now
)
event_place = models.CharField(max_length=200, blank=True)
photographer = models.CharField(
verbose_name="Fotograph(en)",
max_length=200,
null=True,
blank=True,
verbose_name="Fotograph(en)", max_length=200, blank=True, default=""
)
DRAFT = "10"
PUBLIC = "20"
STATUS = (
(DRAFT, "DRAFT"),
(PUBLIC, "PUBLIC"),
)
status = models.CharField(max_length=2, choices=STATUS, default=DRAFT)
# TextChoices
Status = Status
description = models.TextField(null=True, blank=True)
status = models.CharField(max_length=2, choices=Status.choices, default=Status.DRAFT)
description = models.TextField(blank=True, default="")
# Managers
objects = models.Manager()
objects = AlbumManager()
class Meta:
verbose_name = "Album"
@@ -57,14 +56,32 @@ class Album(models.Model):
def get_absolute_url(self):
return reverse("gallery:album", kwargs={"slug": self.slug})
def get_images(self):
def clean(self):
if not self.images:
logger.info("No thumbnails are generated.")
@property
def images(self) -> list:
return get_image_list(self.folder_name)
def get_images_length_sub_3(self):
return len(self.get_images()) - 3
return len(self.images) - 3
def get_model_name(self):
return self._meta.model_name
def get_thumbnail(self):
return None
@property
def get_album_thumbnail(self):
if img_list := self.images:
return next(
(
img["thumb_url"]
for img in img_list
if self.thumbnail and self.thumbnail in img["title"]
),
img_list[randint(0, len(img_list) - 1)]["thumb_url"],
)
self.status = Album.Status.DRAFT
logger.info("Album '%s' is empty.", self.title)
return settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"

View File

@@ -1,11 +1,12 @@
from django.urls import path
from . import apps, views
from .views import AlbumDetailView, DraftAlbumDetailView
app_name = apps.GalleryConfig.name
urlpatterns = [
path("", views.index, name="index"),
path("<slug:slug>/", views.show_album, name="album"),
path("draft/<slug:slug>/", views.show_draft_album, name="album_draft"),
path("<slug:slug>/", AlbumDetailView.as_view(), name="album"),
path("draft/<slug:slug>/", DraftAlbumDetailView.as_view(), name="album_draft"),
]

View File

@@ -1,80 +1,56 @@
import logging
import os
from pathlib import Path
from django.conf import settings
from django.core.validators import get_available_image_extensions
from PIL import Image, ImageOps
gallery_path = settings.GALLERY["path"]
gallery_thumb_path = settings.GALLERY["thumb_path"]
gallery_path = Path(settings.MEDIA_ROOT) / settings.GALLERY["path"]
gallery_path_url = Path(settings.MEDIA_URL) / settings.GALLERY["path"]
gallery_thumb_path = Path(settings.MEDIA_ROOT) / settings.GALLERY["thumb_path"]
gallery_thumb_path_url = Path(settings.MEDIA_URL) / settings.GALLERY["thumb_path"]
logger = logging.getLogger(__name__)
size = (320, 320)
valid_images = [".jpg", ".png"]
def get_image_list(folder_name):
file_path = os.path.join(settings.MEDIA_ROOT + gallery_path, folder_name)
def get_image_list(folder_name: str) -> list:
image_path = Path(gallery_path) / folder_name
thumb_path = Path(gallery_thumb_path) / folder_name
img_list = []
if os.path.exists(file_path):
for img in os.listdir(file_path):
ext = os.path.splitext(img)[1]
if ext.lower() not in valid_images:
continue
if not Path(image_path).exists():
logger.info("Image path '%s' not found.", image_path)
return img_list
thumb_path = os.path.join(settings.MEDIA_ROOT + gallery_thumb_path, folder_name)
thumb_file_path = os.path.join(thumb_path, f"thumb_{img}")
if os.path.exists(thumb_file_path):
thumb_url = os.path.join(
settings.MEDIA_URL + gallery_thumb_path,
folder_name + "/" + f"thumb_{img}",
)
else:
thumb_url = None
Path(thumb_path).mkdir(exist_ok=True)
img_dict = {
"title": img,
"image_url": os.path.join(
settings.MEDIA_URL + gallery_path,
folder_name + "/" + img,
),
"thumb_url": thumb_url,
}
for _file in os.listdir(image_path):
if Path(_file).suffix.lower()[1:] not in get_available_image_extensions():
continue
img_list.append(img_dict)
thumb_file_path = Path(thumb_path) / f"thumb_{_file}"
if not Path(thumb_file_path).exists():
with Image.open(Path(image_path) / _file, "r") as im:
if im._getexif() is not None:
im = ImageOps.exif_transpose(im)
thumb = ImageOps.fit(im, size, Image.Resampling.LANCZOS)
thumb.save(thumb_file_path)
logger.info("Save thumb 'thumb_%s'.", _file)
img_dict = {
"title": _file,
"image_url": Path(gallery_path_url) / folder_name / _file,
"thumb_url": Path(gallery_thumb_path_url) / folder_name / f"thumb_{_file}",
}
img_list.append(img_dict)
return img_list
def get_folder_list():
if os.path.exists(settings.MEDIA_ROOT + gallery_path):
return next(os.walk(settings.MEDIA_ROOT + gallery_path))[1]
if Path(gallery_path).exists():
return next(os.walk(gallery_path))[1]
return None
def create_thumbs(folder_path):
file_path = os.path.join(settings.MEDIA_ROOT + gallery_path, folder_path)
thumb_path = os.path.join(settings.MEDIA_ROOT + gallery_thumb_path, folder_path)
if os.path.exists(file_path):
os.makedirs(thumb_path, exist_ok=True)
for f in os.listdir(file_path):
ext = os.path.splitext(f)[1]
if ext.lower() not in valid_images:
continue
thumb_file_path = os.path.join(thumb_path, f"thumb_{f}")
if os.path.exists(thumb_file_path):
continue
image_path = os.path.join(file_path, f)
logger.info("Edit picture '%s'.", f)
with Image.open(str(image_path), "r") as image:
if image._getexif() is not None:
image = ImageOps.exif_transpose(image)
thumb = ImageOps.fit(image, size, Image.ANTIALIAS)
thumb.save(thumb_file_path)
logger.info("Save thumb 'thumb_%s'.", f)

View File

@@ -1,94 +1,65 @@
import logging
from collections import deque
from random import randint
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import Http404
from django.shortcuts import render
from django.utils.text import slugify
from authentications.decorators import authenticated_user
from django.views.generic.detail import DetailView
from .models import Album
from .utils import create_thumbs, get_folder_list
from .utils import get_folder_list
logger = logging.getLogger(__name__)
def index(request):
if request.user.is_authenticated:
albums = deque(Album.objects.all().order_by("-event_date"))
albums = deque(Album.objects.all())
# Get albums that are in the server but not in the db.
for folder in get_folder_list():
if not Album.objects.filter(folder_name=folder):
albums.append(
Album(title=folder, slug=slugify(folder), folder_name=folder, event_date=None)
)
# get albums that are in the server but not in the db.
folders = get_folder_list()
if folders:
for folder in folders:
if not Album.objects.filter(folder_name=folder):
albums.append(
Album(
title=folder,
slug=slugify(folder),
folder_name=folder,
event_date=None,
),
)
else:
# show only PUBLIC albums.
albums = deque(Album.objects.filter(status=Album.PUBLIC).order_by("-event_date"))
# Show only PUBLIC albums.
albums = Album.objects.public()
for album in list(albums):
img_list = album.get_images()
if img_list:
if album.thumbnail:
for img in img_list:
if album.thumbnail in img["title"]:
album.thumbnail = img["thumb_url"]
break
else:
value = randint(0, len(img_list) - 1)
album.thumbnail = img_list[value]["thumb_url"]
else:
value = randint(0, len(img_list) - 1)
album.thumbnail = img_list[value]["thumb_url"]
else:
# empty album is temporarily set to DRAFT.
album.thumbnail = settings.STATIC_URL + "img/FET-Logo-2014-quadrat.png"
album.status = Album.DRAFT
context = {
"albums": albums,
}
context = {"albums": albums}
return render(request, "gallery/index.html", context)
def show_album(request, slug):
album = Album.objects.filter(slug=slug).first()
if not album:
for folder in get_folder_list():
if slug == slugify(folder):
album = Album(
title=folder,
slug=slugify(folder),
folder_name=folder,
event_date=None,
)
break
else:
raise Http404("wrong album slug")
class AlbumDetailView(DetailView):
model = Album
template_name = "gallery/album.html"
img_list = album.get_images()
if not img_list:
# empty album is temporarily set to DRAFT.
album.status = Album.DRAFT
create_thumbs(album.folder_name)
context = {
"album": album,
"images": img_list,
}
return render(request, "gallery/album.html", context)
def get_queryset(self):
return (
Album.objects.public()
if not self.request.user.is_authenticated
else Album.objects.all()
)
@authenticated_user
def show_draft_album(request, slug):
return show_album(request, slug)
class DraftAlbumDetailView(LoginRequiredMixin, DetailView):
model = Album
template_name = "gallery/album.html"
def get_object(self, queryset=None):
slug = self.kwargs.get(self.slug_url_kwarg)
if not (album := Album.objects.filter(slug=slug).first()):
for folder in get_folder_list():
if slug == slugify(folder):
album = Album(
title=folder, slug=slugify(folder), folder_name=folder, event_date=None
)
break
else:
raise Http404("Album slug not found.")
return album

View File

@@ -13,7 +13,7 @@
<h1 class="page-title">Fotos</h1>
<section class="text-gray-800 dark:text-gray-200 my-2 sm:my-4">
{% if album.status == album.DRAFT %}
{% if album.status == album.Status.DRAFT %}
<h2 class="text-gray-900 dark:text-gray-100 text-lg font-medium tracking-wide"><i class="fa-solid fa-eye-slash text-gray-600 dark:text-gray-300 mr-2" title="Album ist nicht öffentlich!"></i>{{ album.title }}</h2>
{% else %}
<h2 class="text-gray-900 dark:text-gray-100 text-lg font-medium tracking-wide">{{ album.title }}</h2>
@@ -34,7 +34,7 @@
{% endif %}
</section>
{% if request.user.is_authenticated %}
{% if request.user.is_authenticated and album.id %}
<a href="{% url 'admin:gallery_album_change' album.id %}" class="page-subtitle block btn-small btn-primary w-full sm:w-max sm:mr-0 sm:ml-auto">
<i class="fa-regular fa-folder-open mr-1"></i>Album bearbeiten
</a>
@@ -52,7 +52,7 @@
</div>
<div id="links" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 2xl:grid-cols-7 justify-items-center gap-3">
{% for image in images %}
{% for image in object.images %}
<a id="{{ image.title }}" href="{{ image.image_url }}" title="{{ image.title }}" class="block max-w-xs sm:max-w-none">
{% if image.thumb_url %}
<img src="{{ image.thumb_url }}" alt="{{ image.title }}" class="rounded-sm">

View File

@@ -15,15 +15,15 @@
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 justify-items-center gap-4">
{% for album in albums %}
{% if request.user.is_authenticated and album.status == album.DRAFT %}
{% if request.user.is_authenticated and album.status == album.Status.DRAFT %}
<a href="{% url 'gallery:album_draft' album.slug %}" class="block max-w-xs sm:max-w-none">
<img src="{{ album.thumbnail }}" class="rounded">
<img src="{{ album.get_album_thumbnail }}" class="rounded">
<h2 class="px-2 text-proprietary-dark dark:text-sky-300">{{ album.title }}</h2>
<h3 class="px-2 text-sm text-proprietary dark:text-sky-400 font-normal"><i class="fa-solid fa-calendar-day mr-1"></i>{{ album.event_date }}</h3>
</a>
{% elif album.status == album.PUBLIC %}
{% elif album.status == album.Status.PUBLIC %}
<a href="{% url 'gallery:album' album.slug %}" class="block max-w-xs sm:max-w-none">
<img src="{{ album.thumbnail }}" class="rounded">
<img src="{{ album.get_album_thumbnail }}" class="rounded">
<h2 class="px-2 text-proprietary-dark dark:text-sky-300">{{ album.title }}</h2>
<h3 class="px-2 text-sm text-proprietary dark:text-sky-400 font-normal"><i class="fa-solid fa-calendar-day mr-1"></i>{{ album.event_date }}</h3>
</a>