This commit is contained in:
Jean-Marie Place 2021-11-29 09:54:51 +01:00 committed by Jean-Marie Place
parent f40c0d8a6e
commit e1f36d9484
3 changed files with 228 additions and 39 deletions

View File

@ -0,0 +1,143 @@
# -*- 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
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 get_action(parameters):
"""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
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__(
"Modification du logo {logo_id} pour le département {dept_id} ({upload}).",
parameters,
)
@staticmethod
def get_action(parameters):
if parameters["upload"] is not None:
return [LogoUpdate(parameters)]
return []
def execute(self):
raise NotImplementedError
class LogoDelete(Action):
"""Action: Delete an existing logo
dept_id: dept_id or '_',
logo_id: logo_id
"""
def __init__(self, parameters):
super().__init__(
"Suppression du logo {logo_id} pour le département {dept_id}.",
parameters,
)
@staticmethod
def get_action(parameters):
if parameters["do_delete"]:
return [LogoDelete(parameters)]
return []
def execute(self):
raise NotImplementedError
class LogoInsert(Action):
"""Action: add a new logo
dept_id: dept_id or '_',
logo_id: logo_id,
upload: image file replacement
"""
def __init__(self, parameters):
super().__init__(
"Ajout du logo {name} pour le département {dept_id} ({upload}).",
parameters,
)
@staticmethod
def get_action(parameters):
if parameters["upload"] and parameters["name"]:
return [LogoInsert(parameters)]
return []
def execute(self):
raise NotImplementedError
class BonusSportUpdate(Action):
"""Action: Change bonus_sport_function_name.
bonus_sport_function_name: the new value"""
def __init__(self, parameters):
super().__init__(
"Changement du calcul de bonus sport ({bonus_sport_func_name}).",
parameters,
)
@staticmethod
def get_action(parameters):
if parameters["bonus_sport_func_name"] is not None:
return [BonusSportUpdate(parameters)]
return []
def execute(self):
ScoDocSiteConfig.set_bonus_sport_func(self.parameters["bonus_sport_func_name"])

View File

@ -43,13 +43,57 @@ 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,
)
JAVASCRIPTS = html_sco_header.BOOTSTRAP_MULTISELECT_JS + []
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
# ---- CONFIGURATION
# 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 = "_"
class AddLogoForm(FlaskForm):
@ -79,38 +123,9 @@ class AddLogoForm(FlaskForm):
],
)
# 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)
#
# 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 __init__(self, *args, **kwargs):
kwargs["meta"] = {"csrf": False}
super().__init__(*args, **kwargs)
class LogoForm(FlaskForm):
@ -131,6 +146,7 @@ class LogoForm(FlaskForm):
do_delete = BooleanField("Supprimer l'image", default=None)
def __init__(self, *args, **kwargs):
kwargs["meta"] = {"csrf": False}
super().__init__(*args, **kwargs)
self.logo = None
self.description = None
@ -140,6 +156,13 @@ class LogoForm(FlaskForm):
self.logo = modele
self.do_delete.data = False
@staticmethod
def get_actions(logo_data):
actions = []
actions += LogoDelete.get_action(logo_data)
actions += LogoUpdate.get_action(logo_data)
return actions
class DeptForm(FlaskForm):
dept_key = HiddenField()
@ -147,6 +170,7 @@ class DeptForm(FlaskForm):
logos = FieldList(FormField(LogoForm))
def __init__(self, *args, **kwargs):
kwargs["meta"] = {"csrf": False}
super().__init__(*args, **kwargs)
self.index = None
self.dept_name = None
@ -197,6 +221,14 @@ class DeptForm(FlaskForm):
self.index[logoname] = entry.form
entry.form.build(modele[logoname])
def get_actions(self, dept_data):
actions = []
actions += LogoInsert.get_action(dept_data["add_logo"])
for logo_data in dept_data["logos"]:
logo_form = self.index[logo_data["logo_id"]]
actions += logo_form.get_actions(logo_data)
return actions
def get_form(self, logoname=None):
"""Retourne le formulaire associé à un logo. None si pas trouvé"""
if logoname is None: # recherche de département
@ -206,7 +238,6 @@ class DeptForm(FlaskForm):
class ScoDocConfigurationForm(FlaskForm):
"Panneau de configuration général"
bonus_sport_func_name = SelectField(
label="Fonction de calcul des bonus sport&culture",
choices=[
@ -253,10 +284,10 @@ class ScoDocConfigurationForm(FlaskForm):
def _build_dept(self, dept_id, dept_name, modele):
dept_key = dept_id or GLOBAL
data = {"dept_id": dept_key}
data = {"dept_key": dept_key}
entry = self.depts.append_entry(data)
entry.form.build(dept_name, modele.get(dept_id, {}))
self.index[dept_key] = entry.form
self.index[str(dept_key)] = entry.form
def build(self, modele):
"Build the Form hierachy (DeptForm, LogoForm) and add extra data (from modele)"
@ -278,16 +309,27 @@ class ScoDocConfigurationForm(FlaskForm):
return None
return dept_form.get_form(logoname)
def get_actions(self, data):
actions = []
if BonusSportUpdate.get_action(data):
actions.append(BonusSportUpdate(data))
for dept_data in data["depts"]:
dept_form = self.index[str(dept_data["dept_key"])]
actions += dept_form.get_actions(dept_data)
return actions
def configuration():
"Panneau de configuration général"
"""Panneau de configuration général"""
form = ScoDocConfigurationForm(
bonus_sport_func_name=ScoDocSiteConfig.get_bonus_sport_func_name(),
)
modele = sco_logos.list_logos()
form.build(modele)
if form.validate_on_submit():
ScoDocSiteConfig.set_bonus_sport_func(form.bonus_sport_func_name.data)
actions = form.get_actions(form.data)
for action in actions:
action.execute()
# if form.header.data:
# sco_logos.write_logo(stream=form.header.data, name="header")
# if form.footer.data:

View File

@ -19,6 +19,7 @@
{% macro render_add_logo(add_logo_form) %}
<div class=""logo-add">
<h3>Ajouter un logo</h3>
{{ add_logo_form.hidden_tag() }}
{{ render_field(add_logo_form.name) }}
{{ render_field(add_logo_form.upload) }}
<div>
@ -26,6 +27,7 @@
{% macro render_logo(logo_form) %}
<div class="logo-edit">
{{ logo_form.hidden_tag() }}
{% if logo_form.titre %}
<tr class="logo-edit">
<td colspan="2" class=""titre">
@ -67,6 +69,8 @@
<table>
{% for logoform in dept_form.index.values() %}
{{ render_logo(logoform) }}
{% else %}
<p class="logo-edit">Aucun logo défini en propre à ce département</p>
{% endfor %}
</table>
{% endmacro %}
@ -83,6 +87,7 @@
<h1>Bibliothèque de logos</h1>
{% for dept, dept_form in form.index.items() %}
{{ dept_form.hidden_tag() }}
{% if dept_form.is_local() %}
<div class=""departement">
<h2>Logos du département {{ dept_form.dept_name }}</h2>
@ -103,7 +108,6 @@
{% endif %}
{% endfor %}
</div>
<div class="sco-submit">{{ form.submit() }}</div>
</form>
{% endblock %}