Compare commits

...

3 Commits

10 changed files with 128 additions and 100 deletions

View File

@ -52,7 +52,8 @@ def formations():
@as_json
def formations_ids():
"""
Retourne la liste de toutes les id de formations (tous départements)
Retourne la liste de toutes les id de formations
(tous départements, ou du département indiqué dans la route)
Exemple de résultat : [ 17, 99, 32 ]
"""

View File

@ -23,9 +23,12 @@ from app.models.but_refcomp import (
from app.scodoc.sco_exceptions import ScoFormatError, ScoValueError
def orebut_import_refcomp(xml_data: str, dept_id: int, orig_filename=None):
def orebut_import_refcomp(
xml_data: str, dept_id: int, orig_filename=None
) -> ApcReferentielCompetences:
"""Importation XML Orébut
peut lever TypeError ou ScoFormatError
L'objet créé est ajouté et commité.
Résultat: instance de ApcReferentielCompetences
"""
# Vérifie que le même fichier n'a pas déjà été chargé:
@ -41,7 +44,7 @@ def orebut_import_refcomp(xml_data: str, dept_id: int, orig_filename=None):
try:
root = ElementTree.XML(xml_data)
except ElementTree.ParseError as exc:
raise ScoFormatError(f"fichier XML Orébut invalide (2): {exc.args}")
raise ScoFormatError(f"fichier XML Orébut invalide (2): {exc.args}") from exc
if root.tag != "referentiel_competence":
raise ScoFormatError("élément racine 'referentiel_competence' manquant")
args = ApcReferentielCompetences.attr_from_xml(root.attrib)
@ -60,7 +63,8 @@ def orebut_import_refcomp(xml_data: str, dept_id: int, orig_filename=None):
# ne devrait plus se produire car pas d'unicité de l'id: donc inutile
db.session.rollback()
raise ScoValueError(
f"""Un référentiel a déjà été chargé avec les mêmes compétences ! ({competence.attrib["id"]})
f"""Un référentiel a déjà été chargé avec les mêmes compétences ! ({
competence.attrib["id"]})
"""
) from exc
ref.competences.append(c)

View File

@ -148,6 +148,7 @@ class ModuleImplResults:
evals_notes = pd.DataFrame(index=self.etudids, dtype=float)
self.evaluations_completes = []
self.evaluations_completes_dict = {}
self.etudids_attente = set() # empty
for evaluation in moduleimpl.evaluations:
eval_df = self._load_evaluation_notes(evaluation)
# is_complete ssi
@ -155,13 +156,13 @@ class ModuleImplResults:
# ou évaluation déclarée "à prise en compte immédiate"
# ou rattrapage, 2eme session, bonus
# ET pas bloquée par date (is_blocked)
is_blocked = evaluation.is_blocked()
etudids_sans_note = inscrits_module - set(eval_df.index) # sans les dem.
is_complete = (
(evaluation.evaluation_type != Evaluation.EVALUATION_NORMALE)
or (evaluation.publish_incomplete)
or (not etudids_sans_note)
) and not evaluation.is_blocked()
) and not is_blocked
self.evaluations_completes.append(is_complete)
self.evaluations_completes_dict[evaluation.id] = is_complete
self.evals_etudids_sans_note[evaluation.id] = etudids_sans_note
@ -178,16 +179,21 @@ class ModuleImplResults:
eval_notes_inscr = evals_notes[str(evaluation.id)][list(inscrits_module)]
# Nombre de notes (non vides, incluant ATT etc) des inscrits:
nb_notes = eval_notes_inscr.notna().sum()
# Etudiants avec notes en attente:
# = ceux avec note ATT
eval_etudids_attente = set(
eval_notes_inscr.iloc[
(eval_notes_inscr == scu.NOTES_ATTENTE).to_numpy()
].index
)
if evaluation.publish_incomplete:
# et en "immédiat", tous ceux sans note
eval_etudids_attente |= etudids_sans_note
if is_blocked:
eval_etudids_attente = set()
else:
# Etudiants avec notes en attente:
# = ceux avec note ATT
eval_etudids_attente = set(
eval_notes_inscr.iloc[
(eval_notes_inscr == scu.NOTES_ATTENTE).to_numpy()
].index
)
if evaluation.publish_incomplete:
# et en "immédiat", tous ceux sans note
eval_etudids_attente |= etudids_sans_note
# Synthèse pour état du module:
self.etudids_attente |= eval_etudids_attente
self.evaluations_etat[evaluation.id] = EvaluationEtat(

View File

@ -209,6 +209,7 @@ class ResultatsSemestre(ResultatsCache):
"evalcomplete" : bool,
"last_modif" : datetime.datetime | None, # saisie de note la plus récente
"nb_notes" : int, # nb notes d'étudiants inscrits
"nb_attente" : int, # nb de notes en ATTente (même si bloquée)
},
"evaluation_id" : int,
"jour" : datetime.datetime, # e.date_debut or datetime.datetime(1900, 1, 1)
@ -236,6 +237,7 @@ class ResultatsSemestre(ResultatsCache):
"etat": {
"blocked": evaluation.is_blocked(),
"evalcomplete": etat.is_complete,
"nb_attente": etat.nb_attente,
"nb_notes": etat.nb_notes,
"last_modif": last_modif,
},

View File

@ -873,7 +873,7 @@ class FormSemestre(db.Model):
descr_sem += " " + self.modalite
return descr_sem
def get_abs_count(self, etudid):
def get_abs_count(self, etudid) -> tuple[int, int, int]:
"""Les comptes d'absences de cet étudiant dans ce semestre:
tuple (nb abs non just, nb abs justifiées, nb abs total)
Utilise un cache.

View File

@ -30,17 +30,18 @@
(coût théorique en heures équivalent TD)
"""
from flask import request
from flask import request, Response
from app.models import FormSemestre
from app.scodoc.gen_tables import GenTable
from app.scodoc import sco_preferences
from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_exceptions import ScoValueError
import app.scodoc.sco_utils as scu
import sco_version
def formsemestre_table_estim_cost(
formsemestre_id,
formsemestre: FormSemestre,
n_group_td=1,
n_group_tp=1,
coef_tp=1,
@ -55,8 +56,6 @@ def formsemestre_table_estim_cost(
peut conduire à une sur-estimation du coût s'il y a des modules optionnels
(dans ce cas, retoucher le tableau excel exporté).
"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
rows = []
for modimpl in formsemestre.modimpls:
rows.append(
@ -76,14 +75,14 @@ def formsemestre_table_estim_cost(
+ coef_cours * row["heures_cours"]
+ coef_tp * row["heures_tp"]
)
sum_cours = sum([t["heures_cours"] for t in rows])
sum_td = sum([t["heures_td"] for t in rows])
sum_tp = sum([t["heures_tp"] for t in rows])
sum_cours = sum(t["heures_cours"] for t in rows)
sum_td = sum(t["heures_td"] for t in rows)
sum_tp = sum(t["heures_tp"] for t in rows)
sum_heqtd = sum_td + coef_cours * sum_cours + coef_tp * sum_tp
assert abs(sum([t["HeqTD"] for t in rows]) - sum_heqtd) < 0.01, "%s != %s" % (
sum([t["HeqTD"] for t in rows]),
sum_heqtd,
)
# assert abs(sum(t["HeqTD"] for t in rows) - sum_heqtd) < 0.01, "%s != %s" % (
# sum(t["HeqTD"] for t in rows),
# sum_heqtd,
# )
rows.append(
{
@ -117,7 +116,7 @@ def formsemestre_table_estim_cost(
),
rows=rows,
html_sortable=True,
preferences=sco_preferences.SemPreferences(formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre.id),
html_class="table_leftalign table_listegroupe",
xls_before_table=[
[formsemestre.titre_annee()],
@ -146,47 +145,45 @@ def formsemestre_table_estim_cost(
return tab
# view
def formsemestre_estim_cost(
formsemestre_id,
n_group_td=1,
n_group_tp=1,
coef_tp=1,
coef_cours=1.5,
formsemestre_id: int,
n_group_td: int | str = 1,
n_group_tp: int | str = 1,
coef_tp: float | str = 1.0,
coef_cours: float | str = 1.5,
fmt="html",
):
) -> str | Response:
"""Page (formulaire) estimation coûts"""
try:
n_group_td = int(n_group_td)
n_group_tp = int(n_group_tp)
coef_tp = float(coef_tp)
coef_cours = float(coef_cours)
except ValueError as exc:
raise ScoValueError("paramètre invalide: utiliser des nombres") from exc
n_group_td = int(n_group_td)
n_group_tp = int(n_group_tp)
coef_tp = float(coef_tp)
coef_cours = float(coef_cours)
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
tab = formsemestre_table_estim_cost(
formsemestre_id,
formsemestre,
n_group_td=n_group_td,
n_group_tp=n_group_tp,
coef_tp=coef_tp,
coef_cours=coef_cours,
)
h = """
<form name="f" method="get" action="%s">
<input type="hidden" name="formsemestre_id" value="%s"></input>
Nombre de groupes de TD: <input type="text" name="n_group_td" value="%s" onchange="document.f.submit()"/><br>
Nombre de groupes de TP: <input type="text" name="n_group_tp" value="%s" onchange="document.f.submit()"/>
&nbsp;Coefficient heures TP: <input type="text" name="coef_tp" value="%s" onchange="document.f.submit()"/>
tab.html_before_table = f"""
<form name="f" method="get" action="{request.base_url}">
<input type="hidden" name="formsemestre_id" value="{formsemestre.id}"></input>
Nombre de groupes de TD: <input type="text" name="n_group_td" value="{n_group_td}" onchange="document.f.submit()"/><br>
Nombre de groupes de TP: <input type="text" name="n_group_tp" value="{n_group_tp}" onchange="document.f.submit()"/>
&nbsp;Coefficient heures TP: <input type="text" name="coef_tp" value="{coef_tp}" onchange="document.f.submit()"/>
<br>
</form>
""" % (
request.base_url,
formsemestre_id,
n_group_td,
n_group_tp,
coef_tp,
)
tab.html_before_table = h
"""
tab.base_url = "%s?formsemestre_id=%s&n_group_td=%s&n_group_tp=%s&coef_tp=%s" % (
request.base_url,
formsemestre_id,
formsemestre.id,
n_group_td,
n_group_tp,
coef_tp,

View File

@ -279,11 +279,18 @@ def _summarize_evals_etats(etat_evals: list[dict]) -> dict:
nb_eval_completes (= prises en compte)
nb_evals_en_cours (= avec des notes, mais pas complete)
nb_evals_vides (= sans aucune note)
nb_evals_attente (= avec des notes en ATTente et pas bloquée)
date derniere modif
Une eval est "complete" ssi tous les etudiants *inscrits* ont une note.
"""
nb_evals_completes, nb_evals_en_cours, nb_evals_vides, nb_evals_blocked = 0, 0, 0, 0
(
nb_evals_completes,
nb_evals_en_cours,
nb_evals_vides,
nb_evals_blocked,
nb_evals_attente,
) = (0, 0, 0, 0, 0)
dates = []
for e in etat_evals:
if e["etat"]["blocked"]:
@ -294,6 +301,8 @@ def _summarize_evals_etats(etat_evals: list[dict]) -> dict:
nb_evals_vides += 1
elif not e["etat"]["blocked"]:
nb_evals_en_cours += 1
if e["etat"]["nb_attente"] and not e["etat"]["blocked"]:
nb_evals_attente += 1
last_modif = e["etat"]["last_modif"]
if last_modif is not None:
dates.append(e["etat"]["last_modif"])
@ -303,6 +312,7 @@ def _summarize_evals_etats(etat_evals: list[dict]) -> dict:
return {
"nb_evals": len(etat_evals),
"nb_evals_attente": nb_evals_attente,
"nb_evals_blocked": nb_evals_blocked,
"nb_evals_completes": nb_evals_completes,
"nb_evals_en_cours": nb_evals_en_cours,

View File

@ -1312,7 +1312,9 @@ def formsemestre_tableau_modules(
if etat["attente"]:
H.append(
f""" <span><a class="redlink" href="{moduleimpl_status_url}"
title="Il y a des notes en attente"><span class="evals_attente">en attente</span></a></span>"""
title="Il y a des notes en attente"><span class="evals_attente">{
etat["nb_evals_attente"]
} en attente</span></a></span>"""
)
if not mod_is_conforme:
H.append(

View File

@ -332,28 +332,29 @@ def fiche_etud(etudid=None):
)
# fiche admission
infos_admission = _infos_admission(etud, restrict_etud_data)
has_adm_notes = any(
infos_admission[k] for k in ("math", "physique", "anglais", "francais")
)
has_bac_info = any(
infos_admission[k]
for k in (
"bac_specialite",
"annee_bac",
"rapporteur",
"commentaire",
"classement",
"type_admission",
"rap",
if etud.admission:
infos_admission = _infos_admission(etud, restrict_etud_data)
has_adm_notes = any(
infos_admission[k] for k in ("math", "physique", "anglais", "francais")
)
)
if has_bac_info or has_adm_notes:
adm_tmpl = """<!-- Donnees admission -->
<div class="fichetitre">Informations admission</div>
"""
if has_adm_notes:
adm_tmpl += """
has_bac_info = any(
infos_admission[k]
for k in (
"bac_specialite",
"annee_bac",
"rapporteur",
"commentaire",
"classement",
"type_admission",
"rap",
)
)
if has_bac_info or has_adm_notes:
adm_tmpl = """<!-- Donnees admission -->
<div class="fichetitre">Informations admission</div>
"""
if has_adm_notes:
adm_tmpl += """
<table>
<tr><th>Bac</th><th>Année</th><th>Rg</th>
<th>Math</th><th>Physique</th><th>Anglais</th><th>Français</th></tr>
@ -364,24 +365,26 @@ def fiche_etud(etudid=None):
<td>%(math)s</td><td>%(physique)s</td><td>%(anglais)s</td><td>%(francais)s</td>
</tr>
</table>
"""
adm_tmpl += """
<div>Bac %(bac_specialite)s obtenu en %(annee_bac)s </div>
<div class="info_lycee">%(info_lycee)s</div>"""
if infos_admission["type_admission"] or infos_admission["classement"]:
adm_tmpl += """<div class="vadmission">"""
if infos_admission["type_admission"]:
adm_tmpl += """<span>Voie d'admission: <span class="etud_type_admission">%(type_admission)s</span></span> """
if infos_admission["classement"]:
adm_tmpl += """<span>Rang admission: <span class="etud_type_admission">%(classement)s</span></span>"""
if infos_admission["type_admission"] or infos_admission["classement"]:
adm_tmpl += "</div>"
if infos_admission["rap"]:
adm_tmpl += """<div class="note_rapporteur">%(rap)s</div>"""
adm_tmpl += """</div>"""
"""
adm_tmpl += """
<div>Bac %(bac_specialite)s obtenu en %(annee_bac)s </div>
<div class="info_lycee">%(info_lycee)s</div>"""
if infos_admission["type_admission"] or infos_admission["classement"]:
adm_tmpl += """<div class="vadmission">"""
if infos_admission["type_admission"]:
adm_tmpl += """<span>Voie d'admission: <span class="etud_type_admission">%(type_admission)s</span></span> """
if infos_admission["classement"]:
adm_tmpl += """<span>Rang admission: <span class="etud_type_admission">%(classement)s</span></span>"""
if infos_admission["type_admission"] or infos_admission["classement"]:
adm_tmpl += "</div>"
if infos_admission["rap"]:
adm_tmpl += """<div class="note_rapporteur">%(rap)s</div>"""
adm_tmpl += """</div>"""
else:
adm_tmpl = "" # pas de boite "info admission"
info["adm_data"] = adm_tmpl % infos_admission
else:
adm_tmpl = "" # pas de boite "info admission"
info["adm_data"] = adm_tmpl % infos_admission
info["adm_data"] = ""
# Fichiers archivés:
info["fichiers_archive_htm"] = (
@ -654,7 +657,7 @@ def _format_adresse(adresse: Adresse | None) -> dict:
def _infos_admission(etud: Identite, restrict_etud_data: bool) -> dict:
"""dict with adminission data, restricted or not"""
"""dict with admission data, restricted or not"""
# info sur rapporteur et son commentaire
rap = ""
if not restrict_etud_data:
@ -799,8 +802,11 @@ def etud_info_html(etudid, with_photo="1", debug=False):
code_cursus, _ = sco_report.get_code_cursus_etud(
etud, formsemestres=etud.get_formsemestres(), prefix="S", separator=", "
)
bac = sco_bac.Baccalaureat(etud.admission.bac, etud.admission.specialite)
bac_abbrev = bac.abbrev()
if etud.admission:
bac = sco_bac.Baccalaureat(etud.admission.bac, etud.admission.specialite)
bac_abbrev = bac.abbrev()
else:
bac_abbrev = "-"
H = f"""<div class="etud_info_div">
<div class="eid_left">
<div class="eid_nom"><div><a class="stdlink" target="_blank" href="{

View File

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