From acd1b36d72d760980dacc23a215b1f1673842f5c Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet
Date: Sun, 18 Sep 2022 16:53:00 +0200
Subject: [PATCH] Bulletins: evals rat. et sessions 2. Fix #492.
---
app/but/bulletin_but.py | 13 +++++----
app/but/bulletin_but_pdf.py | 8 ++++--
app/but/jury_but_pv.py | 4 ++-
app/comp/moy_mod.py | 13 +++++----
app/scodoc/sco_bulletins.py | 40 ++++++++++++++++-----------
app/scodoc/sco_exceptions.py | 2 +-
app/scodoc/sco_formsemestre_status.py | 13 ++++++---
app/scodoc/sco_import_etuds.py | 17 ++++++------
app/static/js/releve-but.js | 4 +--
app/views/notes.py | 4 +--
10 files changed, 70 insertions(+), 48 deletions(-)
diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py
index 506f8754..af33fac9 100644
--- a/app/but/bulletin_but.py
+++ b/app/but/bulletin_but.py
@@ -13,7 +13,7 @@ import numpy as np
from flask import g, has_request_context, url_for
from app.comp.res_but import ResultatsSemestreBUT
-from app.models import FormSemestre, Identite
+from app.models import Evaluation, FormSemestre, Identite
from app.models.groups import GroupDescr
from app.models.ues import UniteEns
from app.scodoc import sco_bulletins, sco_utils as scu
@@ -249,7 +249,7 @@ class BulletinBUT:
}
return d
- def etud_eval_results(self, etud, e) -> dict:
+ def etud_eval_results(self, etud, e: Evaluation) -> dict:
"dict resultats d'un étudiant à une évaluation"
# eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
eval_notes = self.res.modimpls_results[e.moduleimpl_id].evals_notes[e.id]
@@ -265,12 +265,14 @@ class BulletinBUT:
poids = collections.defaultdict(lambda: 0.0)
d = {
"id": e.id,
- "description": e.description,
+ "coef": fmt_note(e.coefficient)
+ if e.evaluation_type == scu.EVALUATION_NORMALE
+ else None,
"date": e.jour.isoformat() if e.jour else None,
+ "description": e.description,
+ "evaluation_type": e.evaluation_type,
"heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
- "coef": fmt_note(e.coefficient),
- "poids": poids,
"note": {
"value": fmt_note(
eval_notes[etud.id],
@@ -280,6 +282,7 @@ class BulletinBUT:
"max": fmt_note(notes_ok.max()),
"moy": fmt_note(notes_ok.mean()),
},
+ "poids": poids,
"url": url_for(
"notes.evaluation_listenotes",
scodoc_dept=g.scodoc_dept,
diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py
index e3e40c51..3954c818 100644
--- a/app/but/bulletin_but_pdf.py
+++ b/app/but/bulletin_but_pdf.py
@@ -13,6 +13,7 @@ from reportlab.platypus import Paragraph, Spacer
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
from app.scodoc import gen_tables
from app.scodoc.sco_codes_parcours import UE_SPORT
+from app.scodoc import sco_utils as scu
class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
@@ -312,18 +313,19 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
]
return col_keys, rows, pdf_style, col_widths
- def evaluations_rows(self, rows, evaluations, ue_acros=()):
+ def evaluations_rows(self, rows, evaluations: list[dict], ue_acros=()):
"lignes des évaluations"
for e in evaluations:
+ coef = e["coef"] if e["evaluation_type"] == scu.EVALUATION_NORMALE else "*"
t = {
"titre": f"{e['description'] or ''}",
"moyenne": e["note"]["value"],
"_moyenne_pdf": Paragraph(
f"""{e["note"]["value"]}"""
),
- "coef": e["coef"],
+ "coef": coef,
"_coef_pdf": Paragraph(
- f"{e['coef']}"
+ f"{coef}"
),
"_pdf_style": [
(
diff --git a/app/but/jury_but_pv.py b/app/but/jury_but_pv.py
index ca7f33b8..78d8d4de 100644
--- a/app/but/jury_but_pv.py
+++ b/app/but/jury_but_pv.py
@@ -90,7 +90,9 @@ def pvjury_table_but(formsemestre_id: int, format="html"):
if deca
else "-",
"decision_but": deca.code_valide if deca else "",
- "devenir": ", ".join([f"S{i}" for i in deca.get_autorisations_passage()]),
+ "devenir": ", ".join([f"S{i}" for i in deca.get_autorisations_passage()])
+ if deca
+ else "",
}
rows.append(row)
diff --git a/app/comp/moy_mod.py b/app/comp/moy_mod.py
index 673e356c..425f2b4e 100644
--- a/app/comp/moy_mod.py
+++ b/app/comp/moy_mod.py
@@ -124,7 +124,7 @@ class ModuleImplResults:
Évaluation "complete" (prise en compte dans les calculs) si:
- soit tous les étudiants inscrits au module ont des notes
- soit elle a été déclarée "à prise en compte immédiate" (publish_incomplete)
-
+ ou est une évaluation de rattrapage ou de session 2
Évaluation "attente" (prise en compte dans les calculs, mais il y
manque des notes) ssi il y a des étudiants inscrits au semestre et au module
qui ont des notes ATT.
@@ -148,11 +148,14 @@ class ModuleImplResults:
eval_df = self._load_evaluation_notes(evaluation)
# is_complete ssi tous les inscrits (non dem) au semestre ont une note
# ou évaluation déclarée "à prise en compte immédiate"
- # Les évaluations de rattrapage et 2eme session sont toujours incomplètes
- # car on calcule leur moyenne à part.
+ # Les évaluations de rattrapage et 2eme session sont toujours complètes
+
etudids_sans_note = inscrits_module - set(eval_df.index) # sans les dem.
- is_complete = (evaluation.evaluation_type == scu.EVALUATION_NORMALE) and (
- evaluation.publish_incomplete or (not etudids_sans_note)
+ is_complete = (
+ (evaluation.evaluation_type == scu.EVALUATION_RATTRAPAGE)
+ or (evaluation.evaluation_type == scu.EVALUATION_SESSION2)
+ or (evaluation.publish_incomplete)
+ or (not etudids_sans_note)
)
self.evaluations_completes.append(is_complete)
self.evaluations_completes_dict[evaluation.id] = is_complete
diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py
index ceb1a8c3..22734932 100644
--- a/app/scodoc/sco_bulletins.py
+++ b/app/scodoc/sco_bulletins.py
@@ -30,6 +30,7 @@
"""
import email
import time
+import numpy as np
from flask import g, request
from flask import flash, jsonify, render_template, url_for
@@ -575,18 +576,20 @@ def _ue_mod_bulletin(
else:
e["name"] = "Points de bonus sur cette UE"
else:
- e["name"] = e["description"] or "le %s" % e["jour"]
- e["target_html"] = (
- "evaluation_listenotes?evaluation_id=%s&format=html&tf_submitted=1"
- % e["evaluation_id"]
+ e["name"] = e["description"] or f"le {e['jour']}"
+ e["target_html"] = url_for(
+ "notes.evaluation_listenotes",
+ scodoc_dept=g.scodoc_dept,
+ evaluation_id=e["evaluation_id"],
+ format="html",
+ tf_submitted=1,
)
- e["name_html"] = '%s' % (
- e["target_html"],
- e["name"],
- )
- val = e["notes"].get(etudid, {"value": "NP"})[
- "value"
- ] # NA si etud demissionnaire
+ e[
+ "name_html"
+ ] = f"""{e['name']}"""
+ val = e["notes"].get(etudid, {"value": "NP"})["value"]
+ # val est NP si etud demissionnaire
if val == "NP":
e["note_txt"] = "nd"
e["note_html"] = 'nd'
@@ -604,13 +607,18 @@ def _ue_mod_bulletin(
if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
e["coef_txt"] = "rat."
elif e["evaluation_type"] == scu.EVALUATION_SESSION2:
- e["coef_txt"] = "sess. 2"
+ e["coef_txt"] = "Ses. 2"
if e["etat"]["evalattente"]:
mod_attente = True # une eval en attente dans ce module
- if (not is_malus) or (val != "NP"):
- mod["evaluations"].append(
- e
- ) # ne liste pas les eval malus sans notes
+ if ((not is_malus) or (val != "NP")) and (
+ (
+ e["evaluation_type"] == scu.EVALUATION_NORMALE
+ or not np.isnan(val)
+ )
+ ):
+ # ne liste pas les eval malus sans notes
+ # ni les rattrapages et sessions 2 si pas de note
+ mod["evaluations"].append(e)
# Evaluations incomplètes ou futures:
mod["evaluations_incompletes"] = []
diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py
index 967b26f7..77e60c91 100644
--- a/app/scodoc/sco_exceptions.py
+++ b/app/scodoc/sco_exceptions.py
@@ -150,7 +150,7 @@ class ScoInvalidIdType(ScoValueError):
Si le problème persiste, merci de contacter l'assistance
via la liste de diffusion Notes
- ou le salon Discord.
+ ou de préférence le salon Discord.
Message serveur: {msg}
"""
diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py
index cfe24135..89748250 100644
--- a/app/scodoc/sco_formsemestre_status.py
+++ b/app/scodoc/sco_formsemestre_status.py
@@ -40,12 +40,14 @@ from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import Module
from app.models.formsemestre import FormSemestre
-from app.models.moduleimpls import ModuleImpl
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
-import app.scodoc.notesdb as ndb
from app.scodoc.sco_permissions import Permission
-from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidDateError
+from app.scodoc.sco_exceptions import (
+ ScoValueError,
+ ScoInvalidDateError,
+ ScoInvalidIdType,
+)
from app.scodoc import html_sco_header
from app.scodoc import htmlutils
from app.scodoc import sco_abs
@@ -58,7 +60,6 @@ from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
-from app.scodoc import sco_formsemestre_edit
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl
@@ -1008,6 +1009,10 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
def formsemestre_status(formsemestre_id=None):
"""Tableau de bord semestre HTML"""
# porté du DTML
+ if formsemestre_id is not None and not isinstance(formsemestre_id, int):
+ raise ScoInvalidIdType(
+ "formsemestre_bulletinetud: formsemestre_id must be an integer !"
+ )
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
formsemestre_id=formsemestre_id
diff --git a/app/scodoc/sco_import_etuds.py b/app/scodoc/sco_import_etuds.py
index 629dec35..53b4563d 100644
--- a/app/scodoc/sco_import_etuds.py
+++ b/app/scodoc/sco_import_etuds.py
@@ -740,7 +740,8 @@ def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None
_ADM_PATTERN = re.compile(r"[\W]+", re.UNICODE) # supprime tout sauf alphanum
-def adm_normalize_string(s): # normalize unicode title
+def adm_normalize_string(s):
+ "normalize unicode title"
return scu.suppress_accents(_ADM_PATTERN.sub("", s.strip().lower())).replace(
"_", ""
)
@@ -750,16 +751,15 @@ def adm_get_fields(titles, formsemestre_id):
"""Cherche les colonnes importables dans les titres (ligne 1) du fichier excel
return: { idx : (field_name, convertor) }
"""
- # log('adm_get_fields: titles=%s' % titles)
- Fmt = sco_import_format_dict()
+ format_dict = sco_import_format_dict()
fields = {}
idx = 0
for title in titles:
title_n = adm_normalize_string(title)
- for k in Fmt:
- for v in Fmt[k]["aliases"]:
+ for k in format_dict:
+ for v in format_dict[k]["aliases"]:
if adm_normalize_string(v) == title_n:
- typ = Fmt[k]["type"]
+ typ = format_dict[k]["type"]
if typ == "real":
convertor = adm_convert_real
elif typ == "integer" or typ == "int":
@@ -769,10 +769,9 @@ def adm_get_fields(titles, formsemestre_id):
# doublons ?
if k in [x[0] for x in fields.values()]:
raise ScoFormatError(
- 'scolars_import_admission: titre "%s" en double (ligne 1)'
- % (title),
+ f"""scolars_import_admission: titre "{title}" en double (ligne 1)""",
dest_url=url_for(
- "scolar.form_students_import_infos_admissions_apb",
+ "scolar.form_students_import_infos_admissions",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
),
diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js
index a84e0dac..3b421338 100644
--- a/app/static/js/releve-but.js
+++ b/app/static/js/releve-but.js
@@ -399,7 +399,7 @@ class releveBUT extends HTMLElement {
${module.titre} - ${evaluation.description || "Note"}
${evaluation.note.value ?? "-"}
- Coef. ${evaluation.coef}
+ Coef. ${evaluation.coef ?? "*"}
`;
@@ -450,7 +450,7 @@ class releveBUT extends HTMLElement {
${this.URL(evaluation.url, evaluation.description || "Évaluation")}
${evaluation.note.value}
- Coef. ${evaluation.coef}
+ Coef. ${evaluation.coef ?? "*"}
Coef
${evaluation.coef}
diff --git a/app/views/notes.py b/app/views/notes.py
index 82348c3b..79b1dde8 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -265,7 +265,7 @@ def formsemestre_bulletinetud(
):
format = format or "html"
- if not isinstance(formsemestre_id, int):
+ if formsemestre_id is not None and not isinstance(formsemestre_id, int):
raise ScoInvalidIdType(
"formsemestre_bulletinetud: formsemestre_id must be an integer !"
)
@@ -1775,7 +1775,7 @@ def evaluation_listenotes():
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
moduleimpl_id = int(vals["moduleimpl_id"])
except ValueError as exc:
- raise ScoValueError("adresse invalide !") from exc
+ raise ScoValueError("evaluation_listenotes: id invalides !") from exc
format = vals.get("format", "html")
html_content, page_title = sco_liste_notes.do_evaluation_listenotes(