diff --git a/app/__init__.py b/app/__init__.py
index 0b7695a29..6a1ea6d04 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 45d9d1ddc..ef58a1a77 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 b4c0a033b..104271cff 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 1d8c7cdf2..2e6fff2ff 100644
--- a/app/templates/formsemestre_header.j2
+++ b/app/templates/formsemestre_header.j2
@@ -3,31 +3,31 @@
{% 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 5e8f349f0..23fc83b2f 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 b28a4e57c..0f04cd9e3 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 d62a2b53c..6e6b32142 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 bd9699611..6fe1e446c 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 914213857..1b5b8eccf 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 78ed0dc04..e07a0256e 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":