Bulletins: evals rat. et sessions 2. Fix #492.

This commit is contained in:
Emmanuel Viennet 2022-09-18 16:53:00 +02:00
parent 219a3c2811
commit acd1b36d72
10 changed files with 70 additions and 48 deletions

View File

@ -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,

View File

@ -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"""<para align=right>{e["note"]["value"]}</para>"""
),
"coef": e["coef"],
"coef": coef,
"_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": [
(

View File

@ -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)

View File

@ -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

View File

@ -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"] = '<a class="bull_link" href="%s">%s</a>' % (
e["target_html"],
e["name"],
)
val = e["notes"].get(etudid, {"value": "NP"})[
"value"
] # NA si etud demissionnaire
e[
"name_html"
] = f"""<a class="bull_link" href="{
e['target_html']}">{e['name']}</a>"""
val = e["notes"].get(etudid, {"value": "NP"})["value"]
# val est NP si etud demissionnaire
if val == "NP":
e["note_txt"] = "nd"
e["note_html"] = '<span class="note_nd">nd</span>'
@ -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"] = []

View File

@ -150,7 +150,7 @@ class ScoInvalidIdType(ScoValueError):
</p>
<p> Si le problème persiste, merci de contacter l'assistance
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>Message serveur: <tt>{msg}</tt></p>
"""

View File

@ -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

View File

@ -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,
),

View File

@ -399,7 +399,7 @@ class releveBUT extends HTMLElement {
<div>${module.titre} - ${evaluation.description || "Note"}</div>
<div>
${evaluation.note.value ?? "-"}
<em>Coef.&nbsp;${evaluation.coef}</em>
<em>Coef.&nbsp;${evaluation.coef ?? "*"}</em>
</div>
</div>
`;
@ -450,7 +450,7 @@ class releveBUT extends HTMLElement {
<div>${this.URL(evaluation.url, evaluation.description || "Évaluation")}</div>
<div>
${evaluation.note.value}
<em>Coef.&nbsp;${evaluation.coef}</em>
<em>Coef.&nbsp;${evaluation.coef ?? "*"}</em>
</div>
<div class=complement>
<div>Coef</div><div>${evaluation.coef}</div>

View File

@ -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(