refactoring sco_logos.py
This commit is contained in:
parent
2f72401ba1
commit
2f247d047b
|
@ -34,30 +34,173 @@ SCODOC_LOGOS_DIR /opt/scodoc-data/config/logos
|
|||
"""
|
||||
import imghdr
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from flask import abort, current_app
|
||||
from flask import abort, current_app, url_for
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from app import Departement, ScoValueError
|
||||
from app.scodoc import sco_utils as scu
|
||||
import PIL
|
||||
from PIL import Image as PILImage
|
||||
|
||||
GLOBAL = "_SERVER" # category for server level logos
|
||||
|
||||
|
||||
def get_logo_filename(logo_type: str, scodoc_dept: str) -> str:
|
||||
"""return full filename for this logo, or "" if not found
|
||||
an existing file with extension.
|
||||
logo_type: "header" or "footer"
|
||||
scodoc-dept: acronym
|
||||
def find_logo(logoname, dept_id=None, global_if_not_found=True, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""
|
||||
# Search logos in dept specific dir (/opt/scodoc-data/config/logos/logos_<dept>),
|
||||
# then in config dir /opt/scodoc-data/config/logos/
|
||||
for image_dir in (
|
||||
scu.SCODOC_LOGOS_DIR + "/logos_" + scodoc_dept,
|
||||
scu.SCODOC_LOGOS_DIR, # global logos
|
||||
):
|
||||
for suffix in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
filename = os.path.join(image_dir, f"logo_{logo_type}.{suffix}")
|
||||
if os.path.isfile(filename) and os.access(filename, os.R_OK):
|
||||
return filename
|
||||
"Recherche un logo 'name' existant.
|
||||
Deux strategies:
|
||||
si global_if_not_found:
|
||||
On recherche en local au dept d'abord puis si pas trouvé recherche globale
|
||||
sinon
|
||||
reherche uniquement dans le département puis si non trouvé au niveau global
|
||||
quelquesoit la stratégie, retourne None si pas trouvé
|
||||
:param logoname: le nom recherche
|
||||
:param dept_id: l'id du département dans lequel se fait la recherche (None si global)
|
||||
:param global_if_not_found: stratégie de recherche
|
||||
:param prefix: le prefix utilisé (parmi scu.LOGO_FILE_PREFIX / scu.BACKGROUND_FILE_PREFIX)
|
||||
:return: un objet Logo désignant le fichier image trouvé (ou None)
|
||||
"""
|
||||
try:
|
||||
logo = Logo(logoname, dept_id, prefix).read()
|
||||
except ScoValueError:
|
||||
logo = None
|
||||
if logo is None and global_if_not_found:
|
||||
try:
|
||||
logo = Logo(logoname=logoname, dept_id=None), prefix.read()
|
||||
except ScoValueError:
|
||||
logo = None
|
||||
return logo
|
||||
|
||||
return ""
|
||||
|
||||
def get_logo_filename(name, dept_id=None):
|
||||
breakpoint()
|
||||
return find_logo(name, dept_id).read().filepath
|
||||
|
||||
|
||||
def get_logo_url(name, dept_id):
|
||||
return find_logo(name, dept_id).read().get_url()
|
||||
|
||||
|
||||
def write_logo(stream, name, dept_id=None):
|
||||
Logo(logoname=name, dept_id=dept_id).create(stream)
|
||||
|
||||
|
||||
def list_logos():
|
||||
inventory = {GLOBAL: _list_dept_logos()} # logos globaux (header / footer)
|
||||
for dept in Departement.query.filter_by(visible=True).all():
|
||||
logos_dept = _list_dept_logos(dept_id=dept.id)
|
||||
if logos_dept:
|
||||
inventory[dept.acronym] = _list_dept_logos(dept.id)
|
||||
return inventory
|
||||
|
||||
|
||||
def _list_dept_logos(dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"Inventorie tous les logos existants (retourne un dictionnaire de dictionnaires [dept_id][logoname]"
|
||||
allowed_ext = "|".join(scu.LOGOS_IMAGES_ALLOWED_TYPES)
|
||||
filename_parser = re.compile(f"{prefix}([^.]*).({allowed_ext})")
|
||||
logos = {}
|
||||
path_dir = Path(scu.SCODOC_LOGOS_DIR)
|
||||
if dept_id:
|
||||
path_dir = Path(
|
||||
os.path.sep.join([scu.SCODOC_LOGOS_DIR, LOGOS_DIR_PREFIX + str(dept_id)])
|
||||
)
|
||||
if path_dir.exists():
|
||||
for entry in path_dir.iterdir():
|
||||
if os.access(path_dir.joinpath(entry).absolute(), os.R_OK):
|
||||
result = filename_parser.match(entry.name)
|
||||
if result:
|
||||
logoname = result.group(1)
|
||||
logos[logoname] = Logo(logoname=logoname, dept_id=dept_id).read()
|
||||
return logos if len(logos.keys()) > 0 else None
|
||||
|
||||
|
||||
class Logo:
|
||||
"""Responsable des opérations (read, create), du calcul des chemins et url
|
||||
et de la récupération des informations sur un logp.
|
||||
Usage:
|
||||
logo existant: Logo(<name>, <dept_id>, ...).read()
|
||||
logo en création: Logo(<name>, <dept_id>, ...).create(stream)
|
||||
Les attributs filename, filepath, get_url() ne devraient pas être utilisés avant les opérations
|
||||
read ou save (le format n'est pas encore connu à ce moement là)
|
||||
"""
|
||||
|
||||
def __init__(self, logoname, dept_id=None, prefix=scu.LOGO_FILE_PREFIX):
|
||||
"""Initialisation des noms et département des logos.
|
||||
Le format est renseigné au moment de la lecture (read) ou de la création (create) de l'objet
|
||||
"""
|
||||
self.logoname = secure_filename(logoname)
|
||||
self.scodoc_dept = dept_id
|
||||
self.prefix = prefix
|
||||
self.suffix = None
|
||||
self.dimensions = None
|
||||
if self.scodoc_dept:
|
||||
self.dirpath = os.path.sep.join(
|
||||
[scu.SCODOC_LOGOS_DIR, scu.LOGOS_DIR_PREFIX + secure_filename(str(dept_id))]
|
||||
)
|
||||
else:
|
||||
self.dirpath = scu.SCODOC_LOGOS_DIR
|
||||
self.basepath = os.path.sep.join(
|
||||
[self.dirpath, self.prefix + secure_filename(self.logoname)]
|
||||
)
|
||||
self.filepath = None
|
||||
self.filename = None
|
||||
|
||||
def set_format(self, fmt):
|
||||
self.suffix = fmt
|
||||
self.filepath = self.basepath + "." + fmt
|
||||
self.filename = self.logoname + "." + fmt
|
||||
|
||||
def _ensure_directory_exists(self):
|
||||
"create enclosing directory if necessary"
|
||||
if not Path(self.dirpath).exists():
|
||||
current_app.logger.info(f"sco_logos creating directory %s", self.dirpath)
|
||||
os.mkdir(self.dirpath)
|
||||
|
||||
def create(self, stream):
|
||||
img_type = guess_image_type(stream)
|
||||
if img_type not in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
abort(400, "type d'image invalide")
|
||||
self.set_format(img_type)
|
||||
self._ensure_directory_exists()
|
||||
filename = self.basepath + "." + self.suffix
|
||||
with open(filename, "wb") as f:
|
||||
f.write(stream.read())
|
||||
current_app.logger.info(f"sco_logos.store_image %s", self.filename)
|
||||
# erase other formats if they exists
|
||||
for suffix in set(scu.LOGOS_IMAGES_ALLOWED_TYPES) - set([img_type]):
|
||||
try:
|
||||
os.unlink(self.basepath + "." + suffix)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def read(self):
|
||||
"""
|
||||
Récupération des données pour un logo existant (sinon -> Exception)
|
||||
il doit exister un et un seul fichier image parmi les types autorisés
|
||||
(sinon on considère le premier trouvé)
|
||||
permet d'affiner le format d'un logo de format inconnu
|
||||
"""
|
||||
for suffix in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
path = Path(self.basepath + "." + suffix)
|
||||
if path.exists():
|
||||
self.set_format(suffix)
|
||||
with open(self.filepath, "rb") as f:
|
||||
img = PILImage.open(f)
|
||||
self.dimensions = img.size
|
||||
return self
|
||||
# if no file found, raise exception
|
||||
raise ScoValueError(
|
||||
"Logo %s not found for dept %s" % (self.logoname, self.scodoc_dept)
|
||||
)
|
||||
|
||||
def get_url(self):
|
||||
if self.scodoc_dept
|
||||
return url_for(
|
||||
"scodoc.logo_custom", scodoc_dept=self.scodoc_dept, name=self.logoname
|
||||
)
|
||||
|
||||
|
||||
def guess_image_type(stream) -> str:
|
||||
|
@ -68,28 +211,3 @@ def guess_image_type(stream) -> str:
|
|||
if not fmt:
|
||||
return None
|
||||
return fmt if fmt != "jpeg" else "jpg"
|
||||
|
||||
|
||||
def _ensure_directory_exists(filename):
|
||||
"create enclosing directory if necessary"
|
||||
directory = os.path.split(filename)[0]
|
||||
if not os.path.exists(directory):
|
||||
current_app.logger.info(f"sco_logos creating directory %s", directory)
|
||||
os.mkdir(directory)
|
||||
|
||||
|
||||
def store_image(stream, basename):
|
||||
img_type = guess_image_type(stream)
|
||||
if img_type not in scu.LOGOS_IMAGES_ALLOWED_TYPES:
|
||||
abort(400, "type d'image invalide")
|
||||
filename = basename + "." + img_type
|
||||
_ensure_directory_exists(filename)
|
||||
with open(filename, "wb") as f:
|
||||
f.write(stream.read())
|
||||
current_app.logger.info(f"sco_logos.store_image %s", filename)
|
||||
# erase other formats if they exists
|
||||
for extension in set(scu.LOGOS_IMAGES_ALLOWED_TYPES) - set([img_type]):
|
||||
try:
|
||||
os.unlink(basename + "." + extension)
|
||||
except IOError:
|
||||
pass
|
||||
|
|
|
@ -228,7 +228,13 @@ if not os.path.exists(SCO_TMP_DIR) and os.path.exists(Config.SCODOC_VAR_DIR):
|
|||
# ----- Les logos: /opt/scodoc-data/config/logos
|
||||
SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos")
|
||||
LOGOS_IMAGES_ALLOWED_TYPES = ("jpg", "jpeg", "png") # remind that PIL does not read pdf
|
||||
LOGOS_DIR_PREFIX = "logos_"
|
||||
LOGO_FILE_PREFIX = "logo_"
|
||||
BACKGROUND_FILE_PREFIX = ""
|
||||
|
||||
# forme générale des noms des fichiers logos/background:
|
||||
# SCODOC_LOGO_DIR/LOGO_FILE_PREFIX<name>.<suffix> (fichier global) ou
|
||||
# SCODOC_LOGO_DIR/LOGOS_DIR_PREFIX<dept_id>/LOGO_FILE_PREFIX<name>.<suffix> (fichier départemental)
|
||||
|
||||
# ----- Les outils distribués
|
||||
SCO_TOOLS_DIR = os.path.join(Config.SCODOC_DIR, "tools")
|
||||
|
|
Loading…
Reference in New Issue