Bulletins: evals rat. et sessions 2. Fix #492.
This commit is contained in:
parent
219a3c2811
commit
acd1b36d72
@ -13,7 +13,7 @@ import numpy as np
|
|||||||
from flask import g, has_request_context, url_for
|
from flask import g, has_request_context, url_for
|
||||||
|
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
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.groups import GroupDescr
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
from app.scodoc import sco_bulletins, sco_utils as scu
|
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||||
@ -249,7 +249,7 @@ class BulletinBUT:
|
|||||||
}
|
}
|
||||||
return d
|
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"
|
"dict resultats d'un étudiant à une évaluation"
|
||||||
# eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
|
# 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]
|
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)
|
poids = collections.defaultdict(lambda: 0.0)
|
||||||
d = {
|
d = {
|
||||||
"id": e.id,
|
"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,
|
"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_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,
|
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
|
||||||
"coef": fmt_note(e.coefficient),
|
|
||||||
"poids": poids,
|
|
||||||
"note": {
|
"note": {
|
||||||
"value": fmt_note(
|
"value": fmt_note(
|
||||||
eval_notes[etud.id],
|
eval_notes[etud.id],
|
||||||
@ -280,6 +282,7 @@ class BulletinBUT:
|
|||||||
"max": fmt_note(notes_ok.max()),
|
"max": fmt_note(notes_ok.max()),
|
||||||
"moy": fmt_note(notes_ok.mean()),
|
"moy": fmt_note(notes_ok.mean()),
|
||||||
},
|
},
|
||||||
|
"poids": poids,
|
||||||
"url": url_for(
|
"url": url_for(
|
||||||
"notes.evaluation_listenotes",
|
"notes.evaluation_listenotes",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
@ -13,6 +13,7 @@ from reportlab.platypus import Paragraph, Spacer
|
|||||||
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
||||||
from app.scodoc import gen_tables
|
from app.scodoc import gen_tables
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
||||||
@ -312,18 +313,19 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
|||||||
]
|
]
|
||||||
return col_keys, rows, pdf_style, col_widths
|
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"
|
"lignes des évaluations"
|
||||||
for e in evaluations:
|
for e in evaluations:
|
||||||
|
coef = e["coef"] if e["evaluation_type"] == scu.EVALUATION_NORMALE else "*"
|
||||||
t = {
|
t = {
|
||||||
"titre": f"{e['description'] or ''}",
|
"titre": f"{e['description'] or ''}",
|
||||||
"moyenne": e["note"]["value"],
|
"moyenne": e["note"]["value"],
|
||||||
"_moyenne_pdf": Paragraph(
|
"_moyenne_pdf": Paragraph(
|
||||||
f"""<para align=right>{e["note"]["value"]}</para>"""
|
f"""<para align=right>{e["note"]["value"]}</para>"""
|
||||||
),
|
),
|
||||||
"coef": e["coef"],
|
"coef": coef,
|
||||||
"_coef_pdf": Paragraph(
|
"_coef_pdf": Paragraph(
|
||||||
f"<para align=right fontSize={self.small_fontsize}><i>{e['coef']}</i></para>"
|
f"<para align=right fontSize={self.small_fontsize}><i>{coef}</i></para>"
|
||||||
),
|
),
|
||||||
"_pdf_style": [
|
"_pdf_style": [
|
||||||
(
|
(
|
||||||
|
@ -90,7 +90,9 @@ def pvjury_table_but(formsemestre_id: int, format="html"):
|
|||||||
if deca
|
if deca
|
||||||
else "-",
|
else "-",
|
||||||
"decision_but": deca.code_valide 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)
|
rows.append(row)
|
||||||
|
@ -124,7 +124,7 @@ class ModuleImplResults:
|
|||||||
Évaluation "complete" (prise en compte dans les calculs) si:
|
Évaluation "complete" (prise en compte dans les calculs) si:
|
||||||
- soit tous les étudiants inscrits au module ont des notes
|
- soit tous les étudiants inscrits au module ont des notes
|
||||||
- soit elle a été déclarée "à prise en compte immédiate" (publish_incomplete)
|
- 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
|
É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
|
manque des notes) ssi il y a des étudiants inscrits au semestre et au module
|
||||||
qui ont des notes ATT.
|
qui ont des notes ATT.
|
||||||
@ -148,11 +148,14 @@ class ModuleImplResults:
|
|||||||
eval_df = self._load_evaluation_notes(evaluation)
|
eval_df = self._load_evaluation_notes(evaluation)
|
||||||
# is_complete ssi tous les inscrits (non dem) au semestre ont une note
|
# is_complete ssi tous les inscrits (non dem) au semestre ont une note
|
||||||
# ou évaluation déclarée "à prise en compte immédiate"
|
# ou évaluation déclarée "à prise en compte immédiate"
|
||||||
# Les évaluations de rattrapage et 2eme session sont toujours incomplètes
|
# Les évaluations de rattrapage et 2eme session sont toujours complètes
|
||||||
# car on calcule leur moyenne à part.
|
|
||||||
etudids_sans_note = inscrits_module - set(eval_df.index) # sans les dem.
|
etudids_sans_note = inscrits_module - set(eval_df.index) # sans les dem.
|
||||||
is_complete = (evaluation.evaluation_type == scu.EVALUATION_NORMALE) and (
|
is_complete = (
|
||||||
evaluation.publish_incomplete or (not etudids_sans_note)
|
(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.append(is_complete)
|
||||||
self.evaluations_completes_dict[evaluation.id] = is_complete
|
self.evaluations_completes_dict[evaluation.id] = is_complete
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
"""
|
"""
|
||||||
import email
|
import email
|
||||||
import time
|
import time
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import flash, jsonify, render_template, url_for
|
from flask import flash, jsonify, render_template, url_for
|
||||||
@ -575,18 +576,20 @@ def _ue_mod_bulletin(
|
|||||||
else:
|
else:
|
||||||
e["name"] = "Points de bonus sur cette UE"
|
e["name"] = "Points de bonus sur cette UE"
|
||||||
else:
|
else:
|
||||||
e["name"] = e["description"] or "le %s" % e["jour"]
|
e["name"] = e["description"] or f"le {e['jour']}"
|
||||||
e["target_html"] = (
|
e["target_html"] = url_for(
|
||||||
"evaluation_listenotes?evaluation_id=%s&format=html&tf_submitted=1"
|
"notes.evaluation_listenotes",
|
||||||
% e["evaluation_id"]
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
evaluation_id=e["evaluation_id"],
|
||||||
|
format="html",
|
||||||
|
tf_submitted=1,
|
||||||
)
|
)
|
||||||
e["name_html"] = '<a class="bull_link" href="%s">%s</a>' % (
|
e[
|
||||||
e["target_html"],
|
"name_html"
|
||||||
e["name"],
|
] = f"""<a class="bull_link" href="{
|
||||||
)
|
e['target_html']}">{e['name']}</a>"""
|
||||||
val = e["notes"].get(etudid, {"value": "NP"})[
|
val = e["notes"].get(etudid, {"value": "NP"})["value"]
|
||||||
"value"
|
# val est NP si etud demissionnaire
|
||||||
] # NA si etud demissionnaire
|
|
||||||
if val == "NP":
|
if val == "NP":
|
||||||
e["note_txt"] = "nd"
|
e["note_txt"] = "nd"
|
||||||
e["note_html"] = '<span class="note_nd">nd</span>'
|
e["note_html"] = '<span class="note_nd">nd</span>'
|
||||||
@ -604,13 +607,18 @@ def _ue_mod_bulletin(
|
|||||||
if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
|
if e["evaluation_type"] == scu.EVALUATION_RATTRAPAGE:
|
||||||
e["coef_txt"] = "rat."
|
e["coef_txt"] = "rat."
|
||||||
elif e["evaluation_type"] == scu.EVALUATION_SESSION2:
|
elif e["evaluation_type"] == scu.EVALUATION_SESSION2:
|
||||||
e["coef_txt"] = "sess. 2"
|
e["coef_txt"] = "Ses. 2"
|
||||||
if e["etat"]["evalattente"]:
|
if e["etat"]["evalattente"]:
|
||||||
mod_attente = True # une eval en attente dans ce module
|
mod_attente = True # une eval en attente dans ce module
|
||||||
if (not is_malus) or (val != "NP"):
|
if ((not is_malus) or (val != "NP")) and (
|
||||||
mod["evaluations"].append(
|
(
|
||||||
e
|
e["evaluation_type"] == scu.EVALUATION_NORMALE
|
||||||
) # ne liste pas les eval malus sans notes
|
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:
|
# Evaluations incomplètes ou futures:
|
||||||
mod["evaluations_incompletes"] = []
|
mod["evaluations_incompletes"] = []
|
||||||
|
@ -150,7 +150,7 @@ class ScoInvalidIdType(ScoValueError):
|
|||||||
</p>
|
</p>
|
||||||
<p> Si le problème persiste, merci de contacter l'assistance
|
<p> Si le problème persiste, merci de contacter l'assistance
|
||||||
via la liste de diffusion <a href="{scu.SCO_USERS_LIST}">Notes</a>
|
via la liste de diffusion <a href="{scu.SCO_USERS_LIST}">Notes</a>
|
||||||
ou le salon Discord.
|
ou de préférence le <a href="{scu.SCO_DISCORD_ASSISTANCE}">salon Discord</a>.
|
||||||
</p>
|
</p>
|
||||||
<p>Message serveur: <tt>{msg}</tt></p>
|
<p>Message serveur: <tt>{msg}</tt></p>
|
||||||
"""
|
"""
|
||||||
|
@ -40,12 +40,14 @@ from app.comp import res_sem
|
|||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import Module
|
from app.models import Module
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
from app.models.moduleimpls import ModuleImpl
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.scodoc.sco_utils import ModuleType
|
from app.scodoc.sco_utils import ModuleType
|
||||||
import app.scodoc.notesdb as ndb
|
|
||||||
from app.scodoc.sco_permissions import Permission
|
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 html_sco_header
|
||||||
from app.scodoc import htmlutils
|
from app.scodoc import htmlutils
|
||||||
from app.scodoc import sco_abs
|
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_evaluation_db
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
from app.scodoc import sco_formsemestre
|
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_formsemestre_inscriptions
|
||||||
from app.scodoc import sco_groups
|
from app.scodoc import sco_groups
|
||||||
from app.scodoc import sco_moduleimpl
|
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):
|
def formsemestre_status(formsemestre_id=None):
|
||||||
"""Tableau de bord semestre HTML"""
|
"""Tableau de bord semestre HTML"""
|
||||||
# porté du DTML
|
# 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)
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
modimpls = sco_moduleimpl.moduleimpl_withmodule_list(
|
||||||
formsemestre_id=formsemestre_id
|
formsemestre_id=formsemestre_id
|
||||||
|
@ -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
|
_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(
|
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
|
"""Cherche les colonnes importables dans les titres (ligne 1) du fichier excel
|
||||||
return: { idx : (field_name, convertor) }
|
return: { idx : (field_name, convertor) }
|
||||||
"""
|
"""
|
||||||
# log('adm_get_fields: titles=%s' % titles)
|
format_dict = sco_import_format_dict()
|
||||||
Fmt = sco_import_format_dict()
|
|
||||||
fields = {}
|
fields = {}
|
||||||
idx = 0
|
idx = 0
|
||||||
for title in titles:
|
for title in titles:
|
||||||
title_n = adm_normalize_string(title)
|
title_n = adm_normalize_string(title)
|
||||||
for k in Fmt:
|
for k in format_dict:
|
||||||
for v in Fmt[k]["aliases"]:
|
for v in format_dict[k]["aliases"]:
|
||||||
if adm_normalize_string(v) == title_n:
|
if adm_normalize_string(v) == title_n:
|
||||||
typ = Fmt[k]["type"]
|
typ = format_dict[k]["type"]
|
||||||
if typ == "real":
|
if typ == "real":
|
||||||
convertor = adm_convert_real
|
convertor = adm_convert_real
|
||||||
elif typ == "integer" or typ == "int":
|
elif typ == "integer" or typ == "int":
|
||||||
@ -769,10 +769,9 @@ def adm_get_fields(titles, formsemestre_id):
|
|||||||
# doublons ?
|
# doublons ?
|
||||||
if k in [x[0] for x in fields.values()]:
|
if k in [x[0] for x in fields.values()]:
|
||||||
raise ScoFormatError(
|
raise ScoFormatError(
|
||||||
'scolars_import_admission: titre "%s" en double (ligne 1)'
|
f"""scolars_import_admission: titre "{title}" en double (ligne 1)""",
|
||||||
% (title),
|
|
||||||
dest_url=url_for(
|
dest_url=url_for(
|
||||||
"scolar.form_students_import_infos_admissions_apb",
|
"scolar.form_students_import_infos_admissions",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
),
|
),
|
||||||
|
@ -399,7 +399,7 @@ class releveBUT extends HTMLElement {
|
|||||||
<div>${module.titre} - ${evaluation.description || "Note"}</div>
|
<div>${module.titre} - ${evaluation.description || "Note"}</div>
|
||||||
<div>
|
<div>
|
||||||
${evaluation.note.value ?? "-"}
|
${evaluation.note.value ?? "-"}
|
||||||
<em>Coef. ${evaluation.coef}</em>
|
<em>Coef. ${evaluation.coef ?? "*"}</em>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -450,7 +450,7 @@ class releveBUT extends HTMLElement {
|
|||||||
<div>${this.URL(evaluation.url, evaluation.description || "Évaluation")}</div>
|
<div>${this.URL(evaluation.url, evaluation.description || "Évaluation")}</div>
|
||||||
<div>
|
<div>
|
||||||
${evaluation.note.value}
|
${evaluation.note.value}
|
||||||
<em>Coef. ${evaluation.coef}</em>
|
<em>Coef. ${evaluation.coef ?? "*"}</em>
|
||||||
</div>
|
</div>
|
||||||
<div class=complement>
|
<div class=complement>
|
||||||
<div>Coef</div><div>${evaluation.coef}</div>
|
<div>Coef</div><div>${evaluation.coef}</div>
|
||||||
|
@ -265,7 +265,7 @@ def formsemestre_bulletinetud(
|
|||||||
):
|
):
|
||||||
format = format or "html"
|
format = format or "html"
|
||||||
|
|
||||||
if not isinstance(formsemestre_id, int):
|
if formsemestre_id is not None and not isinstance(formsemestre_id, int):
|
||||||
raise ScoInvalidIdType(
|
raise ScoInvalidIdType(
|
||||||
"formsemestre_bulletinetud: formsemestre_id must be an integer !"
|
"formsemestre_bulletinetud: formsemestre_id must be an integer !"
|
||||||
)
|
)
|
||||||
@ -1775,7 +1775,7 @@ def evaluation_listenotes():
|
|||||||
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
|
if "moduleimpl_id" in vals and vals["moduleimpl_id"]:
|
||||||
moduleimpl_id = int(vals["moduleimpl_id"])
|
moduleimpl_id = int(vals["moduleimpl_id"])
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ScoValueError("adresse invalide !") from exc
|
raise ScoValueError("evaluation_listenotes: id invalides !") from exc
|
||||||
|
|
||||||
format = vals.get("format", "html")
|
format = vals.get("format", "html")
|
||||||
html_content, page_title = sco_liste_notes.do_evaluation_listenotes(
|
html_content, page_title = sco_liste_notes.do_evaluation_listenotes(
|
||||||
|
Loading…
Reference in New Issue
Block a user