Code optimization
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
6
fet2020/gallery/choices.py
Normal file
6
fet2020/gallery/choices.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Status(models.TextChoices):
|
||||
DRAFT = "10", "DRAFT"
|
||||
PUBLIC = "20", "PUBLIC"
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.")
|
||||
11
fet2020/gallery/managers.py
Normal file
11
fet2020/gallery/managers.py
Normal 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)
|
||||
@@ -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"
|
||||
|
||||
@@ -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"),
|
||||
]
|
||||
|
||||
@@ -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:
|
||||
if not Path(image_path).exists():
|
||||
logger.info("Image path '%s' not found.", image_path)
|
||||
return img_list
|
||||
|
||||
Path(thumb_path).mkdir(exist_ok=True)
|
||||
|
||||
for _file in os.listdir(image_path):
|
||||
if Path(_file).suffix.lower()[1:] not in get_available_image_extensions():
|
||||
continue
|
||||
|
||||
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
|
||||
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": img,
|
||||
"image_url": os.path.join(
|
||||
settings.MEDIA_URL + gallery_path,
|
||||
folder_name + "/" + img,
|
||||
),
|
||||
"thumb_url": thumb_url,
|
||||
"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)
|
||||
|
||||
@@ -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.
|
||||
folders = get_folder_list()
|
||||
if folders:
|
||||
for folder in folders:
|
||||
# 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,
|
||||
),
|
||||
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"))
|
||||
|
||||
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
|
||||
# Show only PUBLIC albums.
|
||||
albums = Album.objects.public()
|
||||
|
||||
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:
|
||||
class AlbumDetailView(DetailView):
|
||||
model = Album
|
||||
template_name = "gallery/album.html"
|
||||
|
||||
def get_queryset(self):
|
||||
return (
|
||||
Album.objects.public()
|
||||
if not self.request.user.is_authenticated
|
||||
else Album.objects.all()
|
||||
)
|
||||
|
||||
|
||||
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,
|
||||
title=folder, slug=slugify(folder), folder_name=folder, event_date=None
|
||||
)
|
||||
break
|
||||
else:
|
||||
raise Http404("wrong album slug")
|
||||
raise Http404("Album slug not found.")
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@authenticated_user
|
||||
def show_draft_album(request, slug):
|
||||
return show_album(request, slug)
|
||||
return album
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user