Réglage précision des notes exportées dans Apogée

This commit is contained in:
Emmanuel Viennet 2022-04-05 11:44:08 +02:00
parent 6b49c8472d
commit bee87cf58b
6 changed files with 26 additions and 35 deletions

View File

@ -70,5 +70,16 @@ class CodesDecisionsForm(FlaskForm):
DEM = _build_code_field("DEM") DEM = _build_code_field("DEM")
NAR = _build_code_field("NAR") NAR = _build_code_field("NAR")
RAT = _build_code_field("RAT") RAT = _build_code_field("RAT")
NOTES_FMT = StringField(
label="Format notes exportées",
description="""Format des notes. Par défaut <tt style="font-family: monotype;">%3.2f</tt> (deux chiffres après la virgule)""",
validators=[
validators.Length(
max=SHORT_STR_LEN,
message=f"Le format ne doit pas dépasser {SHORT_STR_LEN} caractères",
),
validators.DataRequired("format requis"),
],
)
submit = SubmitField("Valider") submit = SubmitField("Valider")
cancel = SubmitField("Annuler", render_kw={"formnovalidate": True}) cancel = SubmitField("Annuler", render_kw={"formnovalidate": True})

View File

@ -36,6 +36,7 @@ CODES_SCODOC_TO_APO = {
DEM: "NAR", DEM: "NAR",
NAR: "NAR", NAR: "NAR",
RAT: "ATT", RAT: "ATT",
"NOTES_FMT": "%3.2f",
} }
@ -157,32 +158,6 @@ class ScoDocSiteConfig(db.Model):
class_list.sort(key=lambda x: x[1].replace(" du ", " de ")) class_list.sort(key=lambda x: x[1].replace(" du ", " de "))
return [("", "")] + class_list return [("", "")] + class_list
@classmethod
def get_bonus_sport_func(cls):
"""Fonction bonus_sport ScoDoc 7 XXX
Transitoire pour les tests durant la transition #sco92
"""
"""returns bonus func with specified name.
If name not specified, return the configured function.
None if no bonus function configured.
Raises ScoValueError if func_name not found in module bonus_sport.
"""
from app.scodoc import bonus_sport
c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first()
if c is None:
return None
func_name = c.value
if func_name == "": # pas de bonus défini
return None
try:
return getattr(bonus_sport, func_name)
except AttributeError:
raise ScoValueError(
f"""Fonction de calcul de l'UE bonus inexistante: "{func_name}".
(contacter votre administrateur local)."""
)
@classmethod @classmethod
def get_code_apo(cls, code: str) -> str: def get_code_apo(cls, code: str) -> str:
"""La représentation d'un code pour les exports Apogée. """La représentation d'un code pour les exports Apogée.

View File

