140 lines
4.9 KiB
Python
140 lines
4.9 KiB
Python
"""
|
|
Naše typy objektů v galeriích.
|
|
|
|
V databázi jsou všechny v jedné tabulce, protože se liší jen prezentací navenek. Všechny implementují ~~rozhraní~~ ABC `ZobrazitelnyBazmek`.
|
|
|
|
Doporučené použití: TODO
|
|
"""
|
|
|
|
import abc
|
|
from datetime import datetime
|
|
import logging
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
from django.contrib.staticfiles.finders import find
|
|
# FIXME: static fily na lokálním webu??
|
|
find = lambda x: '/static/'+x
|
|
from django.utils.safestring import mark_safe, SafeString
|
|
from django.utils.html import format_html, format_html_join
|
|
from django.urls import reverse
|
|
|
|
from imagekit import ImageSpec
|
|
from imagekit.cachefiles import ImageCacheFile
|
|
from imagekit.processors import ResizeToFit, Transpose
|
|
|
|
HTML = str | SafeString
|
|
|
|
from galerie.models import Obrazek as DbObrazek
|
|
|
|
class ZobrazitelnyBazmek(abc.ABC):
|
|
def __init__(self, db_objekt):
|
|
self.db_objekt = db_objekt
|
|
# zkratka
|
|
self.soubor = db_objekt.soubor
|
|
|
|
# Volá se z templatetagu
|
|
@abc.abstractmethod
|
|
def zobrazit(self, **kwargs) -> HTML:
|
|
"""To, co se zobrazí v galerii jako hlavní obrázek (při prohlížení konkrétního obrázku a jako tittulní obrázek u galerií, které nemají vlastní obrázky (kupř. Vávrovka 2015))"""
|
|
...
|
|
@abc.abstractmethod
|
|
def zmenseny_nahled(self, **kwargs) -> HTML:
|
|
"""Zmenšené obrázky v přehledu obrázků a pod hlavním obrázkem (předchozí/následující)"""
|
|
...
|
|
|
|
@property
|
|
#@abc.abstractmethod
|
|
def cas_porizeni(self) -> datetime | None:
|
|
# TODO: použít tohle na automatické řazení věcí. Má vytáhnout datum z metadat
|
|
raise NotImplementedError("Prosím, naimplementuj hledání času vzniku v metadatech (nebo vrať None).")
|
|
|
|
#TODO: nativní rozměry?
|
|
|
|
|
|
### Obrázky ###
|
|
# Odpovídá původnímu chování (bo se mi nechce vymýšlet novoty…
|
|
class ObrazekStredniSpec(ImageSpec):
|
|
"""Specifikace obrázku pro velké zobrazení"""
|
|
processors = [
|
|
Transpose(Transpose.AUTO), # Rotuj podle dat v EXIFu
|
|
ResizeToFit(900, 675, upscale=False),
|
|
]
|
|
format = 'JPEG'
|
|
options = {'quality': 95} # Proč tolik?
|
|
|
|
class ObrazekMalySpec(ImageSpec):
|
|
"""Specifikace obrázku pro náhledy (pod hlavním obrázkem nebo v přehledu galerie)"""
|
|
processors = [
|
|
Transpose(Transpose.AUTO),
|
|
ResizeToFit(167, 167, upscale=False),
|
|
]
|
|
format = 'JPEG'
|
|
options = {'quality': 95}
|
|
|
|
def _fmt_attrs(attrs):
|
|
return format_html_join(' ', r'{}="{}"', ((mark_safe(k), v) for k, v in attrs.items()))
|
|
|
|
class Obrazek(ZobrazitelnyBazmek):
|
|
"""Obrázek pro zobrazení
|
|
|
|
Použije některý z ImageSpec-ů jako popis transformace a ImageCacheFile pro uložení výsledného obrázku.
|
|
|
|
Reference: https://django-imagekit.readthedocs.io/en/latest/#defining-specs-outside-of-models
|
|
Reference: https://django-imagekit.readthedocs.io/en/latest/caching.html
|
|
"""
|
|
def zobrazit(self, **kwargs):
|
|
# Jak se takový cachefile používá je potřeba vyčíst ze zdrojáků?
|
|
file = ImageCacheFile(ObrazekStredniSpec(source=self.soubor))
|
|
file.generate()
|
|
attrs = _fmt_attrs(kwargs)
|
|
html = format_html(r'<img src="{}" {} />', file.url, attrs)
|
|
return html
|
|
|
|
def zmenseny_nahled(self, **kwargs):
|
|
file = ImageCacheFile(ObrazekMalySpec(source=self.soubor))
|
|
file.generate()
|
|
attrs = _fmt_attrs(kwargs)
|
|
html = format_html(r'<img src="{}" {} />', file.url, attrs)
|
|
return html
|
|
|
|
|
|
class Video(ZobrazitelnyBazmek):
|
|
def __init__(self, *a, **kwa):
|
|
super().__init__(*a, **kwa)
|
|
self.placeholder = find('galerie/video_placeholder.svg')
|
|
|
|
def zobrazit(self, **kwargs):
|
|
attrs = _fmt_attrs(kwargs)
|
|
# Atributy specifické pro video musíme vesměs vyřešit tady… (šlo by to {% if %}-ovat podle typu, ale to je spíš haluz.
|
|
html = format_html(r'<video src="{}" preload="metadata" controls {} />', self.soubor.url, attrs)
|
|
return html
|
|
def zmenseny_nahled(self, **kwargs):
|
|
attrs = _fmt_attrs(kwargs)
|
|
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
|
|
|
|
class DummyBazmek(ZobrazitelnyBazmek):
|
|
def __init__(self, *a, **kwa):
|
|
super().__init__(*a, **kwa)
|
|
self.placeholder = find('galerie/neznamy_placeholder.svg')
|
|
|
|
def zobrazit(self, **kwargs):
|
|
attrs = _fmt_attrs(kwargs)
|
|
# Stavíme HTML ad-hoc, co se může rozbít :'-)
|
|
obrazek = format_html(r'<img src="{}"/>', self.placeholder, attrs)
|
|
popisek = format_html(r'<p style="text-align: center; font-size: 1.1em;">Prosím oprav typ aktuálního obrázku v <a href="{}">adminu</a>!</p>', reverse('admin:galerie_obrazek_change', args=(self.db_objekt.id,)))
|
|
return format_html(r'<div {}>{}{}</div>', attrs, obrazek, popisek)
|
|
def zmenseny_nahled(self, **kwargs):
|
|
attrs = _fmt_attrs(kwargs)
|
|
return format_html(r'<img src="{}" {} />', self.placeholder, attrs)
|
|
|
|
def tipniTyp(soubor) -> DbObrazek.Typ:
|
|
from PIL import Image, UnidentifiedImageError
|
|
try:
|
|
Image.open(soubor)
|
|
return DbObrazek.Typ.OBRAZEK
|
|
except UnidentifiedImageError:
|
|
return DbObrazek.Typ.NEVIM
|
|
logger.warning("Nepodařilo se tipnout typ nečekaným způsobem!")
|
|
return DbObrazek.Typ.NEVIM
|
|
|