From e2b863a5deb2672c47d6c1f4507ae869e7879ba3 Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 22 Aug 2023 15:43:10 +0200 Subject: [PATCH 1/8] =?UTF-8?q?Assiduites=20:=20pr=C3=A9f=C3=A9rences=20in?= =?UTF-8?q?ternes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_assiduites.py | 2 +- app/scodoc/sco_preferences.py | 11 ++----- app/scodoc/sco_utils.py | 62 ++++++++++++++++++++++++++++++----- app/tables/visu_assiduites.py | 4 +-- app/views/assiduites.py | 14 +++++--- 5 files changed, 68 insertions(+), 25 deletions(-) diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py index 98e9af5fc..230e729dd 100644 --- a/app/scodoc/sco_assiduites.py +++ b/app/scodoc/sco_assiduites.py @@ -427,7 +427,7 @@ def invalidate_assiduites_count(etudid, sem): """Invalidate (clear) cached counts""" date_debut = sem["date_debut_iso"] date_fin = sem["date_fin_iso"] - for met in sco_preferences.ASSIDUITES_METRIC_LABEL.values(): + for met in [string.lower() for string in scu.AssiduitesMetricShort.all()]: key = str(etudid) + "_" + date_debut + "_" + date_fin + f"{met}_assiduites" sco_cache.AbsSemEtudCache.delete(key) diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index c97c4c48a..5a3784535 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -198,13 +198,6 @@ def _get_pref_default_value_from_config(name, pref_spec): _INSTALLED_FONTS = ", ".join(sco_pdf.get_available_font_names()) -ASSIDUITES_METRIC_LABEL = { - # l'ordre est important, c'est celui-du menu. Le defaut en 1er donc. - "1/2 J.": "demi", - "J.": "journee", - "H.": "heure", -} - PREF_CATEGORIES = ( # sur page "Paramètres" ("general", {"title": ""}), # voir paramètre titlr de TrivialFormulator @@ -666,8 +659,8 @@ class BasePreferences(object): { "initvalue": "1/2 J.", "input_type": "menu", - "labels": list(ASSIDUITES_METRIC_LABEL.keys()), - "allowed_values": list(ASSIDUITES_METRIC_LABEL.keys()), + "labels": scu.AssiduitesMetrics.LONG, + "allowed_values": scu.AssiduitesMetrics.SHORT, "title": "Métrique de l'assiduité", "explanation": "Unité utilisée dans la fiche étudiante, les bilans et les calculs", "category": "assi", diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 6208c16e1..e22e0e87b 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -163,6 +163,11 @@ class BiDirectionalEnum(Enum): """Vérifie sur un attribut existe dans l'enum""" return attr.upper() in cls._member_names_ + @classmethod + def all(cls, keys=True): + """Retourne toutes les clés de l'enum""" + return cls._member_names_ if keys else list(cls._value2member_map_.keys()) + @classmethod def get(cls, attr: str, default: any = None): """Récupère une valeur à partir de son attribut""" @@ -251,15 +256,54 @@ def is_period_overlapping( return p_deb < i_fin and p_fin > i_deb -def translate_assiduites_metric(hr_metric) -> str: - if hr_metric == "1/2 J.": - return "demi" - if hr_metric == "J.": - return "journee" - if hr_metric == "N.": - return "compte" - if hr_metric == "H.": - return "heure" +class AssiduitesMetrics: + """Labels associés au métrique de l'assiduité""" + + SHORT: list[str] = ["1/2 J.", "J.", "H."] + LONG: list[str] = ["Demi-Journée", "Journée", "Heure"] + TAG: list[str] = ["demi", "journee", "heure"] + + +def translate_assiduites_metric(metric, inverse=True, short=True) -> str: + """ + translate_assiduites_metric + + SHORT[true] : "J." "H." "N." "1/2 J." + SHORT[false] : "Journée" "Heure" "Nombre" "Demi-Journée" + + inverse[false] : "demi" -> "1/2 J." + inverse[true] : "1/2 J." -> "demi" + + + Args: + metric (str): la métrique à traduire + inverse (bool, optional). Defaults to True. + short (bool, optional). Defaults to True. + + Returns: + str: la métrique traduite + """ + index: int = None + if not inverse: + try: + index = AssiduitesMetrics.TAG.index(metric) + return ( + AssiduitesMetrics.SHORT[index] + if short + else AssiduitesMetrics.LONG[index] + ) + except ValueError: + return None + + try: + index = ( + AssiduitesMetrics.SHORT.index(metric) + if short + else AssiduitesMetrics.LONG.index(metric) + ) + return AssiduitesMetrics.TAG[index] + except ValueError: + return None # Types de modules diff --git a/app/tables/visu_assiduites.py b/app/tables/visu_assiduites.py index 0907da90f..9d1adff30 100644 --- a/app/tables/visu_assiduites.py +++ b/app/tables/visu_assiduites.py @@ -125,8 +125,8 @@ class RowAssi(tb.Row): "absent": ["Absences", 0.0, 0.0], } - assi_metric = sco_preferences.ASSIDUITES_METRIC_LABEL.get( - sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id) + assi_metric = scu.translate_assiduites_metric( + sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id), ) for etat, valeur in retour.items(): diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 4d26a8ef3..b11e6b115 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -327,8 +327,8 @@ def bilan_etud(): date_debut: str = f"{scu.annee_scolaire()}-09-01" date_fin: str = f"{scu.annee_scolaire()+1}-06-30" - assi_metric = sco_preferences.ASSIDUITES_METRIC_LABEL.get( - sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id) + assi_metric = scu.translate_assiduites_metric( + sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id), ) return HTMLBuilder( @@ -840,8 +840,14 @@ def visu_assi_group(): return render_template( "assiduites/pages/visu_assi.j2", - assi_metric=sco_preferences.ASSIDUITES_METRIC_LABEL.get( - sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id) + assi_metric=scu.translate_assiduites_metric( + scu.translate_assiduites_metric( + sco_preferences.get_preference( + "assi_metrique", dept_id=g.scodoc_dept_id + ), + ), + inverse=False, + short=False, ), date_debut=dates["debut"], date_fin=dates["fin"], From a04403cd0e2e0c8be35499bfcc04503d55db11dd Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 22 Aug 2023 16:06:56 +0200 Subject: [PATCH 2/8] Assiduites : fixes select annee cal + saisie par null --- app/templates/assiduites/pages/calendrier.j2 | 12 +++++++----- app/templates/assiduites/widgets/minitimeline.j2 | 6 +++++- app/views/assiduites.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index 76ab5eb3a..ce8e9e0f1 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -336,19 +336,21 @@ } const defAnnee = {{ annee }} + let annees = {{ annees | safe }} + annees = annees.filter((x, i) => annees.indexOf(x) === i) const etudid = {{ sco.etud.id }}; const nonwork = [{{ nonworkdays | safe }}]; window.onload = () => { const select = document.querySelector('#annee'); - for (let i = defAnnee + 1; i > defAnnee - 6; i--) { + annees.forEach((a) => { const opt = document.createElement("option"); - opt.value = i + "", - opt.textContent = i + ""; - if (i === defAnnee) { + opt.value = a + "", + opt.textContent = `${a} - ${a + 1}`; + if (a === defAnnee) { opt.selected = true; } select.appendChild(opt) - } + }) setterAnnee(defAnnee) }; diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2 index 811fd1937..f3febd833 100644 --- a/app/templates/assiduites/widgets/minitimeline.j2 +++ b/app/templates/assiduites/widgets/minitimeline.j2 @@ -162,7 +162,11 @@ userIdDiv.textContent = `saisi le ${formatDateModal( assiduite.entry_date, "à" - )} \npar ${assiduite.user_id}`; + )}`; + + if (assiduite.user_id != null) { + userIdDiv.textContent += `\npar ${assiduite.user_id}` + } bubble.appendChild(userIdDiv); bubble.style.left = `${event.clientX - bubble.offsetWidth / 2}px`; diff --git a/app/views/assiduites.py b/app/views/assiduites.py index b11e6b115..0969c4aed 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -417,6 +417,16 @@ def calendrier_etud(): ], ) + annees: list[int] = sorted( + [ins.formsemestre.date_debut.year for ins in etud.formsemestre_inscriptions], + reverse=True, + ) + + annees_str: str = "[" + for ann in annees: + annees_str += f"{ann}," + annees_str += "]" + return HTMLBuilder( header, render_template( @@ -425,6 +435,7 @@ def calendrier_etud(): annee=scu.annee_scolaire(), nonworkdays=_non_work_days(), minitimeline=_mini_timeline(), + annees=annees_str, ), ).build() From efaeeb7b5fcaa408882776bd5ac3e5dab24f7d02 Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 22 Aug 2023 16:18:58 +0200 Subject: [PATCH 3/8] Assiduites : migration heure aprem --- scodoc.py | 15 ++++++++++++--- tools/migrate_abs_to_assiduites.py | 10 +++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/scodoc.py b/scodoc.py index aef11ae0a..f7e7266f7 100755 --- a/scodoc.py +++ b/scodoc.py @@ -660,7 +660,12 @@ def profile(host, port, length, profile_dir): @click.option( "-n", "--noon", - help="Spécifie l'heure de fin du matin (et donc début de l'après-midi) format `hh:mm`", + help="Spécifie l'heure de fin du matin format `hh:mm`", +) +@click.option( + "-a", + "--afternoon", + help="Spécifie l'heure de début de l'après-midi format `hh:mm` valeur identique à --noon si non spécifié", ) @click.option( "-e", @@ -669,10 +674,14 @@ def profile(host, port, length, profile_dir): ) @with_appcontext def migrate_abs_to_assiduites( - dept: str = None, morning: str = None, noon: str = None, evening: str = None + dept: str = None, + morning: str = None, + noon: str = None, + afternoon: str = None, + evening: str = None, ): # migrate-abs-to-assiduites """Permet de migrer les absences vers le nouveau module d'assiduités""" - tools.migrate_abs_to_assiduites(dept, morning, noon, evening) + tools.migrate_abs_to_assiduites(dept, morning, noon, afternoon, evening) # import cProfile # cProfile.runctx( # f"tools.migrate_abs_to_assiduites({dept})", diff --git a/tools/migrate_abs_to_assiduites.py b/tools/migrate_abs_to_assiduites.py index 0234a518c..68e2cab98 100644 --- a/tools/migrate_abs_to_assiduites.py +++ b/tools/migrate_abs_to_assiduites.py @@ -47,6 +47,7 @@ class _glob: MORNING: time = None NOON: time = None + AFTERNOON: time = None EVENING: time = None @@ -93,7 +94,7 @@ class _Merger: time_ = _glob.NOON if end else _glob.MORNING date_ = datetime.combine(couple[0], time_) else: - time_ = _glob.EVENING if end else _glob.NOON + time_ = _glob.EVENING if end else _glob.AFTERNOON date_ = datetime.combine(couple[0], time_) d = localize_datetime(date_) return d @@ -229,6 +230,7 @@ def migrate_abs_to_assiduites( dept: str = None, morning: str = None, noon: str = None, + afternoon: str = None, evening: str = None, debug: bool = False, ): @@ -266,6 +268,12 @@ def migrate_abs_to_assiduites( noon: list[str] = str(noon).split(":") _glob.NOON = time(int(noon[0]), int(noon[1])) + if afternoon is None: + afternoon = ScoDocSiteConfig.get("assi_lunch_time", time(13, 0)) + + afternoon: list[str] = str(afternoon).split(":") + _glob.AFTERNOON = time(int(afternoon[0]), int(afternoon[1])) + if evening is None: evening = ScoDocSiteConfig.get("assi_afternoon_time", time(18, 0)) From 262d68c844c8ca1aa472e6286374799c4e894598 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 22 Aug 2023 18:00:13 +0200 Subject: [PATCH 4/8] =?UTF-8?q?Fix:=20calcul=20moyenne=20g=C3=A9n=C3=A9ral?= =?UTF-8?q?e=20BUT=20si=20aucune=20UE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/comp/moy_sem.py | 4 ++++ app/scodoc/sco_utils.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/comp/moy_sem.py b/app/comp/moy_sem.py index a1fe0104a..15313550a 100644 --- a/app/comp/moy_sem.py +++ b/app/comp/moy_sem.py @@ -78,7 +78,11 @@ def compute_sem_moys_apc_using_ects( else: ects = ects_df.to_numpy() # ects est maintenant un array nb_etuds x nb_ues + moy_gen = (etud_moy_ue_df * ects).sum(axis=1) / ects.sum(axis=1) + except ZeroDivisionError: + # peut arriver si aucun module... on ignore + moy_gen = pd.Series(np.NaN, index=etud_moy_ue_df.index) except TypeError: if None in ects: formation = db.session.get(Formation, formation_id) diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index e22e0e87b..6bc8e8f5f 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -260,7 +260,7 @@ class AssiduitesMetrics: """Labels associés au métrique de l'assiduité""" SHORT: list[str] = ["1/2 J.", "J.", "H."] - LONG: list[str] = ["Demi-Journée", "Journée", "Heure"] + LONG: list[str] = ["Demi-journée", "Journée", "Heure"] TAG: list[str] = ["demi", "journee", "heure"] From e7b0966011af9f15f92e21615e7cac79114bdfd1 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 22 Aug 2023 23:42:32 +0200 Subject: [PATCH 5/8] Version bump --- sco_version.py | 2 +- tools/config.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sco_version.py b/sco_version.py index 30ba50589..037083020 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.9" +SCOVERSION = "9.6.10" SCONAME = "ScoDoc" diff --git a/tools/config.sh b/tools/config.sh index 2a6c1627f..25d953163 100644 --- a/tools/config.sh +++ b/tools/config.sh @@ -45,7 +45,7 @@ then PSQL=/usr/lib/postgresql/15/bin/psql #export POSTGRES_SERVICE="postgresql@11-main.service" else - die "unsupported Debian version" + die "unsupported Debian version (${debian_version}, expected 12)" fi export PSQL From 4061a5d102e0159fb7349087563cfc26e8efaa1c Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 23 Aug 2023 00:06:15 +0200 Subject: [PATCH 6/8] build_release: option to skip tests --- sco_version.py | 2 +- tools/build_release.sh | 32 ++++++++++++++++++++++++++------ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/sco_version.py b/sco_version.py index 037083020..2c6860862 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.10" +SCOVERSION = "9.6.11" SCONAME = "ScoDoc" diff --git a/tools/build_release.sh b/tools/build_release.sh index f7f89828a..f182dab4f 100755 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -4,6 +4,22 @@ # Prend la version dans le code source local et cherche une release gitea de même tag. # Lance ensuite les tests unitaires locaux. +SKIP_TESTS=0 +while getopts "s" opt; do + case "$opt" in + s) + SKIP_TESTS=1 + ;; + \?) + echo "Invalid option: -$OPTARG" >&2 + exit 1 + ;; + :) + echo "Option -$OPTARG requires an argument." >&2 + exit 1 + ;; + esac +done # Le répertoire de ce script: .../scodoc/tools SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" @@ -57,13 +73,17 @@ SCODOC_USER=scodoc [ -z "$FACTORY_DIR" ] && die "empty FACTORY_DIR" [ "$(id -nu)" != "$SCODOC_USER" ] && die "Erreur: le script $0 doit être lancé par l'utilisateur $SCODOC_USER" -# Tests unitaires lancés dans le répertoire de travail -echo "TESTS UNITAIRES" -(cd "$UNIT_TESTS_DIR"; pytest tests/unit) || terminate "Erreur dans tests unitaires" - -# Tests API -(cd "$UNIT_TESTS_DIR"; tools/test_api.sh) || terminate "Erreur dans tests unitaires API" +if [ "$SKIP_TESTS" = 1 ] +then + echo "SKIPPING UNIT TESTS !" +else + # Tests unitaires lancés dans le répertoire de travail + echo "TESTS UNITAIRES" + (cd "$UNIT_TESTS_DIR"; pytest tests/unit) || terminate "Erreur dans tests unitaires" + # Tests API + (cd "$UNIT_TESTS_DIR"; tools/test_api.sh) || terminate "Erreur dans tests unitaires API" +fi # Création répertoire du paquet, et de opt slash="$FACTORY_DIR"/"$DEST_DIR" From 78add09990caf33c38ffc1dfb62a1a9d53ffb82d Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 23 Aug 2023 01:42:03 +0200 Subject: [PATCH 7/8] flake8 config. Code cosmetic. --- .flake8 | 3 + app/email.py | 9 +- app/scodoc/sco_abs.py | 23 +++--- app/scodoc/sco_abs_views.py | 137 +++++++++++++++++++------------ app/scodoc/sco_assiduites.py | 11 +-- app/scodoc/sco_bulletins_json.py | 2 +- 6 files changed, 113 insertions(+), 72 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..b7f214681 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] + max-line-length = 88 + ignore = E203,W503 \ No newline at end of file diff --git a/app/email.py b/app/email.py index a75b2def3..a983ef2df 100644 --- a/app/email.py +++ b/app/email.py @@ -79,13 +79,15 @@ Adresses d'origine: to : {orig_to} cc : {orig_cc} bcc: {orig_bcc} ---- +--- \n\n""" + msg.body ) current_app.logger.info( - f"""email sent to{' (mode test)' if email_test_mode_address else ''}: {msg.recipients} + f"""email sent to{ + ' (mode test)' if email_test_mode_address else '' + }: {msg.recipients} from sender {msg.sender} """ ) @@ -98,7 +100,8 @@ def get_from_addr(dept_acronym: str = None): """L'adresse "from" à utiliser pour envoyer un mail Si le departement est spécifié, ou si l'attribut `g.scodoc_dept`existe, - prend le `email_from_addr` des préférences de ce département si ce champ est non vide. + prend le `email_from_addr` des préférences de ce département si ce champ + est non vide. Sinon, utilise le paramètre global `email_from_addr`. Sinon, la variable de config `SCODOC_MAIL_FROM`. """ diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index 1e56ca87e..8a04cac27 100755 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -318,7 +318,7 @@ def list_abs_in_range( Returns: List of absences """ - if matin != None: + if matin is not None: matin = _toboolean(matin) ismatin = " AND A.MATIN = %(matin)s " else: @@ -387,7 +387,7 @@ def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int: Returns: An integer. """ - if matin != None: + if matin is not None: matin = _toboolean(matin) ismatin = " AND A.MATIN = %(matin)s " else: @@ -482,7 +482,9 @@ def _get_abs_description(a, cursor=None): else: a["matin"] = False cursor.execute( - """select * from absences where etudid=%(etudid)s and jour=%(jour)s and matin=%(matin)s order by entry_date desc""", + """SELECT * FROM absences + WHERE etudid=%(etudid)s AND jour=%(jour)s AND matin=%(matin)s + ORDER BY entry_date desc""", a, ) A = cursor.dictfetchall() @@ -517,9 +519,9 @@ def list_abs_jour(date, am=True, pm=True, is_abs=True, is_just=None): req = """SELECT DISTINCT etudid, jour, matin FROM ABSENCES A WHERE A.jour = %(date)s """ - if is_abs != None: + if is_abs is not None: req += " AND A.estabs = %(is_abs)s" - if is_just != None: + if is_just is not None: req += " AND A.estjust = %(is_just)s" if not am: req += " AND NOT matin " @@ -883,7 +885,7 @@ def MonthTableBody( descr = ev[4] # cc = [] - if color != None: + if color is not None: cc.append('' % color) else: cc.append('') @@ -896,7 +898,7 @@ def MonthTableBody( cc.append("" % (href, descr)) if legend or d == 1: - if pad_width != None: + if pad_width is not None: n = pad_width - len(legend) # pad to 8 cars if n > 0: legend = ( @@ -959,7 +961,7 @@ def MonthTableBody( ev_year = int(ev[0][:4]) ev_month = int(ev[0][5:7]) ev_day = int(ev[0][8:10]) - if ev[4] != None: + if ev[4] is not None: ev_half = int(ev[4]) else: ev_half = 0 @@ -978,7 +980,7 @@ def MonthTableBody( if len(ev) > 5 and ev[5]: descr = ev[5] # - if color != None: + if color is not None: cc.append('' % (color)) else: cc.append('') @@ -1072,7 +1074,8 @@ def invalidate_abs_count_sem(sem): def invalidate_abs_etud_date(etudid, date): # was invalidateAbsEtudDate - """Doit etre appelé à chaque modification des absences pour cet étudiant et cette date. + """Doit etre appelé à chaque modification des absences + pour cet étudiant et cette date. Invalide cache absence et caches semestre date: date au format ISO """ diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index f3473bf5d..4af828c39 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -137,14 +137,14 @@ def doSignaleAbsence( ] if dates: H.append( - f"""

Ajout de {nbadded} absences {just_str}justifiées + f"""

Ajout de {nbadded} absences {just_str}justifiées du {datedebut} au {datefin} {indication_module}

""" ) else: H.append( - f"""

Aucune date ouvrable + f"""

Aucune date ouvrable entre le {datedebut} et le {datefin} !

""" @@ -152,11 +152,11 @@ def doSignaleAbsence( H.append( f"""
    -
  • Autre absence pour {etud.nomprenom}
  • -
  • Calendrier de ses absences
  • @@ -180,8 +180,12 @@ def SignaleAbsenceEtud(): # etudid implied "abs_require_module" ) # on utilise la pref globale car pas de sem courant if require_module: - menu_module = """
    Pas inscrit dans un semestre courant, - et l'indication du module est requise. Donc pas de saisie d'absence possible !
    """ + menu_module = """
    Pas + inscrit dans un semestre courant, + et l'indication du module est requise. + Donc pas de saisie d'absence possible ! +
    + """ disabled = True else: menu_module = "" @@ -197,17 +201,17 @@ def SignaleAbsenceEtud(): # etudid implied menu_module = """ -

    Module: +

    Module: j/m/a + + + j/m/a +    Date fin (optionnelle): j/m/a @@ -269,14 +276,14 @@ Raison: (optionnel)

    - +

    Seuls les modules du semestre en cours apparaissent.

    Évitez de saisir une absence pour un module qui n'est pas en place à cette date.

    Toutes les dates sont au format jour/mois/annee.

    - + """ % { "etudid": etud["etudid"], @@ -354,7 +361,10 @@ def doJustifAbsence( ) H.append( - """
    • Autre justification pour %(nomprenom)s
    • + """
        +
      • Autre justification + pour %(nomprenom)s +
      • Signaler une absence
      • Calendrier de ses absences
      • Liste de ses absences
      • @@ -389,12 +399,12 @@ def JustifAbsenceEtud(): # etudid implied ), """""", """ -
        +

        - + @@ -412,7 +422,7 @@ def JustifAbsenceEtud(): # etudid implied Raison: (optionnel)

        - + """ % etud, @@ -458,8 +468,10 @@ def doAnnuleAbsence(datedebut, datefin, demijournee, etudid=False): # etudid im H.append( """


        """ % etud @@ -480,10 +492,11 @@ def AnnuleAbsenceEtud(): # etudid implied page_title="Annulation d'une absence pour %(nomprenom)s" % etud, ), """
        Date début : Date début :
        -

        Annulation d'une absence pour %(nomprenom)s

        +

        Annulation d'une absence + pour %(nomprenom)s

        """ - % etud, # " + % etud, """""" % url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid), sco_photos.etud_photo_html( @@ -491,16 +504,19 @@ def AnnuleAbsenceEtud(): # etudid implied title="fiche de " + etud["nomprenom"], ), """
        """, - """

        A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent.

        -

        Si plusieurs modules sont affectés, les absences seront toutes effacées.

        - """ + """

        A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que + l'étudiant était en fait présent. +

        +

        Si plusieurs modules sont affectés, + les absences seront toutes effacées.

        + """ % etud, """
        -
        +

        - + @@ -511,22 +527,22 @@ def AnnuleAbsenceEtud(): # etudid implied
        Date début : Date début : j/m/a
        -journée(s) +journée(s)  Matin(s)  Après midi

        - -

        + +
        -
        +

        - + @@ -538,15 +554,16 @@ def AnnuleAbsenceEtud(): # etudid implied
        Date début : Date début : j/m/a

        -journée(s) +journée(s)  Matin(s)  Après midi

        - -(utiliser ceci en cas de justificatif erroné saisi indépendemment d'une absence) -

        + +(utiliser ceci en cas de justificatif erroné saisi indépendemment +d'une absence) +
        """ % etud, html_sco_header.sco_footer(), @@ -591,8 +608,10 @@ def doAnnuleJustif(datedebut0, datefin0, demijournee): # etudid implied H.append( """
        """ % etud @@ -634,8 +653,11 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None): # supr les absences non justifiees for date in dates: cursor.execute( - """DELETE FROM absences - WHERE etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s + """DELETE FROM absences + WHERE etudid=%(etudid)s + AND (not estjust) + AND jour=%(date)s + AND moduleimpl_id=%(moduleimpl_id)s """, vars(), ) @@ -643,8 +665,11 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None): # s'assure que les justificatifs ne sont pas "absents" for date in dates: cursor.execute( - """UPDATE absences SET estabs=FALSE - WHERE etudid=%(etudid)s AND jour=%(date)s AND moduleimpl_id=%(moduleimpl_id)s + """UPDATE absences + SET estabs=FALSE + WHERE etudid=%(etudid)s + AND jour=%(date)s + AND moduleimpl_id=%(moduleimpl_id)s """, vars(), ) @@ -724,7 +749,7 @@ def _convert_sco_year(year) -> int: year = int(year) if year > 1900 and year < 2999: return year - except: + except ValueError: raise ScoValueError("année scolaire invalide") @@ -771,7 +796,8 @@ def CalAbs(etudid, sco_year=None): """A : absence NON justifiée
        a : absence justifiée
        X : justification sans absence
        - %d absences sur l'année, dont %d justifiées (soit %d non justifiées)
        (%d justificatifs inutilisés) + %d absences sur l'année, dont %d justifiées (soit %d non justifiées) + (%d justificatifs inutilisés)

        """ % (nbabs, nbabsjust, nbabs - nbabsjust, len(justifs_noabs)), @@ -790,7 +816,8 @@ def CalAbs(etudid, sco_year=None): """
        """, """""" % etudid, """Année scolaire %s-%s""" % (annee_scolaire, annee_scolaire + 1), - """  Changer année: """, ] for y in range(annee_courante, min(annee_courante - 6, annee_scolaire - 6), -1): H.append("""