diff --git a/Readme.md b/Readme.md index 49ef4040..fa81c7ef 100644 --- a/Readme.md +++ b/Readme.md @@ -30,11 +30,6 @@ docker-compose up ### Command Befehle -Erstellt die fehlenden Thumbs für die Alben in der Galerie: - -python3 fet2020/manage.py create_thumbs - - Erstellt alle Searchindexes neu: python3 fet2020/manage.py rebuild_index diff --git a/fet2020/gallery/admin.py b/fet2020/gallery/admin.py index 691cc4f0..6b4a0beb 100644 --- a/fet2020/gallery/admin.py +++ b/fet2020/gallery/admin.py @@ -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 diff --git a/fet2020/gallery/choices.py b/fet2020/gallery/choices.py new file mode 100644 index 00000000..d3124a08 --- /dev/null +++ b/fet2020/gallery/choices.py @@ -0,0 +1,6 @@ +from django.db import models + + +class Status(models.TextChoices): + DRAFT = "10", "DRAFT" + PUBLIC = "20", "PUBLIC" diff --git a/fet2020/gallery/forms.py b/fet2020/gallery/forms.py index 498b6c5b..472ff581 100644 --- a/fet2020/gallery/forms.py +++ b/fet2020/gallery/forms.py @@ -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.", diff --git a/fet2020/gallery/management/__init__.py b/fet2020/gallery/management/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/fet2020/gallery/management/commands/__init__.py b/fet2020/gallery/management/commands/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/fet2020/gallery/management/commands/create_thumbs.py b/fet2020/gallery/management/commands/create_thumbs.py deleted file mode 100644 index 8bacdcf9..00000000 --- a/fet2020/gallery/management/commands/create_thumbs.py +++ /dev/null @@ -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.") diff --git a/fet2020/gallery/managers.py b/fet2020/gallery/managers.py new file mode 100644 index 00000000..c58dca14 --- /dev/null +++ b/fet2020/gallery/managers.py @@ -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) diff --git a/fet2020/gallery/models.py b/fet2020/gallery/models.py index 09faac43..a83ce945 100644 --- a/fet2020/gallery/models.py +++ b/fet2020/gallery/models.py @@ -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" diff --git a/fet2020/gallery/urls.py b/fet2020/gallery/urls.py index 9f677242..65a628ed 100644 --- a/fet2020/gallery/urls.py +++ b/fet2020/gallery/urls.py @@ -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("/", views.show_album, name="album"), - path("draft//", views.show_draft_album, name="album_draft"), + path("/", AlbumDetailView.as_view(), name="album"), + path("draft//", DraftAlbumDetailView.as_view(), name="album_draft"), ] diff --git a/fet2020/gallery/utils.py b/fet2020/gallery/utils.py index 4f19b34b..9c15a72e 100644 --- a/fet2020/gallery/utils.py +++ b/fet2020/gallery/utils.py @@ -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) diff --git a/fet2020/gallery/views.py b/fet2020/gallery/views.py index 92d38ee4..4a229cab 100644 --- a/fet2020/gallery/views.py +++ b/fet2020/gallery/views.py @@ -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 diff --git a/fet2020/templates/gallery/album.html b/fet2020/templates/gallery/album.html index ffc6f3c9..e2218a01 100644 --- a/fet2020/templates/gallery/album.html +++ b/fet2020/templates/gallery/album.html @@ -13,7 +13,7 @@

Fotos

- {% if album.status == album.DRAFT %} + {% if album.status == album.Status.DRAFT %}

{{ album.title }}

{% else %}

{{ album.title }}

@@ -34,7 +34,7 @@ {% endif %}
- {% if request.user.is_authenticated %} + {% if request.user.is_authenticated and album.id %} Album bearbeiten @@ -52,7 +52,7 @@