diff --git a/app/__init__.py b/app/__init__.py index 0b7695a2..6a1ea6d0 100755 --- a/app/__init__.py +++ b/app/__init__.py @@ -315,12 +315,6 @@ def create_app(config_class=DevConfig): app.register_error_handler(503, postgresql_server_error) app.register_error_handler(APIInvalidParams, handle_invalid_usage) - # Add some globals - # previously in Flask-Bootstrap: - app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance( - field, HiddenField - ) - from app.auth import bp as auth_bp app.register_blueprint(auth_bp, url_prefix="/auth") @@ -338,8 +332,15 @@ def create_app(config_class=DevConfig): from app.api import api_bp from app.api import api_web_bp + # Jinja2 configuration # Enable autoescaping of all templates, including .j2 app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True) + app.jinja_env.trim_blocks = True + app.jinja_env.lstrip_blocks = True + # previously in Flask-Bootstrap: + app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance( + field, HiddenField + ) # https://scodoc.fr/ScoDoc app.register_blueprint(scodoc_bp) diff --git a/app/templates/babase.j2 b/app/templates/babase.j2 index 45d9d1dd..ef58a1a7 100644 --- a/app/templates/babase.j2 +++ b/app/templates/babase.j2 @@ -1,6 +1,4 @@ -{# Base de toutes les pages ScoDoc #} -{% block doc -%} - +{%- block doc -%}{# Base de toutes les pages ScoDoc #} {%- block html %} diff --git a/app/templates/but/change_refcomp.j2 b/app/templates/but/change_refcomp.j2 index b4c0a033..104271cf 100644 --- a/app/templates/but/change_refcomp.j2 +++ b/app/templates/but/change_refcomp.j2 @@ -1,5 +1,5 @@ {# -*- mode: jinja-html -*- #} -{% extends "base.j2" %} +{% extends "sco_page.j2" %} {% import 'wtf.j2' as wtf %} {% block app_content %} diff --git a/app/templates/formsemestre_header.j2 b/app/templates/formsemestre_header.j2 index 1d8c7cdf..2e6fff2f 100644 --- a/app/templates/formsemestre_header.j2 +++ b/app/templates/formsemestre_header.j2 @@ -3,31 +3,31 @@
- {{sco.sem.titre}} - - {% if sco.sem.semestre_id != -1 %}, {{sco.sem.formation.get_cursus().SESSION_NAME}} - {{sco.sem.semestre_id}} + scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id) + }}">{{sco.formsemestre.titre}} + + {% if sco.formsemestre.semestre_id != -1 %}, {{sco.formsemestre.formation.get_cursus().SESSION_NAME}} + {{sco.formsemestre.semestre_id}} {% endif %} - {% if sco.sem.modalite %} en {{sco.sem.modalite}}{% endif %} + {% if sco.formsemestre.modalite %} en {{sco.formsemestre.modalite}}{% endif %} - {{scu.MONTH_NAMES_ABBREV[ sco.sem.date_debut.month - 1]}} - {{sco.sem.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.sem.date_fin.month - 1]}} - {{sco.sem.date_fin.year}} + {{scu.MONTH_NAMES_ABBREV[ sco.formsemestre.date_debut.month - 1]}} + {{sco.formsemestre.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.formsemestre.date_fin.month - 1]}} + {{sco.formsemestre.date_fin.year}} {{sco.sem.responsables_str()}} + title="{{sco.formsemestre.responsables_str(abbrev_prenom=False)}}">{{sco.formsemestre.responsables_str()}} {{sco.sem.inscriptions|length}} inscrits{% if - not sco.sem.etat %}{{scu.icontag("lock_img", border="0", title="Semestre + formsemestre_id=sco.formsemestre.id)}}">{{sco.formsemestre.inscriptions|length}} inscrits{% if + not sco.formsemestre.etat %}{{scu.icontag("lock_img", border="0", title="Semestre verrouillé")|safe}}{% endif %} {% if not scu.is_passerelle_disabled() %} - {% if sco.sem.bul_hide_xml %} + formsemestre_id=sco.formsemestre.id)}}"> + {% if sco.formsemestre.bul_hide_xml %} {{ scu.ICON_HIDDEN|safe}} {% else %} {{ scu.ICON_PUBLISHED|safe }} @@ -36,6 +36,6 @@
- {{ sco.sem_menu_bar|safe }} + {{ sco.formsemestre_menu_bar|safe }}
\ No newline at end of file diff --git a/app/templates/sco_page.j2 b/app/templates/sco_page.j2 index 0591dbcb..177c8b0f 100644 --- a/app/templates/sco_page.j2 +++ b/app/templates/sco_page.j2 @@ -1,5 +1,6 @@ +{%- extends 'babase.j2' -%} {# -*- Base des pages ordinaires, dans départements -*- #} -{% extends 'babase.j2' %} + {% block styles %} {{super()}} @@ -27,7 +28,7 @@
{% include "flashed_messages.j2" %} - {% if sco.sem %} + {% if sco.formsemestre %} {% block formsemestre_header %} {% include "formsemestre_header.j2" %} {% endblock %} diff --git a/app/templates/sidebar.j2 b/app/templates/sidebar.j2 index 5e8f349f..23fc83b2 100755 --- a/app/templates/sidebar.j2 +++ b/app/templates/sidebar.j2 @@ -58,7 +58,7 @@ {% if sco.etud_cur_sem %} ({{sco.prefs["assi_metrique"]}}) -
{{'%1g'|format(sco.nbabsjust)}} J., {{'%1g'|format(sco.nbabsnj)}} N.J.
+
{{'%1g'|format(sco.nb_abs_just)}} J., {{'%1g'|format(sco.nb_abs_nj)}} N.J. {% endif %}
    {% if current_user.has_permission(sco.Permission.AbsChange) %} diff --git a/app/views/__init__.py b/app/views/__init__.py index b28a4e57..0f04cd9e 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -2,6 +2,7 @@ """ScoDoc Flask views """ import datetime +from functools import cached_property from flask import Blueprint from flask import g, current_app, request @@ -57,8 +58,20 @@ class ScoData: self.Permission = Permission self.scu = scu self.SCOVERSION = sco_version.SCOVERSION - # -- Informations étudiant courant, si sélectionné: - if etud is None: + self._init_etud = etud + self._init_formsemestre = formsemestre + # les comptes d'absences sont initialisés lors de l'accès à etud_cur_sem + self.nb_abs_nj = 0 + self.nb_abs_just = 0 + self.nb_abs = 0 + # .etud, .formsemestre, etc. sont des cached_property + # car ScoData() est créé par @context_processor + # AVANT le décorateur scodoc qui initialise g.scodoc_xxx + + @cached_property + def etud(self) -> Identite | None: + "Informations étudiant courant, si sélectionné" + if self._init_etud is None: etudid = g.get("etudid", None) if etudid is None: if request.method == "GET": @@ -66,50 +79,61 @@ class ScoData: elif request.method == "POST": etudid = request.form.get("etudid", None) if etudid is not None: - etud = Identite.get_etud(etudid) - self.etud = etud - if etud is not None: - # Infos sur l'étudiant courant - ins = self.etud.inscription_courante() - if ins: - self.etud_cur_sem = ins.formsemestre - ( - self.nbabsnj, - self.nbabsjust, - self.nbabs, - ) = sco_assiduites.get_assiduites_count_in_interval( - etud.id, - self.etud_cur_sem.date_debut.isoformat(), - self.etud_cur_sem.date_fin.isoformat(), - scu.translate_assiduites_metric( - sco_preferences.get_preference("assi_metrique") - ), - ) - else: - self.etud_cur_sem = None - else: - self.etud = None - # --- Informations sur semestre courant, si sélectionné - if formsemestre is None: - formsemestre_id = retreive_formsemestre_from_request() - if formsemestre_id is not None: - formsemestre = FormSemestre.get_formsemestre(formsemestre_id) - if formsemestre is None: - self.sem = None - self.sem_menu_bar = None - else: - self.sem = formsemestre - self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar( - self.sem + return Identite.get_etud(etudid) + return self._init_etud + + @cached_property + def etud_cur_sem(self) -> FormSemestre | None: + "le semestre courant de l'étudiant courant" + etud = self.etud + if etud is None: + return None + ins = self.etud.inscription_courante() + cur_sem = ins.formsemestre + if ins: + ( + self.nb_abs_nj, + self.nb_abs_just, + self.nb_abs, + ) = sco_assiduites.get_assiduites_count_in_interval( + etud.id, + cur_sem.date_debut.isoformat(), + cur_sem.date_fin.isoformat(), + scu.translate_assiduites_metric( + sco_preferences.get_preference("assi_metrique") + ), ) - self.formsemestre = formsemestre - # --- Préférences - # prefs fallback to global pref if sem is None: - if formsemestre: - formsemestre_id = formsemestre.id - else: - formsemestre_id = None - self.prefs = sco_preferences.SemPreferences(formsemestre_id) + return cur_sem + return None + + @cached_property + def formsemestre(self) -> FormSemestre | None: + "Le formsemestre courant, si sélectionné" + if self._init_formsemestre is None: + formsemestre_id = retreive_formsemestre_from_request() + return ( + FormSemestre.get_formsemestre(formsemestre_id) + if formsemestre_id is not None + else None + ) + return self._init_formsemestre + + @cached_property + def sem_menu_bar(self) -> str | None: + "Le html de la bare de menu formsemestre s'il y en a un." + return ( + sco_formsemestre_status.formsemestre_status_menubar(self.formsemestre) + if self.formsemestre + else None + ) + + @cached_property + def prefs(self): + "Préférences" + # prefs fallback to global pref if no current formsemestre: + return sco_preferences.SemPreferences( + self.formsemestre.id if self.formsemestre else None + ) from app.views import ( diff --git a/app/views/scolar.py b/app/views/scolar.py index d62a2b53..6e6b3214 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -823,7 +823,8 @@ def form_change_coordonnees(etudid): ("telephonemobile", {"size": 13, "title": "Mobile"}), ), initvalues=adr, - submitlabel="Valider le formulaire", + submitlabel="Enregistrer", + cancelbutton="Annuler", ) dest_url = url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid) if tf[0] == 0: diff --git a/sco_version.py b/sco_version.py index bd969961..6fe1e446 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.959" +SCOVERSION = "9.6.960" SCONAME = "ScoDoc" diff --git a/scodoc.py b/scodoc.py index 91421385..1b5b8ecc 100755 --- a/scodoc.py +++ b/scodoc.py @@ -45,7 +45,7 @@ from app.models.evaluations import Evaluation from app.scodoc import sco_dump_db from app.scodoc.sco_logos import make_logo_local from app.scodoc.sco_permissions import Permission -from app.views import notes, scolar +from app.views import notes, scolar, ScoData import app.scodoc.sco_utils as scu import tools from tools.fakedatabase import create_test_api_database @@ -58,9 +58,9 @@ cli.register(app) @app.context_processor def inject_sco_utils(): - "Make scu available in all Jinja templates" + "Make scu and sco available in all Jinja templates" # if modified, put the same in conftest.py#27 - return dict(scu=scu) + return {"scu": scu, "sco": ScoData()} @app.shell_context_processor diff --git a/tests/conftest.py b/tests/conftest.py index 78ed0dc0..e07a0256 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,7 @@ def test_client(): @apptest.context_processor def inject_sco_utils(): "Make scu available in all Jinja templates" - return dict(scu=scu) + return {"scu": scu, "sco": ScoData()} with apptest.test_request_context(): # initialize scodoc "g":