@ -83,6 +83,7 @@ XXX A vérifier:
import collections import collections
import datetime import datetime
from functools import reduce from functools import reduce
import functools
import io import io
import os import os
import pprint import pprint
@ -125,7 +126,7 @@ APO_SEP = "\t"
APO_NEWLINE = "\r\n" APO_NEWLINE = "\r\n"
def _apo_fmt_note(note): def _apo_fmt_note(note, fmt="%3.2f"):
"Formatte une note pour Apogée (séparateur décimal: ',')" "Formatte une note pour Apogée (séparateur décimal: ',')"
# if not note and isinstance(note, float): changé le 31/1/2022, étrange ? # if not note and isinstance(note, float): changé le 31/1/2022, étrange ?
# return "" # return ""
@ -133,7 +134,7 @@ def _apo_fmt_note(note):
val = float(note) val = float(note)
except ValueError: except ValueError:
return "" return ""
return ("%3.2f" % val).replace(".", APO_DECIMAL_SEP) return (fmt % val).replace(".", APO_DECIMAL_SEP)
def guess_data_encoding(text, threshold=0.6): def guess_data_encoding(text, threshold=0.6):
@ -270,6 +271,9 @@ class ApoEtud(dict):
self.export_res_modules = export_res_modules self.export_res_modules = export_res_modules
self.export_res_sdj = export_res_sdj # export meme si pas de decision de jury self.export_res_sdj = export_res_sdj # export meme si pas de decision de jury
self.export_res_rat = export_res_rat self.export_res_rat = export_res_rat
self.fmt_note = functools.partial(
_apo_fmt_note, fmt=ScoDocSiteConfig.get_code_apo("NOTES_FMT") or "3.2f"
)
def __repr__(self): def __repr__(self):
return "ApoEtud( nom='%s', nip='%s' )" % (self["nom"], self["nip"]) return "ApoEtud( nom='%s', nip='%s' )" % (self["nom"], self["nip"])
@ -423,7 +427,7 @@ class ApoEtud(dict):
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"]) ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
code_decision_ue = decisions_ue[ue["ue_id"]]["code"] code_decision_ue = decisions_ue[ue["ue_id"]]["code"]
return dict( return dict(
N=_apo_fmt_note(ue_status["moy"] if ue_status else ""), N=self.fmt_note(ue_status["moy"] if ue_status else ""),
B=20, B=20,
J="", J="",
R=ScoDocSiteConfig.get_code_apo(code_decision_ue), R=ScoDocSiteConfig.get_code_apo(code_decision_ue),
@ -443,7 +447,7 @@ class ApoEtud(dict):
].split(","): ].split(","):
n = nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid) n = nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
if n != "NI" and self.export_res_modules: if n != "NI" and self.export_res_modules:
return dict(N=_apo_fmt_note(n), B=20, J="", R="") return dict(N=self.fmt_note(n), B=20, J="", R="")
else: else:
module_code_found = True module_code_found = True
if module_code_found: if module_code_found:
@ -465,7 +469,7 @@ class ApoEtud(dict):
if decision_apo == "DEF" or decision["code"] == DEM or decision["code"] == DEF: if decision_apo == "DEF" or decision["code"] == DEM or decision["code"] == DEF:
note_str = "0,01" # note non nulle pour les démissionnaires note_str = "0,01" # note non nulle pour les démissionnaires
else: else:
note_str = _apo_fmt_note(note) note_str = self.fmt_note(note)
return dict(N=note_str, B=20, J="", R=decision_apo, M="") return dict(N=note_str, B=20, J="", R=decision_apo, M="")
def comp_elt_annuel(self, etudid, cur_sem, autre_sem): def comp_elt_annuel(self, etudid, cur_sem, autre_sem):
@ -531,7 +535,7 @@ class ApoEtud(dict):
moy_annuelle = (note + autre_note) / 2 moy_annuelle = (note + autre_note) / 2
except TypeError: except TypeError:
moy_annuelle = "" moy_annuelle = ""
note_str = _apo_fmt_note(moy_annuelle) note_str = self.fmt_note(moy_annuelle)
if code_semestre_validant(autre_decision["code"]): if code_semestre_validant(autre_decision["code"]):
decision_apo_annuelle = decision_apo decision_apo_annuelle = decision_apo

View File

@ -191,7 +191,7 @@ def fmt_note(val, note_max=None, keep_numeric=False):
if isinstance(val, float) or isinstance(val, int): if isinstance(val, float) or isinstance(val, int):
if np.isnan(val): if np.isnan(val):
return "~" return "~"
if note_max != None and note_max > 0: if (note_max is not None) and note_max > 0:
val = val * 20.0 / note_max val = val * 20.0 / note_max
if keep_numeric: if keep_numeric:
return val return val

View File

@ -144,11 +144,12 @@ def config_codes_decisions():
if form.validate_on_submit(): if form.validate_on_submit():
for code in models.config.CODES_SCODOC_TO_APO: for code in models.config.CODES_SCODOC_TO_APO:
ScoDocSiteConfig.set_code_apo(code, getattr(form, code).data) ScoDocSiteConfig.set_code_apo(code, getattr(form, code).data)
flash(f"Codes décisions enregistrés.") flash("Codes décisions enregistrés.")
return redirect(url_for("scodoc.index")) return redirect(url_for("scodoc.index"))
elif request.method == "GET": elif request.method == "GET":
for code in models.config.CODES_SCODOC_TO_APO: for code in models.config.CODES_SCODOC_TO_APO:
getattr(form, code).data = ScoDocSiteConfig.get_code_apo(code) getattr(form, code).data = ScoDocSiteConfig.get_code_apo(code)
return render_template( return render_template(
"config_codes_decisions.html", "config_codes_decisions.html",
form=form, form=form,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.1.90" SCOVERSION = "9.1.91"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"