temp. le 19/11 12h
@ -33,6 +33,7 @@ from app.scodoc.sco_exceptions import (
|
|||||||
)
|
)
|
||||||
from config import DevConfig
|
from config import DevConfig
|
||||||
import sco_version
|
import sco_version
|
||||||
|
from flask_debugtoolbar import DebugToolbarExtension
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
migrate = Migrate(compare_type=True)
|
migrate = Migrate(compare_type=True)
|
||||||
@ -187,6 +188,7 @@ def create_app(config_class=DevConfig):
|
|||||||
moment.init_app(app)
|
moment.init_app(app)
|
||||||
cache.init_app(app)
|
cache.init_app(app)
|
||||||
sco_cache.CACHE = cache
|
sco_cache.CACHE = cache
|
||||||
|
toolbar = DebugToolbarExtension(app)
|
||||||
|
|
||||||
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
||||||
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
||||||
|
181
app/scodoc/sco_config_actions.py
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ScoDoc
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Module main: page d'accueil, avec liste des départements
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2021
|
||||||
|
"""
|
||||||
|
from app.models import ScoDocSiteConfig
|
||||||
|
from app.scodoc.sco_logos import write_logo, find_logo, delete_logo
|
||||||
|
import app
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
|
||||||
|
class Action:
|
||||||
|
"""Base class for all classes describing an action from from config form."""
|
||||||
|
|
||||||
|
def __init__(self, message, parameters):
|
||||||
|
self.message = message
|
||||||
|
self.parameters = parameters
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_action(parameters, stream=None):
|
||||||
|
"""Check (from parameters) if some action has to be done and
|
||||||
|
then return list of action (or else return empty list)."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
"""return a str describing the action to be done"""
|
||||||
|
return self.message.format_map(self.parameters)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
"""Executes the action"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
GLOBAL = "_"
|
||||||
|
|
||||||
|
|
||||||
|
class LogoUpdate(Action):
|
||||||
|
"""Action: change a logo
|
||||||
|
dept_id: dept_id or '_',
|
||||||
|
logo_id: logo_id,
|
||||||
|
upload: image file replacement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parameters):
|
||||||
|
super().__init__(
|
||||||
|
f"Modification du logo {parameters['logo_id']} pour le département {parameters['dept_id']}",
|
||||||
|
parameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_action(parameters):
|
||||||
|
dept_id = parameters["dept_key"]
|
||||||
|
if dept_id == GLOBAL:
|
||||||
|
dept_id = None
|
||||||
|
parameters["dept_id"] = dept_id
|
||||||
|
if parameters["upload"] is not None:
|
||||||
|
return LogoUpdate(parameters)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
current_app.logger.info(self.message)
|
||||||
|
write_logo(
|
||||||
|
stream=self.parameters["upload"],
|
||||||
|
dept_id=self.parameters["dept_id"],
|
||||||
|
name=self.parameters["logo_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoDelete(Action):
|
||||||
|
"""Action: Delete an existing logo
|
||||||
|
dept_id: dept_id or '_',
|
||||||
|
logo_id: logo_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parameters):
|
||||||
|
super().__init__(
|
||||||
|
f"Suppression du logo {parameters['logo_id']} pour le département {parameters['dept_id']}.",
|
||||||
|
parameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_action(parameters):
|
||||||
|
parameters["dept_id"] = parameters["dept_key"]
|
||||||
|
if parameters["dept_key"] == GLOBAL:
|
||||||
|
parameters["dept_id"] = None
|
||||||
|
if parameters["do_delete"]:
|
||||||
|
return LogoDelete(parameters)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
current_app.logger.info(self.message)
|
||||||
|
delete_logo(name=self.parameters["logo_id"], dept_id=self.parameters["dept_id"])
|
||||||
|
|
||||||
|
|
||||||
|
class LogoInsert(Action):
|
||||||
|
"""Action: add a new logo
|
||||||
|
dept_key: dept_id or '_',
|
||||||
|
logo_id: logo_id,
|
||||||
|
upload: image file replacement
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, parameters):
|
||||||
|
super().__init__(
|
||||||
|
f"Ajout du logo {parameters['name']} pour le département {parameters['dept_key']} ({parameters['upload']}).",
|
||||||
|
parameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_action(parameters):
|
||||||
|
if parameters["dept_key"] == GLOBAL:
|
||||||
|
parameters["dept_id"] = None
|
||||||
|
if parameters["upload"] and parameters["name"]:
|
||||||
|
logo = find_logo(
|
||||||
|
logoname=parameters["name"], dept_id=parameters["dept_key"]
|
||||||
|
)
|
||||||
|
if logo is None:
|
||||||
|
return LogoInsert(parameters)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
dept_id = self.parameters["dept_key"]
|
||||||
|
if dept_id == GLOBAL:
|
||||||
|
dept_id = None
|
||||||
|
current_app.logger.info(self.message)
|
||||||
|
write_logo(
|
||||||
|
stream=self.parameters["upload"],
|
||||||
|
name=self.parameters["name"],
|
||||||
|
dept_id=dept_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BonusSportUpdate(Action):
|
||||||
|
"""Action: Change bonus_sport_function_name.
|
||||||
|
bonus_sport_function_name: the new value"""
|
||||||
|
|
||||||
|
def __init__(self, parameters):
|
||||||
|
super().__init__(
|
||||||
|
f"Changement du calcul de bonus sport pour ({parameters['bonus_sport_func_name']}).",
|
||||||
|
parameters,
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_action(parameters):
|
||||||
|
if (
|
||||||
|
parameters["bonus_sport_func_name"]
|
||||||
|
!= ScoDocSiteConfig.get_bonus_sport_func_name()
|
||||||
|
):
|
||||||
|
return [BonusSportUpdate(parameters)]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
current_app.logger.info(self.message)
|
||||||
|
ScoDocSiteConfig.set_bonus_sport_func(self.parameters["bonus_sport_func_name"])
|
||||||
|
app.clear_scodoc_cache()
|
402
app/scodoc/sco_config_form.py
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ScoDoc
|
||||||
|
#
|
||||||
|
# Copyright (c) 1999 - 2021 Emmanuel Viennet. All rights reserved.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
#
|
||||||
|
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Module main: page d'accueil, avec liste des départements
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2021
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
from flask import flash, url_for, redirect, render_template
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask_wtf.file import FileField, FileAllowed
|
||||||
|
from wtforms import SelectField, SubmitField, FormField, validators, FieldList
|
||||||
|
from wtforms.fields.simple import BooleanField, StringField, HiddenField
|
||||||
|
|
||||||
|
from app import AccessDenied
|
||||||
|
from app.models import Departement
|
||||||
|
from app.models import ScoDocSiteConfig
|
||||||
|
from app.scodoc import sco_logos, html_sco_header
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
from app.scodoc.sco_config_actions import (
|
||||||
|
LogoDelete,
|
||||||
|
LogoUpdate,
|
||||||
|
LogoInsert,
|
||||||
|
BonusSportUpdate,
|
||||||
|
)
|
||||||
|
|
||||||
|
from flask_login import current_user
|
||||||
|
|
||||||
|
from app.scodoc.sco_logos import find_logo
|
||||||
|
|
||||||
|
JAVASCRIPTS = html_sco_header.BOOTSTRAP_MULTISELECT_JS + []
|
||||||
|
|
||||||
|
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
|
||||||
|
|
||||||
|
# class ItemForm(FlaskForm):
|
||||||
|
# """Unused Generic class to document common behavior for classes
|
||||||
|
# * ScoConfigurationForm
|
||||||
|
# * DeptForm
|
||||||
|
# * LogoForm
|
||||||
|
# Some or all of these implements:
|
||||||
|
# * Composite design pattern (ScoConfigurationForm and DeptForm)
|
||||||
|
# - a FieldList(FormField(ItemForm))
|
||||||
|
# - FieldListItem are created by browsing the model
|
||||||
|
# - index dictionnary to provide direct access to a SubItemForm
|
||||||
|
# - the direct access method (get_form)
|
||||||
|
# * have some information added to be displayed
|
||||||
|
# - information are collected from a model object
|
||||||
|
# Common methods:
|
||||||
|
# * build(model) (not for LogoForm who has no child)
|
||||||
|
# for each child:
|
||||||
|
# * create en entry in the FieldList for each subitem found
|
||||||
|
# * update self.index
|
||||||
|
# * fill_in additional information into the form
|
||||||
|
# * recursively calls build for each chid
|
||||||
|
# some spécific information may be added after standard processing
|
||||||
|
# (typically header/footer description)
|
||||||
|
# * preview(data)
|
||||||
|
# check the data from a post and build a list of operations that has to be done.
|
||||||
|
# for a two phase process:
|
||||||
|
# * phase 1 (list all opérations)
|
||||||
|
# * phase 2 (may be confirmation and execure)
|
||||||
|
# - if no op found: return to the form with a message 'Aucune modification trouvée'
|
||||||
|
# - only one operation found: execute and go to main page
|
||||||
|
# - more than 1 operation found. asked form confirmation (and execution if confirmed)
|
||||||
|
#
|
||||||
|
# Someday we'll have time to refactor as abstract classes but Abstract FieldList makes this a bit complicated
|
||||||
|
# """
|
||||||
|
|
||||||
|
# Terminology:
|
||||||
|
# dept_id : identifies a dept in modele (= list_logos()). None designates globals logos
|
||||||
|
# dept_key : identifies a dept in this form only (..index[dept_key], and fields 'dept_key').
|
||||||
|
# 'GLOBAL' designates globals logos (we need a string value to set up HiddenField
|
||||||
|
GLOBAL = "_"
|
||||||
|
|
||||||
|
|
||||||
|
def dept_id_to_key(dept_id):
|
||||||
|
if dept_id is None:
|
||||||
|
return GLOBAL
|
||||||
|
return dept_id
|
||||||
|
|
||||||
|
|
||||||
|
def dept_key_to_id(dept_key):
|
||||||
|
if dept_key == GLOBAL:
|
||||||
|
return None
|
||||||
|
return dept_key
|
||||||
|
|
||||||
|
|
||||||
|
class AddLogoForm(FlaskForm):
|
||||||
|
"""Formulaire permettant l'ajout d'un logo (dans un département)"""
|
||||||
|
|
||||||
|
dept_key = HiddenField()
|
||||||
|
name = StringField(
|
||||||
|
label="Nom",
|
||||||
|
validators=[
|
||||||
|
validators.regexp(
|
||||||
|
r"^[a-zA-Z0-9-]*$",
|
||||||
|
re.IGNORECASE,
|
||||||
|
"Ne doit comporter que lettres, chiffres ou -",
|
||||||
|
),
|
||||||
|
validators.Length(
|
||||||
|
max=20, message="Un nom ne doit pas dépasser 20 caractères"
|
||||||
|
),
|
||||||
|
validators.required("Nom de logo requis (alphanumériques ou '-')"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
upload = FileField(
|
||||||
|
label="Sélectionner l'image",
|
||||||
|
validators=[
|
||||||
|
FileAllowed(
|
||||||
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
f"n'accepte que les fichiers image {', '.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}",
|
||||||
|
),
|
||||||
|
validators.required("Fichier image manquant"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
do_insert = SubmitField("ajouter une image")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def validate_name(self, name):
|
||||||
|
dept_id = dept_key_to_id(self.dept_key.data)
|
||||||
|
if dept_id == GLOBAL:
|
||||||
|
dept_id = None
|
||||||
|
if find_logo(logoname=name.data, dept_id=dept_id) is not None:
|
||||||
|
raise validators.ValidationError("Un logo de même nom existe déjà")
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
if self.data["do_insert"]:
|
||||||
|
if self.validate():
|
||||||
|
return LogoInsert.build_action(self.data)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class LogoForm(FlaskForm):
|
||||||
|
"""Embed both presentation of a logo (cf. template file configuration.html)
|
||||||
|
and all its data and UI action (change, delete)"""
|
||||||
|
|
||||||
|
dept_key = HiddenField()
|
||||||
|
logo_id = HiddenField()
|
||||||
|
upload = FileField(
|
||||||
|
label="Remplacer l'image",
|
||||||
|
validators=[
|
||||||
|
FileAllowed(
|
||||||
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
f"n'accepte que les fichiers image {', '.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
do_delete = SubmitField("Supprimer l'image")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.logo = find_logo(
|
||||||
|
logoname=self.logo_id.data, dept_id=dept_key_to_id(self.dept_key.data)
|
||||||
|
).select()
|
||||||
|
self.description = None
|
||||||
|
self.titre = None
|
||||||
|
self.can_delete = True
|
||||||
|
if self.dept_key.data == GLOBAL:
|
||||||
|
if self.logo_id.data == "header":
|
||||||
|
self.can_delete = False
|
||||||
|
self.description = ""
|
||||||
|
self.titre = "Logo en-tête"
|
||||||
|
if self.logo_id.data == "footer":
|
||||||
|
self.can_delete = False
|
||||||
|
self.titre = "Logo pied de page"
|
||||||
|
self.description = ""
|
||||||
|
else:
|
||||||
|
if self.logo_id.data == "header":
|
||||||
|
self.description = "Se substitue au header défini au niveau global"
|
||||||
|
self.titre = "Logo en-tête"
|
||||||
|
if self.logo_id.data == "footer":
|
||||||
|
self.description = "Se substitue au footer défini au niveau global"
|
||||||
|
self.titre = "Logo pied de page"
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
if self.do_delete.data and self.can_delete:
|
||||||
|
return LogoDelete.build_action(self.data)
|
||||||
|
if self.upload.data and self.validate():
|
||||||
|
return LogoUpdate.build_action(self.data)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class DeptForm(FlaskForm):
|
||||||
|
dept_key = HiddenField()
|
||||||
|
dept_name = HiddenField()
|
||||||
|
add_logo = FormField(AddLogoForm)
|
||||||
|
logos = FieldList(FormField(LogoForm))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["meta"] = {"csrf": False}
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def is_local(self):
|
||||||
|
if self.dept_key.data == GLOBAL:
|
||||||
|
return None
|
||||||
|
return True
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
action = self.add_logo.form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
for logo_entry in self.logos.entries:
|
||||||
|
logo_form = logo_entry.form
|
||||||
|
action = logo_form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_form(self, logoname=None):
|
||||||
|
"""Retourne le formulaire associé à un logo. None si pas trouvé"""
|
||||||
|
if logoname is None: # recherche de département
|
||||||
|
return self
|
||||||
|
return self.index.get(logoname, None)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_dept_id_name():
|
||||||
|
"""Cette section assute que tous les départements sont traités (y compris ceux qu'ont pas de logo au départ)
|
||||||
|
et détermine l'ordre d'affichage des DeptForm (GLOBAL d'abord, puis par ordre alpha de nom de département)
|
||||||
|
-> [ (None, None), (dept_id, dept_name)... ]"""
|
||||||
|
depts = [(None, GLOBAL)]
|
||||||
|
for dept in (
|
||||||
|
Departement.query.filter_by(visible=True).order_by(Departement.acronym).all()
|
||||||
|
):
|
||||||
|
depts.append((dept.id, dept.acronym))
|
||||||
|
return depts
|
||||||
|
|
||||||
|
|
||||||
|
def _ordered_logos(modele):
|
||||||
|
"""sort logoname alphabetically but header and footer moved at start. (since there is no space in logoname)"""
|
||||||
|
|
||||||
|
def sort(name):
|
||||||
|
if name == "header":
|
||||||
|
return " 0"
|
||||||
|
if name == "footer":
|
||||||
|
return " 1"
|
||||||
|
return name
|
||||||
|
|
||||||
|
order = sorted(modele.keys(), key=sort)
|
||||||
|
return order
|
||||||
|
|
||||||
|
|
||||||
|
def _make_dept_data(dept_id, dept_name, modele):
|
||||||
|
dept_key = dept_id_to_key(dept_id)
|
||||||
|
data = {
|
||||||
|
"dept_key": dept_key,
|
||||||
|
"dept_name": dept_name,
|
||||||
|
"add_logo": {"dept_key": dept_key},
|
||||||
|
}
|
||||||
|
logos = []
|
||||||
|
if modele is not None:
|
||||||
|
for name in _ordered_logos(modele):
|
||||||
|
logos.append({"dept_key": dept_key, "logo_id": name})
|
||||||
|
data["logos"] = logos
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _make_depts_data(modele):
|
||||||
|
data = []
|
||||||
|
for dept_id, dept_name in _make_dept_id_name():
|
||||||
|
data.append(
|
||||||
|
_make_dept_data(
|
||||||
|
dept_id=dept_id, dept_name=dept_name, modele=modele.get(dept_id, None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def _make_data(bonus_sport, modele):
|
||||||
|
data = {
|
||||||
|
"bonus_sport_func_name": bonus_sport,
|
||||||
|
"depts": _make_depts_data(modele=modele),
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class ScoDocConfigurationForm(FlaskForm):
|
||||||
|
"Panneau de configuration général"
|
||||||
|
bonus_sport_func_name = SelectField(
|
||||||
|
label="Fonction de calcul des bonus sport&culture",
|
||||||
|
choices=[
|
||||||
|
(x, x if x else "Aucune")
|
||||||
|
for x in ScoDocSiteConfig.get_bonus_sport_func_names()
|
||||||
|
],
|
||||||
|
)
|
||||||
|
depts = FieldList(FormField(DeptForm))
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# def _set_global_logos_infos(self):
|
||||||
|
# "specific processing for globals items"
|
||||||
|
# global_header = self.get_form(logoname="header")
|
||||||
|
# global_header.description = (
|
||||||
|
# "image placée en haut de certains documents documents PDF."
|
||||||
|
# )
|
||||||
|
# global_header.titre = "Logo en-tête"
|
||||||
|
# global_header.can_delete = False
|
||||||
|
# global_footer = self.get_form(logoname="footer")
|
||||||
|
# global_footer.description = (
|
||||||
|
# "image placée en pied de page de certains documents documents PDF."
|
||||||
|
# )
|
||||||
|
# global_footer.titre = "Logo pied de page"
|
||||||
|
# global_footer.can_delete = False
|
||||||
|
|
||||||
|
# def _build_dept(self, dept_id, dept_name, modele):
|
||||||
|
# dept_key = dept_id or GLOBAL
|
||||||
|
# data = {"dept_key": dept_key}
|
||||||
|
# entry = self.depts.append_entry(data)
|
||||||
|
# entry.form.build(dept_name, modele.get(dept_id, {}))
|
||||||
|
# self.index[str(dept_key)] = entry.form
|
||||||
|
|
||||||
|
# def build(self, modele):
|
||||||
|
# "Build the Form hierachy (DeptForm, LogoForm) and add extra data (from modele)"
|
||||||
|
# # if entries already initialized (POST). keep subforms
|
||||||
|
# self.index = {}
|
||||||
|
# # create entries in FieldList (one entry per dept
|
||||||
|
# for dept_id, dept_name in self.dept_id_name:
|
||||||
|
# self._build_dept(dept_id=dept_id, dept_name=dept_name, modele=modele)
|
||||||
|
# self._set_global_logos_infos()
|
||||||
|
|
||||||
|
def get_form(self, dept_key=GLOBAL, logoname=None):
|
||||||
|
"""Retourne un formulaire:
|
||||||
|
* pour un département (get_form(dept_id)) ou à un logo (get_form(dept_id, logname))
|
||||||
|
* propre à un département (get_form(dept_id, logoname) ou global (get_form(logoname))
|
||||||
|
retourne None si le formulaire cherché ne peut être trouvé
|
||||||
|
"""
|
||||||
|
dept_form = self.index.get(dept_key, None)
|
||||||
|
if dept_form is None: # département non trouvé
|
||||||
|
return None
|
||||||
|
return dept_form.get_form(logoname)
|
||||||
|
|
||||||
|
def select_action(self):
|
||||||
|
if (
|
||||||
|
self.data["bonus_sport_func_name"]
|
||||||
|
!= ScoDocSiteConfig.get_bonus_sport_func_name()
|
||||||
|
):
|
||||||
|
return BonusSportUpdate(self.data)
|
||||||
|
for dept_entry in self.depts:
|
||||||
|
dept_form = dept_entry.form
|
||||||
|
action = dept_form.select_action()
|
||||||
|
if action:
|
||||||
|
return action
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def configuration():
|
||||||
|
"""Panneau de configuration général"""
|
||||||
|
auth_name = str(current_user)
|
||||||
|
if not current_user.is_administrator():
|
||||||
|
raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name)
|
||||||
|
form = ScoDocConfigurationForm(
|
||||||
|
data=_make_data(
|
||||||
|
bonus_sport=ScoDocSiteConfig.get_bonus_sport_func_name(),
|
||||||
|
modele=sco_logos.list_logos(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if form.is_submitted():
|
||||||
|
action = form.select_action()
|
||||||
|
if action:
|
||||||
|
action.execute()
|
||||||
|
flash(action.message)
|
||||||
|
return redirect(
|
||||||
|
url_for(
|
||||||
|
"scodoc.configuration",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return render_template(
|
||||||
|
"configuration.html",
|
||||||
|
scodoc_dept=None,
|
||||||
|
title="Configuration ScoDoc",
|
||||||
|
form=form,
|
||||||
|
)
|
@ -46,7 +46,7 @@ from app import Departement, ScoValueError
|
|||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
|
||||||
GLOBAL = "_GLOBAL" # category for server level logos
|
GLOBAL = "_" # category for server level logos
|
||||||
|
|
||||||
|
|
||||||
def find_logo(logoname, dept_id=None, strict=False, prefix=scu.LOGO_FILE_PREFIX):
|
def find_logo(logoname, dept_id=None, strict=False, prefix=scu.LOGO_FILE_PREFIX):
|
||||||
@ -70,6 +70,18 @@ def find_logo(logoname, dept_id=None, strict=False, prefix=scu.LOGO_FILE_PREFIX)
|
|||||||
return logo
|
return logo
|
||||||
|
|
||||||
|
|
||||||
|
def delete_logo(name, dept_id=None):
|
||||||
|
"""Delete all files matching logo (dept_id, name) (including all allowed extensions)
|
||||||
|
Args:
|
||||||
|
name: The name of the logo
|
||||||
|
dept_id: the dept_id (if local). Use None to destroy globals logos
|
||||||
|
"""
|
||||||
|
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||||
|
while logo is not None:
|
||||||
|
os.unlink(logo.select().filepath)
|
||||||
|
logo = find_logo(logoname=name, dept_id=dept_id)
|
||||||
|
|
||||||
|
|
||||||
def write_logo(stream, name, dept_id=None):
|
def write_logo(stream, name, dept_id=None):
|
||||||
"""Crée le fichier logo sur le serveur.
|
"""Crée le fichier logo sur le serveur.
|
||||||
Le suffixe du fichier (parmi LOGO_IMAGES_ALLOWED_TYPES) est déduit du contenu du stream"""
|
Le suffixe du fichier (parmi LOGO_IMAGES_ALLOWED_TYPES) est déduit du contenu du stream"""
|
||||||
@ -79,14 +91,15 @@ def write_logo(stream, name, dept_id=None):
|
|||||||
def list_logos():
|
def list_logos():
|
||||||
"""Crée l'inventaire de tous les logos existants.
|
"""Crée l'inventaire de tous les logos existants.
|
||||||
L'inventaire se présente comme un dictionnaire de dictionnaire de Logo:
|
L'inventaire se présente comme un dictionnaire de dictionnaire de Logo:
|
||||||
[GLOBAL][name] pour les logos globaux
|
[None][name] pour les logos globaux
|
||||||
[dept_id][name] pour les logos propres à un département (attention id numérique du dept)
|
[dept_id][name] pour les logos propres à un département (attention id numérique du dept)
|
||||||
|
Les départements sans logos sont absents du résultat
|
||||||
"""
|
"""
|
||||||
inventory = {GLOBAL: _list_dept_logos()} # logos globaux (header / footer)
|
inventory = {None: _list_dept_logos()} # logos globaux (header / footer)
|
||||||
for dept in Departement.query.filter_by(visible=True).all():
|
for dept in Departement.query.filter_by(visible=True).all():
|
||||||
logos_dept = _list_dept_logos(dept_id=dept.id)
|
logos_dept = _list_dept_logos(dept_id=dept.id)
|
||||||
if logos_dept:
|
if logos_dept:
|
||||||
inventory[dept.acronym] = _list_dept_logos(dept.id)
|
inventory[dept.id] = _list_dept_logos(dept.id)
|
||||||
return inventory
|
return inventory
|
||||||
|
|
||||||
|
|
||||||
@ -236,7 +249,7 @@ class Logo:
|
|||||||
"""Retourne l'URL permettant d'obtenir l'image du logo"""
|
"""Retourne l'URL permettant d'obtenir l'image du logo"""
|
||||||
return url_for(
|
return url_for(
|
||||||
"scodoc.get_logo",
|
"scodoc.get_logo",
|
||||||
scodoc_dept=self.scodoc_dept_id,
|
dept_id=self.scodoc_dept_id,
|
||||||
name=self.logoname,
|
name=self.logoname,
|
||||||
global_if_not_found=False,
|
global_if_not_found=False,
|
||||||
)
|
)
|
||||||
@ -245,7 +258,7 @@ class Logo:
|
|||||||
"""Retourne l'URL permettant d'obtenir l'image du logo sous forme de miniature"""
|
"""Retourne l'URL permettant d'obtenir l'image du logo sous forme de miniature"""
|
||||||
return url_for(
|
return url_for(
|
||||||
"scodoc.get_logo_small",
|
"scodoc.get_logo_small",
|
||||||
scodoc_dept=self.scodoc_dept_id,
|
dept_id=self.scodoc_dept_id,
|
||||||
name=self.logoname,
|
name=self.logoname,
|
||||||
global_if_not_found=False,
|
global_if_not_found=False,
|
||||||
)
|
)
|
||||||
@ -254,7 +267,7 @@ class Logo:
|
|||||||
if self.mm is None:
|
if self.mm is None:
|
||||||
return f'<logo name="{self.logoname}" width="?? mm" height="?? mm">'
|
return f'<logo name="{self.logoname}" width="?? mm" height="?? mm">'
|
||||||
else:
|
else:
|
||||||
return f'<logo name="{self.logoname}" width="{self.mm[0]}mm" height="{self.mm[1]}mm">'
|
return f'<logo name="{self.logoname}" width="{self.mm[0]}mm"">'
|
||||||
|
|
||||||
|
|
||||||
def guess_image_type(stream) -> str:
|
def guess_image_type(stream) -> str:
|
||||||
@ -268,7 +281,6 @@ def guess_image_type(stream) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def make_logo_local(logoname, dept_name):
|
def make_logo_local(logoname, dept_name):
|
||||||
breakpoint()
|
|
||||||
depts = Departement.query.filter_by(acronym=dept_name).all()
|
depts = Departement.query.filter_by(acronym=dept_name).all()
|
||||||
if len(depts) == 0:
|
if len(depts) == 0:
|
||||||
print(f"no dept {dept_name} found. aborting")
|
print(f"no dept {dept_name} found. aborting")
|
||||||
|
@ -60,7 +60,6 @@ from reportlab.lib.pagesizes import letter, A4, landscape
|
|||||||
from flask import g
|
from flask import g
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc.sco_logos import find_logo
|
|
||||||
from app.scodoc.sco_utils import CONFIG
|
from app.scodoc.sco_utils import CONFIG
|
||||||
from app import log
|
from app import log
|
||||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
||||||
@ -193,6 +192,10 @@ class ScolarsPageTemplate(PageTemplate):
|
|||||||
preferences=None, # dictionnary with preferences, required
|
preferences=None, # dictionnary with preferences, required
|
||||||
):
|
):
|
||||||
"""Initialise our page template."""
|
"""Initialise our page template."""
|
||||||
|
from app.scodoc.sco_logos import (
|
||||||
|
find_logo,
|
||||||
|
) # defered import (solve circular dependency ->sco_logo ->scodoc, ->sco_pdf
|
||||||
|
|
||||||
self.preferences = preferences
|
self.preferences = preferences
|
||||||
self.pagesbookmarks = pagesbookmarks
|
self.pagesbookmarks = pagesbookmarks
|
||||||
self.pdfmeta_author = author
|
self.pdfmeta_author = author
|
||||||
|
@ -2017,10 +2017,10 @@ class BasePreferences(object):
|
|||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(page_title="Préférences"),
|
html_sco_header.sco_header(page_title="Préférences"),
|
||||||
"<h2>Préférences globales pour %s</h2>" % scu.ScoURL(),
|
"<h2>Préférences globales pour %s</h2>" % scu.ScoURL(),
|
||||||
f"""<p><a href="{url_for("scolar.config_logos", scodoc_dept=g.scodoc_dept)
|
# f"""<p><a href="{url_for("scolar.config_logos", scodoc_dept=g.scodoc_dept)
|
||||||
}">modification des logos du département (pour documents pdf)</a></p>"""
|
# }">modification des logos du département (pour documents pdf)</a></p>"""
|
||||||
if current_user.is_administrator()
|
# if current_user.is_administrator()
|
||||||
else "",
|
# else "",
|
||||||
"""<p class="help">Ces paramètres s'appliquent par défaut à tous les semestres, sauf si ceux-ci définissent des valeurs spécifiques.</p>
|
"""<p class="help">Ces paramètres s'appliquent par défaut à tous les semestres, sauf si ceux-ci définissent des valeurs spécifiques.</p>
|
||||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||||
""",
|
""",
|
||||||
|
@ -867,6 +867,9 @@ div.sco_help {
|
|||||||
|
|
||||||
span.wtf-field ul.errors li {
|
span.wtf-field ul.errors li {
|
||||||
color: red;
|
color: red;
|
||||||
|
}
|
||||||
|
.configuration_logo div.img {
|
||||||
|
|
||||||
}
|
}
|
||||||
.configuration_logo div.img-container {
|
.configuration_logo div.img-container {
|
||||||
width: 256px;
|
width: 256px;
|
||||||
@ -874,6 +877,20 @@ span.wtf-field ul.errors li {
|
|||||||
.configuration_logo div.img-container img {
|
.configuration_logo div.img-container img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
.configuration_logo div.img-data {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.configuration_logo logo-edit titre {
|
||||||
|
background-color:lightblue;
|
||||||
|
}
|
||||||
|
.configuration_logo logo-edit nom {
|
||||||
|
float: left;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
.configuration_logo logo-edit description {
|
||||||
|
float:right;
|
||||||
|
vertical-align:baseline;
|
||||||
|
}
|
||||||
|
|
||||||
p.indent {
|
p.indent {
|
||||||
padding-left: 2em;
|
padding-left: 2em;
|
||||||
|
6
app/static/js/configuration.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
function submit_form() {
|
||||||
|
$("#configuration_form").submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
})
|
81
app/templates/config_dept.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{% macro render_field(field) %}
|
||||||
|
<div>
|
||||||
|
<span class="wtf-field">{{ field.label }} :</span>
|
||||||
|
<span class="wtf-field">{{ field()|safe }}
|
||||||
|
{% if field.errors %}
|
||||||
|
<ul class=errors>
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<li>{{ error }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro render_logo(logo_form, titre=None) %}
|
||||||
|
{% if titre %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<h3>{{ titre }}</h3>
|
||||||
|
<hr/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td style="padding-right: 20px; vertical-align: top;">
|
||||||
|
<p class="help">{{ logo_form.form.description }} Image actuelle:</p>
|
||||||
|
<div class="img-container"><img src="{{ logo_form.logo.get_url_small() }}"
|
||||||
|
alt="pas de logo chargé" /></div>
|
||||||
|
</td>
|
||||||
|
<td style="vertical-align: top;">
|
||||||
|
{{ logo_form.form.dept_id() }}
|
||||||
|
{{ logo_form.form.logo_id() }}
|
||||||
|
Nom: {{ logo_form.form.logo.logoname }}<br/>
|
||||||
|
{# {{ logo_form.form.description }}<br/>#}
|
||||||
|
Format: {{ logo_form.logo.suffix }}<br/>
|
||||||
|
Taille en px: {{ logo_form.logo.size }}<br/>
|
||||||
|
{% if logo_form.logo.mm %}
|
||||||
|
Taile en mm: {{ logo_form.logo.mm }}<br/>
|
||||||
|
{% endif %}
|
||||||
|
Aspect ratio: {{ logo_form.logo.aspect_ratio }}<br/>
|
||||||
|
<hr/>
|
||||||
|
Usage: {{ logo_form.logo.get_usage() }}
|
||||||
|
<hr/>
|
||||||
|
<span class="wtf-field">{{ render_field(logo_form.upload) }}</span>
|
||||||
|
{% if logo_form.can_delete %}
|
||||||
|
{{ render_field(logo_form.do_delete) }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{#{% block app_content %}#}
|
||||||
|
|
||||||
|
{% if scodoc_dept %}
|
||||||
|
<h1>Logos du département {{ scodoc_dept }}</h1>
|
||||||
|
{% else %}
|
||||||
|
<h1>Configuration générale</h1>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
{% if not scodoc_dept %}
|
||||||
|
<div class="sco_help">Les paramètres donnés ici s'appliquent à tout ScoDoc (tous les départements):</div>
|
||||||
|
{{ render_field(form.bonus_sport_func_name)}}
|
||||||
|
<div class="configuration_logo">
|
||||||
|
<table>
|
||||||
|
{{ render_logo(form.header, 'Logo en-tête') }}
|
||||||
|
{{ render_logo(form.footer, 'Logo pied de page') }}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- <div class="sco_help">Les paramètres ci-dessous peuvent être changés dans chaque département
|
||||||
|
(paramétrage).<br />On indique ici les valeurs initiales par défaut:
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<div class="sco-submit">{{ form.submit() }}</div>
|
||||||
|
</form>
|
||||||
|
{#{% endblock %}#}
|
@ -1,10 +1,12 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% import 'bootstrap/wtf.html' as wtf %}
|
{% import 'bootstrap/wtf.html' as wtf %}
|
||||||
|
|
||||||
{% macro render_field(field) %}
|
{% macro render_field(field, with_label=True) %}
|
||||||
<div>
|
<div>
|
||||||
<span class="wtf-field">{{ field.label }} :</span>
|
{% if with_label %}
|
||||||
<span class="wtf-field">{{ field()|safe }}
|
<span class="wtf-field">{{ field.label }} :</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="wtf-field">{{ field(**kwargs)|safe }}
|
||||||
{% if field.errors %}
|
{% if field.errors %}
|
||||||
<ul class=errors>
|
<ul class=errors>
|
||||||
{% for error in field.errors %}
|
{% for error in field.errors %}
|
||||||
@ -16,66 +18,102 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro render_logo(logo_form, titre=None) %}
|
{% macro render_add_logo(add_logo_form) %}
|
||||||
{% if titre %}
|
<div class="logo-add">
|
||||||
<tr>
|
<h3>Ajouter un logo</h3>
|
||||||
<td colspan="2">
|
{{ add_logo_form.hidden_tag() }}
|
||||||
<h3>{{ titre }}</h3>
|
{{ render_field(add_logo_form.name) }}
|
||||||
</td>
|
{{ render_field(add_logo_form.upload) }}
|
||||||
</tr>
|
{{ render_field(add_logo_form.do_insert, False, onSubmit="submit_form") }}
|
||||||
{% endif %}
|
</div>
|
||||||
<tr>
|
|
||||||
<td style="padding-right: 20px">
|
|
||||||
<p class="help">{{ logo_form.form.description }} Image actuelle:</p>
|
|
||||||
<div class="img-container"><img src="{{ logo_form.logo.get_url_small() }}"
|
|
||||||
alt="pas de logo chargé" /></div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Nom: {{ logo_form.form.logo.logoname }}<br/>
|
|
||||||
{{ logo_form.form.description }}<br/>
|
|
||||||
Format: {{ logo_form.logo.suffix }}<br/>
|
|
||||||
Taille en px: {{ logo_form.logo.size }}<br/>
|
|
||||||
{% if logo_form.logo.mm %}
|
|
||||||
Taile en mm: {{ logo_form.logo.mm }}<br/>
|
|
||||||
{% endif %}
|
|
||||||
Aspect ratio: {{ logo_form.logo.aspect_ratio }}<br/>
|
|
||||||
Usage: {{ logo_form.logo.get_usage() }}
|
|
||||||
<span class="wtf-field">{{ logo_form.action()|safe }}</span>
|
|
||||||
<span class="wtf-field">{{ render_field(logo_form.upload) }}</span>
|
|
||||||
{% if logo_form.can_delete %}
|
|
||||||
{{ render_field(logo_form.do_delete) }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro render_logo(dept_form, logo_form) %}
|
||||||
|
<div class="logo-edit">
|
||||||
|
{{ logo_form.hidden_tag() }}
|
||||||
|
{% if logo_form.titre %}
|
||||||
|
<tr class="logo-edit">
|
||||||
|
<td colspan="3" class="titre">
|
||||||
|
<div class="nom"><h3>{{ logo_form.titre }}</h3></div>
|
||||||
|
<div class="description">{{ logo_form.description or "" }}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr class="logo-edit">
|
||||||
|
<td colspan="3" class="titre">
|
||||||
|
<span class="nom"><h3>Logo personalisé: {{ logo_form.logo_id.data }}</h3></span>
|
||||||
|
<span class="description">{{ logo_form.description or "" }}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
<td style="padding-right: 20px; ">
|
||||||
|
<div class="img-container">
|
||||||
|
<img src="{{ logo_form.logo.get_url_small() }}" alt="pas de logo chargé" /></div>
|
||||||
|
</td><td class="img-data">
|
||||||
|
<h3>{{ logo_form.logo.logoname }} (Format: {{ logo_form.logo.suffix }})</h3>
|
||||||
|
Taille: {{ logo_form.logo.size }} px
|
||||||
|
{% if logo_form.logo.mm %} / {{ logo_form.logo.mm }} mm {% endif %}<br/>
|
||||||
|
Aspect ratio: {{ logo_form.logo.aspect_ratio }}<br/>
|
||||||
|
Usage: <span style="font-family: system-ui">{{ logo_form.logo.get_usage() }}</span>
|
||||||
|
</td><td class=""img-action">
|
||||||
|
<p>Modifier l'image</p>
|
||||||
|
<span class="wtf-field">{{ render_field(logo_form.upload, False, onchange="submit_form()") }}</span>
|
||||||
|
{% if logo_form.can_delete %}
|
||||||
|
<p>Supprimer l'image</p>
|
||||||
|
{{ render_field(logo_form.do_delete, False, onSubmit="submit_form()") }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro render_logos(dept_form) %}
|
||||||
|
<table>
|
||||||
|
{% for logo_entry in dept_form.logos.entries %}
|
||||||
|
{% set logo_form = logo_entry.form %}
|
||||||
|
{{ render_logo(dept_form, logo_form) }}
|
||||||
|
{% else %}
|
||||||
|
<p class="logo-edit"><h3>Aucun logo défini en propre à ce département</h3></p>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
|
|
||||||
{% if scodoc_dept %}
|
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||||
<h1>Logos du département {{ scodoc_dept }}</h1>
|
<script src="/ScoDoc/static/js/configuration.js"></script>
|
||||||
{% else %}
|
|
||||||
<h1>Configuration générale</h1>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<form class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
|
<form id="configuration_form" class="sco-form" action="" method="post" enctype="multipart/form-data" novalidate>
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
{% if not scodoc_dept %}
|
|
||||||
<div class="sco_help">Les paramètres donnés ici s'appliquent à tout ScoDoc (tous les départements):</div>
|
|
||||||
{{ render_field(form.bonus_sport_func_name)}}
|
|
||||||
<div class="configuration_logo">
|
|
||||||
<table>
|
|
||||||
{{ form.footer.form.breakpoint(form) }}
|
|
||||||
{{ render_logo(form.header, 'Logo en-tête') }}
|
|
||||||
{{ render_logo(form.footer, 'Logo pied de page') }}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- <div class="sco_help">Les paramètres ci-dessous peuvent être changés dans chaque département
|
<div class="configuration_logo">
|
||||||
(paramétrage).<br />On indique ici les valeurs initiales par défaut:
|
<h1>Configuration générale</h1>
|
||||||
</div> -->
|
<div class="sco_help">Les paramètres donnés ici s'appliquent à tout ScoDoc (tous les départements):</div>
|
||||||
|
{{ render_field(form.bonus_sport_func_name, onChange="submit_form()")}}
|
||||||
|
|
||||||
<div class="sco-submit">{{ form.submit() }}</div>
|
<h1>Bibliothèque de logos</h1>
|
||||||
|
{% for dept_entry in form.depts.entries %}
|
||||||
|
{% set dept_form = dept_entry.form %}
|
||||||
|
{{ dept_entry.form.hidden_tag() }}
|
||||||
|
{% if dept_entry.form.is_local() %}
|
||||||
|
<div class="departement">
|
||||||
|
<h2>Département {{ dept_form.dept_name.data }}</h2>
|
||||||
|
<h3>Logos locaux</h3>
|
||||||
|
<div class="sco_help">Les paramètres donnés sont spécifiques à ce département.<br/>
|
||||||
|
Les logos du département se substituent aux logos de même nom définis globalement:</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="departement">
|
||||||
|
<h2>Logos généraux</h2>
|
||||||
|
<div class="sco_help">Les images de cette section sont utilisé pour tous les départements,
|
||||||
|
mais peuvent être redéfinies localement au niveau de chaque département
|
||||||
|
(il suffit de définir un logo local de même nom)</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{{ render_logos(dept_form) }}
|
||||||
|
{{ render_add_logo(dept_form.add_logo.form) }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -46,7 +46,7 @@ from flask_login.utils import login_required, current_user
|
|||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask_wtf.file import FileField, FileAllowed
|
from flask_wtf.file import FileField, FileAllowed
|
||||||
from werkzeug.exceptions import BadRequest, NotFound
|
from werkzeug.exceptions import BadRequest, NotFound
|
||||||
from wtforms import SelectField, SubmitField, FormField, validators, Form
|
from wtforms import SelectField, SubmitField, FormField, validators, Form, FieldList
|
||||||
from wtforms.fields import IntegerField
|
from wtforms.fields import IntegerField
|
||||||
from wtforms.fields.simple import BooleanField, StringField, TextAreaField, HiddenField
|
from wtforms.fields.simple import BooleanField, StringField, TextAreaField, HiddenField
|
||||||
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
||||||
@ -56,7 +56,7 @@ from app.models import Departement, Identite
|
|||||||
from app.models import FormSemestre, FormsemestreInscription
|
from app.models import FormSemestre, FormsemestreInscription
|
||||||
from app.models import ScoDocSiteConfig
|
from app.models import ScoDocSiteConfig
|
||||||
import sco_version
|
import sco_version
|
||||||
from app.scodoc import sco_logos
|
from app.scodoc import sco_logos, sco_config_form
|
||||||
from app.scodoc import sco_find_etud
|
from app.scodoc import sco_find_etud
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
@ -64,7 +64,9 @@ from app.decorators import (
|
|||||||
scodoc7func,
|
scodoc7func,
|
||||||
scodoc,
|
scodoc,
|
||||||
permission_required_compat_scodoc7,
|
permission_required_compat_scodoc7,
|
||||||
|
permission_required,
|
||||||
)
|
)
|
||||||
|
from app.scodoc.sco_config_form import configuration
|
||||||
from app.scodoc.sco_exceptions import AccessDenied
|
from app.scodoc.sco_exceptions import AccessDenied
|
||||||
from app.scodoc.sco_logos import find_logo
|
from app.scodoc.sco_logos import find_logo
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
@ -180,64 +182,6 @@ def about(scodoc_dept=None):
|
|||||||
|
|
||||||
# ---- CONFIGURATION
|
# ---- CONFIGURATION
|
||||||
|
|
||||||
|
|
||||||
class LogoForm(FlaskForm):
|
|
||||||
action = HiddenField("action")
|
|
||||||
upload = FileField(
|
|
||||||
label="Modifier l'image:",
|
|
||||||
validators=[
|
|
||||||
FileAllowed(
|
|
||||||
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
|
||||||
f"n'accepte que les fichiers image {','.join([e for e in scu.LOGOS_IMAGES_ALLOWED_TYPES])}",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
do_delete = SubmitField("Supprimer")
|
|
||||||
|
|
||||||
def set_infos(self, logo, description=None, can_delete=None):
|
|
||||||
self.logo = logo
|
|
||||||
self.description = description
|
|
||||||
self.can_delete = can_delete
|
|
||||||
|
|
||||||
def breakpoint(self, form):
|
|
||||||
breakpoint()
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(LogoForm, self).__init__(*args, **kwargs)
|
|
||||||
self.logo = None
|
|
||||||
self.description = None
|
|
||||||
self.can_delete = None
|
|
||||||
|
|
||||||
|
|
||||||
class ScoDocConfigurationForm(FlaskForm):
|
|
||||||
"Panneau de configuration général"
|
|
||||||
|
|
||||||
bonus_sport_func_name = SelectField(
|
|
||||||
label="Fonction de calcul des bonus sport&culture",
|
|
||||||
choices=[
|
|
||||||
(x, x if x else "Aucune")
|
|
||||||
for x in ScoDocSiteConfig.get_bonus_sport_func_names()
|
|
||||||
],
|
|
||||||
)
|
|
||||||
header = FormField(LogoForm)
|
|
||||||
footer = FormField(LogoForm)
|
|
||||||
submit = SubmitField("Enregistrer")
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(ScoDocConfigurationForm, self).__init__(*args, **kwargs)
|
|
||||||
breakpoint()
|
|
||||||
self.header.form.set_infos(
|
|
||||||
logo=find_logo("header", dept_id=None).select(),
|
|
||||||
description="image placée en haut de certains documents documents PDF.",
|
|
||||||
can_delete=False,
|
|
||||||
)
|
|
||||||
self.footer.form.set_infos(
|
|
||||||
logo=find_logo("footer", dept_id=None).select(),
|
|
||||||
description="image placée en pied de page de certains documents documents PDF.",
|
|
||||||
can_delete=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Notes pour variables config: (valeurs par défaut des paramètres de département)
|
# Notes pour variables config: (valeurs par défaut des paramètres de département)
|
||||||
# Chaines simples
|
# Chaines simples
|
||||||
# SCOLAR_FONT = "Helvetica"
|
# SCOLAR_FONT = "Helvetica"
|
||||||
@ -259,29 +203,13 @@ class ScoDocConfigurationForm(FlaskForm):
|
|||||||
@bp.route("/ScoDoc/configuration", methods=["GET", "POST"])
|
@bp.route("/ScoDoc/configuration", methods=["GET", "POST"])
|
||||||
@admin_required
|
@admin_required
|
||||||
def configuration():
|
def configuration():
|
||||||
"Panneau de configuration général"
|
auth_name = str(current_user)
|
||||||
form = ScoDocConfigurationForm(
|
if not current_user.is_administrator():
|
||||||
bonus_sport_func_name=ScoDocSiteConfig.get_bonus_sport_func_name(),
|
raise AccessDenied("invalid user (%s) must be SuperAdmin" % auth_name)
|
||||||
)
|
return sco_config_form.configuration()
|
||||||
if form.validate_on_submit():
|
|
||||||
ScoDocSiteConfig.set_bonus_sport_func(form.bonus_sport_func_name.data)
|
|
||||||
if form.header.data:
|
|
||||||
sco_logos.write_logo(stream=form.header.data, name="header")
|
|
||||||
if form.footer.data:
|
|
||||||
sco_logos.write_logo(stream=form.footer.data, name="footer")
|
|
||||||
app.clear_scodoc_cache()
|
|
||||||
flash(f"Configuration enregistrée")
|
|
||||||
return redirect(url_for("scodoc.index"))
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"configuration.html",
|
|
||||||
title="Configuration ScoDoc",
|
|
||||||
form=form,
|
|
||||||
scodoc_dept=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
SMALL_SIZE = (300, 300)
|
SMALL_SIZE = (200, 200)
|
||||||
|
|
||||||
|
|
||||||
def _return_logo(name="header", dept_id="", small=False, strict: bool = True):
|
def _return_logo(name="header", dept_id="", small=False, strict: bool = True):
|
||||||
|
@ -174,7 +174,7 @@ class DeptLogosConfigurationForm(FlaskForm):
|
|||||||
validators=[
|
validators=[
|
||||||
FileAllowed(
|
FileAllowed(
|
||||||
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
f"n'accepte que les fichiers image <tt>{','.join([e for e in scu.LOGOS_IMAGES_ALLOWED_TYPES])}</tt>",
|
f"n'accepte que les fichiers image <tt>{','.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}</tt>",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -185,7 +185,7 @@ class DeptLogosConfigurationForm(FlaskForm):
|
|||||||
validators=[
|
validators=[
|
||||||
FileAllowed(
|
FileAllowed(
|
||||||
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
f"n'accepte que les fichiers image <tt>{','.join([e for e in scu.LOGOS_IMAGES_ALLOWED_TYPES])}</tt>",
|
f"n'accepte que les fichiers image <tt>{','.join(scu.LOGOS_IMAGES_ALLOWED_TYPES)}</tt>",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -193,36 +193,96 @@ class DeptLogosConfigurationForm(FlaskForm):
|
|||||||
submit = SubmitField("Enregistrer")
|
submit = SubmitField("Enregistrer")
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/config_logos", methods=["GET", "POST"])
|
# @bp.route("/config_logos", methods=["GET", "POST"])
|
||||||
@permission_required(Permission.ScoChangePreferences)
|
# @permission_required(Permission.ScoChangePreferences)
|
||||||
def config_logos(scodoc_dept):
|
# def config_logos(scodoc_dept):
|
||||||
"Panneau de configuration général"
|
# "Panneau de configuration général"
|
||||||
form = DeptLogosConfigurationForm()
|
# form = DeptLogosConfigurationForm()
|
||||||
if form.validate_on_submit():
|
# if form.validate_on_submit():
|
||||||
if form.logo_header.data:
|
# if form.logo_header.data:
|
||||||
sco_logos.store_image(
|
# sco_logos.store_image(
|
||||||
form.logo_header.data,
|
# form.logo_header.data,
|
||||||
os.path.join(
|
# os.path.join(
|
||||||
scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_header"
|
# scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_header"
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
if form.logo_footer.data:
|
# if form.logo_footer.data:
|
||||||
sco_logos.store_image(
|
# sco_logos.store_image(
|
||||||
form.logo_footer.data,
|
# form.logo_footer.data,
|
||||||
os.path.join(
|
# os.path.join(
|
||||||
scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_footer"
|
# scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_footer"
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
app.clear_scodoc_cache()
|
# app.clear_scodoc_cache()
|
||||||
flash(f"Logos enregistrés")
|
# flash(f"Logos enregistrés")
|
||||||
return flask.redirect(url_for("scolar.index_html", scodoc_dept=scodoc_dept))
|
# return flask.redirect(url_for("scolar.index_html", scodoc_dept=scodoc_dept))
|
||||||
|
#
|
||||||
|
# return render_template(
|
||||||
|
# "configuration.html",
|
||||||
|
# title="Configuration Logos du département",
|
||||||
|
# form=form,
|
||||||
|
# scodoc_dept=scodoc_dept,
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# class DeptLogosConfigurationForm(FlaskForm):
|
||||||
|
# "Panneau de configuration logos dept"
|
||||||
|
#
|
||||||
|
# logo_header = FileField(
|
||||||
|
# label="Modifier l'image:",
|
||||||
|
# description="logo placé en haut des documents PDF",
|
||||||
|
# validators=[
|
||||||
|
# FileAllowed(
|
||||||
|
# scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
# f"n'accepte que les fichiers image <tt>{','.join([e for e in scu.LOGOS_IMAGES_ALLOWED_TYPES])}</tt>",
|
||||||
|
# )
|
||||||
|
# ],
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# logo_footer = FileField(
|
||||||
|
# label="Modifier l'image:",
|
||||||
|
# description="logo placé en pied des documents PDF",
|
||||||
|
# validators=[
|
||||||
|
# FileAllowed(
|
||||||
|
# scu.LOGOS_IMAGES_ALLOWED_TYPES,
|
||||||
|
# f"n'accepte que les fichiers image <tt>{','.join([e for e in scu.LOGOS_IMAGES_ALLOWED_TYPES])}</tt>",
|
||||||
|
# )
|
||||||
|
# ],
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# submit = SubmitField("Enregistrer")
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"configuration.html",
|
# @bp.route("/config_logos", methods=["GET", "POST"])
|
||||||
title="Configuration Logos du département",
|
# @permission_required(Permission.ScoChangePreferences)
|
||||||
form=form,
|
# def config_logos(scodoc_dept):
|
||||||
scodoc_dept=scodoc_dept,
|
# "Panneau de configuration général"
|
||||||
)
|
# form = DeptLogosConfigurationForm()
|
||||||
|
# if form.validate_on_submit():
|
||||||
|
# if form.logo_header.data:
|
||||||
|
# sco_logos.store_image(
|
||||||
|
# form.logo_header.data,
|
||||||
|
# os.path.join(
|
||||||
|
# scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_header"
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
# if form.logo_footer.data:
|
||||||
|
# sco_logos.store_image(
|
||||||
|
# form.logo_footer.data,
|
||||||
|
# os.path.join(
|
||||||
|
# scu.SCODOC_LOGOS_DIR, "logos_" + scodoc_dept, "logo_footer"
|
||||||
|
# ),
|
||||||
|
# )
|
||||||
|
# app.clear_scodoc_cache()
|
||||||
|
# flash(f"Logos enregistrés")
|
||||||
|
# return flask.redirect(url_for("scolar.index_html", scodoc_dept=scodoc_dept))
|
||||||
|
#
|
||||||
|
# return render_template(
|
||||||
|
# "configuration.html",
|
||||||
|
# title="Configuration Logos du département",
|
||||||
|
# form=form,
|
||||||
|
# scodoc_dept=scodoc_dept,
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 3.7 KiB |
BIN
tests/ressources/test_logos/logo_A1.jpg
Normal file
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.2 KiB |
BIN
tests/ressources/test_logos/logos_2/logo_A1.jpg
Normal file
After Width: | Height: | Size: 3.4 KiB |
@ -7,7 +7,6 @@ Utiliser comme:
|
|||||||
pytest tests/unit/test_logos.py
|
pytest tests/unit/test_logos.py
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from io import BytesIO
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import copytree, copy, rmtree
|
from shutil import copytree, copy, rmtree
|
||||||
|
|
||||||
@ -18,7 +17,14 @@ import app
|
|||||||
from app import db
|
from app import db
|
||||||
from app.models import Departement
|
from app.models import Departement
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc.sco_logos import find_logo, Logo, list_logos
|
from app.scodoc.sco_logos import (
|
||||||
|
find_logo,
|
||||||
|
Logo,
|
||||||
|
list_logos,
|
||||||
|
GLOBAL,
|
||||||
|
write_logo,
|
||||||
|
delete_logo,
|
||||||
|
)
|
||||||
|
|
||||||
RESOURCES_DIR = "/opt/scodoc/tests/ressources/test_logos"
|
RESOURCES_DIR = "/opt/scodoc/tests/ressources/test_logos"
|
||||||
|
|
||||||
@ -30,12 +36,15 @@ def create_dept(test_client):
|
|||||||
"""
|
"""
|
||||||
dept1 = Departement(acronym="RT")
|
dept1 = Departement(acronym="RT")
|
||||||
dept2 = Departement(acronym="INFO")
|
dept2 = Departement(acronym="INFO")
|
||||||
|
dept3 = Departement(acronym="GEA")
|
||||||
db.session.add(dept1)
|
db.session.add(dept1)
|
||||||
db.session.add(dept2)
|
db.session.add(dept2)
|
||||||
|
db.session.add(dept3)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
yield dept1, dept2
|
yield dept1, dept2, dept3
|
||||||
db.session.delete(dept1)
|
db.session.delete(dept1)
|
||||||
db.session.delete(dept2)
|
db.session.delete(dept2)
|
||||||
|
db.session.delete(dept3)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
@ -52,9 +61,10 @@ def create_logos(create_dept):
|
|||||||
+-- logos_{d2} --+-- logo_A.jpg
|
+-- logos_{d2} --+-- logo_A.jpg
|
||||||
|
|
||||||
"""
|
"""
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
d1 = dept1.id
|
d1 = dept1.id
|
||||||
d2 = dept2.id
|
d2 = dept2.id
|
||||||
|
d3 = dept3.id
|
||||||
FILE_LIST = ["logo_A.jpg", "logo_C.jpg", "logo_D.png", "logo_E.jpg", "logo_F.jpeg"]
|
FILE_LIST = ["logo_A.jpg", "logo_C.jpg", "logo_D.png", "logo_E.jpg", "logo_F.jpeg"]
|
||||||
for fn in FILE_LIST:
|
for fn in FILE_LIST:
|
||||||
from_path = Path(RESOURCES_DIR).joinpath(fn)
|
from_path = Path(RESOURCES_DIR).joinpath(fn)
|
||||||
@ -83,33 +93,33 @@ def test_select_global_only(create_logos):
|
|||||||
|
|
||||||
|
|
||||||
def test_select_local_only(create_dept, create_logos):
|
def test_select_local_only(create_dept, create_logos):
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
B_logo = app.scodoc.sco_logos.find_logo(logoname="B", dept_id=dept1.id)
|
B_logo = app.scodoc.sco_logos.find_logo(logoname="B", dept_id=dept1.id)
|
||||||
assert B_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_B.jpg"
|
assert B_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_B.jpg"
|
||||||
|
|
||||||
|
|
||||||
def test_select_local_override_global(create_dept, create_logos):
|
def test_select_local_override_global(create_dept, create_logos):
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
A1_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id)
|
A1_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id)
|
||||||
assert A1_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg"
|
assert A1_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg"
|
||||||
|
|
||||||
|
|
||||||
def test_select_global_with_strict(create_dept, create_logos):
|
def test_select_global_with_strict(create_dept, create_logos):
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
A_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id, strict=True)
|
A_logo = app.scodoc.sco_logos.find_logo(logoname="A", dept_id=dept1.id, strict=True)
|
||||||
assert A_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg"
|
assert A_logo.filepath == f"{scu.SCODOC_LOGOS_DIR}/logos_{dept1.id}/logo_A.jpg"
|
||||||
|
|
||||||
|
|
||||||
def test_looks_for_non_existant_should_give_none(create_dept, create_logos):
|
def test_looks_for_non_existant_should_give_none(create_dept, create_logos):
|
||||||
# search for a local non-existant logo returns None
|
# search for a local non-existant logo returns None
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
no_logo = app.scodoc.sco_logos.find_logo(logoname="Z", dept_id=dept1.id)
|
no_logo = app.scodoc.sco_logos.find_logo(logoname="Z", dept_id=dept1.id)
|
||||||
assert no_logo is None
|
assert no_logo is None
|
||||||
|
|
||||||
|
|
||||||
def test_looks_localy_for_a_global_should_give_none(create_dept, create_logos):
|
def test_looks_localy_for_a_global_should_give_none(create_dept, create_logos):
|
||||||
# search for a local non-existant logo returns None
|
# search for a local non-existant logo returns None
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
no_logo = app.scodoc.sco_logos.find_logo(
|
no_logo = app.scodoc.sco_logos.find_logo(
|
||||||
logoname="C", dept_id=dept1.id, strict=True
|
logoname="C", dept_id=dept1.id, strict=True
|
||||||
)
|
)
|
||||||
@ -123,8 +133,8 @@ def test_get_jpg_data(create_dept, create_logos):
|
|||||||
assert logo.logoname == "A"
|
assert logo.logoname == "A"
|
||||||
assert logo.suffix == "jpg"
|
assert logo.suffix == "jpg"
|
||||||
assert logo.filename == "A.jpg"
|
assert logo.filename == "A.jpg"
|
||||||
assert logo.size == (1200, 600)
|
assert logo.size == (140, 121)
|
||||||
assert logo.mm == approx((40, 30), 0.1)
|
assert logo.mm == approx((5.74, 4.96), 0.1)
|
||||||
|
|
||||||
|
|
||||||
def test_get_png_without_data(create_dept, create_logos):
|
def test_get_png_without_data(create_dept, create_logos):
|
||||||
@ -139,10 +149,59 @@ def test_get_png_without_data(create_dept, create_logos):
|
|||||||
assert logo.mm is None
|
assert logo.mm is None
|
||||||
|
|
||||||
|
|
||||||
def test_create_globale_jpg_logo(create_dept, create_logos):
|
def test_delete_unique_global_jpg_logo(create_dept, create_logos):
|
||||||
|
from_path = Path(RESOURCES_DIR).joinpath("logo_A.jpg")
|
||||||
|
to_path = Path(scu.SCODOC_LOGOS_DIR).joinpath("logo_W.jpg")
|
||||||
|
copy(from_path.absolute(), to_path.absolute())
|
||||||
|
assert to_path.exists()
|
||||||
|
delete_logo(name="W")
|
||||||
|
assert not to_path.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_unique_local_jpg_logo(create_dept, create_logos):
|
||||||
|
dept1, dept2, dept3 = create_dept
|
||||||
|
from_path = Path(RESOURCES_DIR).joinpath("logo_A.jpg")
|
||||||
|
to_path = Path(scu.SCODOC_LOGOS_DIR).joinpath(f"logos_{dept1.id}", "logo_W.jpg")
|
||||||
|
copy(from_path.absolute(), to_path.absolute())
|
||||||
|
assert to_path.exists()
|
||||||
|
delete_logo(name="W", dept_id=dept1.id)
|
||||||
|
assert not to_path.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_multiple_local_jpg_logo(create_dept, create_logos):
|
||||||
|
dept1, dept2, dept3 = create_dept
|
||||||
|
from_path_A = Path(RESOURCES_DIR).joinpath("logo_A.jpg")
|
||||||
|
to_path_A = Path(scu.SCODOC_LOGOS_DIR).joinpath(f"logos_{dept1.id}", "logo_V.jpg")
|
||||||
|
from_path_B = Path(RESOURCES_DIR).joinpath("logo_D.png")
|
||||||
|
to_path_B = Path(scu.SCODOC_LOGOS_DIR).joinpath(f"logos_{dept1.id}", "logo_V.png")
|
||||||
|
copy(from_path_A.absolute(), to_path_A.absolute())
|
||||||
|
copy(from_path_B.absolute(), to_path_B.absolute())
|
||||||
|
assert to_path_A.exists()
|
||||||
|
assert to_path_B.exists()
|
||||||
|
delete_logo(name="V", dept_id=dept1.id)
|
||||||
|
assert not to_path_A.exists()
|
||||||
|
assert not to_path_B.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_global_jpg_logo(create_dept, create_logos):
|
||||||
path = Path(f"{RESOURCES_DIR}/logo_C.jpg")
|
path = Path(f"{RESOURCES_DIR}/logo_C.jpg")
|
||||||
logo = Logo("X") # create global logo
|
|
||||||
stream = path.open("rb")
|
stream = path.open("rb")
|
||||||
|
logo_path = Path(scu.SCODOC_LOGOS_DIR).joinpath("logo_X.jpg")
|
||||||
|
assert not logo_path.exists()
|
||||||
|
write_logo(stream, name="X") # create global logo
|
||||||
|
assert logo_path.exists()
|
||||||
|
logo_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_locale_jpg_logo(create_dept, create_logos):
|
||||||
|
dept1, dept2, dept3 = create_dept
|
||||||
|
path = Path(f"{RESOURCES_DIR}/logo_C.jpg")
|
||||||
|
stream = path.open("rb")
|
||||||
|
logo_path = Path(scu.SCODOC_LOGOS_DIR).joinpath(f"logos_{dept1.id}", "logo_Y.jpg")
|
||||||
|
assert not logo_path.exists()
|
||||||
|
write_logo(stream, name="Y", dept_id=dept1.id) # create global logo
|
||||||
|
assert logo_path.exists()
|
||||||
|
logo_path.unlink(missing_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def test_create_jpg_instead_of_png_logo(create_dept, create_logos):
|
def test_create_jpg_instead_of_png_logo(create_dept, create_logos):
|
||||||
@ -169,15 +228,17 @@ def test_create_jpg_instead_of_png_logo(create_dept, create_logos):
|
|||||||
|
|
||||||
def test_list_logo(create_dept, create_logos):
|
def test_list_logo(create_dept, create_logos):
|
||||||
# test only existence of copied logos. We assumes that they are OK
|
# test only existence of copied logos. We assumes that they are OK
|
||||||
dept1, dept2 = create_dept
|
dept1, dept2, dept3 = create_dept
|
||||||
logos = list_logos()
|
logos = list_logos()
|
||||||
assert logos.keys() == {"_GLOBAL", "RT", "INFO"}
|
assert set(logos.keys()) == {dept1.id, dept2.id, None}
|
||||||
assert {"A", "C", "D", "E", "F", "header", "footer"}.issubset(
|
assert {"A", "C", "D", "E", "F", "header", "footer"}.issubset(
|
||||||
set(logos["_GLOBAL"].keys())
|
set(logos[None].keys())
|
||||||
)
|
)
|
||||||
rt = logos.get("RT", None)
|
rt = logos.get(dept1.id, None)
|
||||||
assert rt is not None
|
assert rt is not None
|
||||||
assert {"A", "B"}.issubset(set(rt.keys()))
|
assert {"A", "B"}.issubset(set(rt.keys()))
|
||||||
info = logos.get("INFO", None)
|
info = logos.get(dept2.id, None)
|
||||||
assert info is not None
|
assert info is not None
|
||||||
assert {"A"}.issubset(set(rt.keys()))
|
assert {"A"}.issubset(set(rt.keys()))
|
||||||
|
gea = logos.get(dept3.id, None)
|
||||||
|
assert gea is None
|
||||||
|