From 9172282451bafe3565dcb16d3e319ef4bd4c64b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 8 Jan 2022 20:07:13 +0100 Subject: [PATCH 1/4] backport fix formations --- app/models/formations.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/models/formations.py b/app/models/formations.py index e926471c..e2273c3b 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -5,6 +5,7 @@ from app import db from app.comp import df_cache from app.models import SHORT_STR_LEN from app.models.modules import Module +from app.models.ues import UniteEns from app.scodoc import notesdb as ndb from app.scodoc import sco_cache from app.scodoc import sco_codes_parcours @@ -130,8 +131,14 @@ class Formation(db.Model): db.session.add(mod) change = True # --- Numéros de modules - if Module.query.filter_by(formation_id=220, numero=None).count() > 0: + if Module.query.filter_by(formation_id=self.id, numero=None).count() > 0: scu.objects_renumber(db, self.modules.all()) + # --- Types d'UE (avant de rendre le type non nullable) + ues_sans_type = UniteEns.query.filter_by(formation_id=self.id, type=None) + if ues_sans_type.count() > 0: + for ue in ues_sans_type: + ue.type = 0 + db.session.add(ue) db.session.commit() if change: From 68dec8e1f8c076c3785d548302362ca6f9fc95a8 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 9 Jan 2022 10:11:50 +0100 Subject: [PATCH 2/4] =?UTF-8?q?Fix:=20anciens=20bulletins=20XML=20du=20BUT?= =?UTF-8?q?=20des=20d=C3=A9missionnaires?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/but/bulletin_but_xml_compat.py | 219 +++++++++++++++-------------- 1 file changed, 112 insertions(+), 107 deletions(-) diff --git a/app/but/bulletin_but_xml_compat.py b/app/but/bulletin_but_xml_compat.py index 5b4a14ca..ee42e444 100644 --- a/app/but/bulletin_but_xml_compat.py +++ b/app/but/bulletin_but_xml_compat.py @@ -68,11 +68,12 @@ def bulletin_but_xml_compat( "bulletin_but_xml_compat( formsemestre_id=%s, etudid=%s )" % (formsemestre_id, etudid) ) - sem = FormSemestre.query.get_or_404(formsemestre_id) + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) etud = Identite.query.get_or_404(etudid) - results = bulletin_but.ResultatsSemestreBUT(sem) + results = bulletin_but.ResultatsSemestreBUT(formsemestre) nb_inscrits = len(results.etuds) - if (not sem.bul_hide_xml) or force_publishing: + etat_inscription = etud.etat_inscription(formsemestre.id) + if (not formsemestre.bul_hide_xml) or force_publishing: published = 1 else: published = 0 @@ -86,10 +87,10 @@ def bulletin_but_xml_compat( "date": docdate, "publie": str(published), } - if sem.etapes: - el["etape_apo"] = sem.etapes[0].etape_apo or "" + if formsemestre.etapes: + el["etape_apo"] = formsemestre.etapes[0].etape_apo or "" n = 2 - for et in sem.etapes[1:]: + for et in formsemestre.etapes[1:]: el["etape_apo" + str(n)] = et.etape_apo or "" n += 1 x = Element("bulletinetud", **el) @@ -120,117 +121,121 @@ def bulletin_but_xml_compat( return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode( scu.SCO_ENCODING ) # stop ! - # Moyenne générale: - doc.append( - Element( - "note", - value=scu.fmt_note(results.etud_moy_gen[etud.id]), - min=scu.fmt_note(results.etud_moy_gen.min()), - max=scu.fmt_note(results.etud_moy_gen.max()), - moy=scu.fmt_note(results.etud_moy_gen.mean()), # moyenne des moy. gen. - ) - ) - rang = 0 # XXX TODO rang de l'étduiant selon la moy gen indicative - bonus = 0 # XXX TODO valeur du bonus sport - doc.append(Element("rang", value=str(rang), ninscrits=str(nb_inscrits))) - # XXX TODO: ajouter "rang_group" : rangs dans les partitions - doc.append(Element("note_max", value="20")) # notes toujours sur 20 - doc.append(Element("bonus_sport_culture", value=str(bonus))) - # Liste les UE / modules /evals - for ue in results.ues: - rang_ue = 0 # XXX TODO rang de l'étudiant dans cette UE - nb_inscrits_ue = ( - nb_inscrits # approx: compliqué de définir le "nb d'inscrit à une UE" - ) - x_ue = Element( - "ue", - id=str(ue.id), - numero=scu.quote_xml_attr(ue.numero), - acronyme=scu.quote_xml_attr(ue.acronyme or ""), - titre=scu.quote_xml_attr(ue.titre or ""), - code_apogee=scu.quote_xml_attr(ue.code_apogee or ""), - ) - doc.append(x_ue) - if ue.type != sco_codes_parcours.UE_SPORT: - v = results.etud_moy_ue[ue.id][etud.id] - else: - v = 0 # XXX TODO valeur bonus sport pour cet étudiant - x_ue.append( + + if etat_inscription == scu.INSCRIT: + # Moyenne générale: + doc.append( Element( "note", - value=scu.fmt_note(v), - min=scu.fmt_note(results.etud_moy_ue[ue.id].min()), - max=scu.fmt_note(results.etud_moy_ue[ue.id].max()), + value=scu.fmt_note(results.etud_moy_gen[etud.id]), + min=scu.fmt_note(results.etud_moy_gen.min()), + max=scu.fmt_note(results.etud_moy_gen.max()), + moy=scu.fmt_note(results.etud_moy_gen.mean()), # moyenne des moy. gen. ) ) - x_ue.append(Element("ects", value=str(ue.ects if ue.ects else 0))) - x_ue.append(Element("rang", value=str(rang_ue))) - x_ue.append(Element("effectif", value=str(nb_inscrits_ue))) - # Liste les modules rattachés à cette UE - for modimpl in results.modimpls: - # Liste ici uniquement les modules rattachés à cette UE - if modimpl.module.ue.id == ue.id: - mod_moy = scu.fmt_note(results.etud_moy_ue[ue.id][etud.id]) - coef = results.modimpl_coefs_df[modimpl.id][ue.id] - x_mod = Element( - "module", - id=str(modimpl.id), - code=str(modimpl.module.code or ""), - coefficient=str(coef), - numero=str(modimpl.module.numero or 0), - titre=scu.quote_xml_attr(modimpl.module.titre or ""), - abbrev=scu.quote_xml_attr(modimpl.module.abbrev or ""), - code_apogee=scu.quote_xml_attr(modimpl.module.code_apogee or ""), + rang = 0 # XXX TODO rang de l'étduiant selon la moy gen indicative + bonus = 0 # XXX TODO valeur du bonus sport + doc.append(Element("rang", value=str(rang), ninscrits=str(nb_inscrits))) + # XXX TODO: ajouter "rang_group" : rangs dans les partitions + doc.append(Element("note_max", value="20")) # notes toujours sur 20 + doc.append(Element("bonus_sport_culture", value=str(bonus))) + # Liste les UE / modules /evals + for ue in results.ues: + rang_ue = 0 # XXX TODO rang de l'étudiant dans cette UE + nb_inscrits_ue = ( + nb_inscrits # approx: compliqué de définir le "nb d'inscrit à une UE" + ) + x_ue = Element( + "ue", + id=str(ue.id), + numero=scu.quote_xml_attr(ue.numero), + acronyme=scu.quote_xml_attr(ue.acronyme or ""), + titre=scu.quote_xml_attr(ue.titre or ""), + code_apogee=scu.quote_xml_attr(ue.code_apogee or ""), + ) + doc.append(x_ue) + if ue.type != sco_codes_parcours.UE_SPORT: + v = results.etud_moy_ue[ue.id][etud.id] + else: + v = 0 # XXX TODO valeur bonus sport pour cet étudiant + x_ue.append( + Element( + "note", + value=scu.fmt_note(v), + min=scu.fmt_note(results.etud_moy_ue[ue.id].min()), + max=scu.fmt_note(results.etud_moy_ue[ue.id].max()), ) - x_ue.append(x_mod) - x_mod.append( - Element( - "note", - value=mod_moy, - min=scu.fmt_note(results.etud_moy_ue[ue.id].min()), - max=scu.fmt_note(results.etud_moy_ue[ue.id].max()), - moy=scu.fmt_note(results.etud_moy_ue[ue.id].mean()), + ) + x_ue.append(Element("ects", value=str(ue.ects if ue.ects else 0))) + x_ue.append(Element("rang", value=str(rang_ue))) + x_ue.append(Element("effectif", value=str(nb_inscrits_ue))) + # Liste les modules rattachés à cette UE + for modimpl in results.modimpls: + # Liste ici uniquement les modules rattachés à cette UE + if modimpl.module.ue.id == ue.id: + mod_moy = scu.fmt_note(results.etud_moy_ue[ue.id][etud.id]) + coef = results.modimpl_coefs_df[modimpl.id][ue.id] + x_mod = Element( + "module", + id=str(modimpl.id), + code=str(modimpl.module.code or ""), + coefficient=str(coef), + numero=str(modimpl.module.numero or 0), + titre=scu.quote_xml_attr(modimpl.module.titre or ""), + abbrev=scu.quote_xml_attr(modimpl.module.abbrev or ""), + code_apogee=scu.quote_xml_attr( + modimpl.module.code_apogee or "" + ), ) - ) - # XXX TODO rangs et effectifs - # --- notes de chaque eval: - if version != "short": - for e in modimpl.evaluations: - if e.visibulletin or version == "long": - x_eval = Element( - "evaluation", - jour=e.jour.isoformat() if e.jour else "", - heure_debut=e.heure_debut.isoformat() - if e.heure_debut - else "", - heure_fin=e.heure_fin.isoformat() - if e.heure_debut - else "", - coefficient=str(e.coefficient), - # pas les poids en XML compat - evaluation_type=str(e.evaluation_type), - description=scu.quote_xml_attr(e.description), - # notes envoyées sur 20, ceci juste pour garder trace: - note_max_origin=str(e.note_max), - ) - x_mod.append(x_eval) - x_eval.append( - Element( - "note", - value=scu.fmt_note( - results.modimpls_evals_notes[e.moduleimpl_id][ - e.id - ][etud.id], - note_max=e.note_max, - ), + x_ue.append(x_mod) + x_mod.append( + Element( + "note", + value=mod_moy, + min=scu.fmt_note(results.etud_moy_ue[ue.id].min()), + max=scu.fmt_note(results.etud_moy_ue[ue.id].max()), + moy=scu.fmt_note(results.etud_moy_ue[ue.id].mean()), + ) + ) + # XXX TODO rangs et effectifs + # --- notes de chaque eval: + if version != "short": + for e in modimpl.evaluations: + if e.visibulletin or version == "long": + x_eval = Element( + "evaluation", + jour=e.jour.isoformat() if e.jour else "", + heure_debut=e.heure_debut.isoformat() + if e.heure_debut + else "", + heure_fin=e.heure_fin.isoformat() + if e.heure_debut + else "", + coefficient=str(e.coefficient), + # pas les poids en XML compat + evaluation_type=str(e.evaluation_type), + description=scu.quote_xml_attr(e.description), + # notes envoyées sur 20, ceci juste pour garder trace: + note_max_origin=str(e.note_max), ) - ) - # XXX TODO: Evaluations incomplètes ou futures: XXX - # XXX TODO UE capitalisee (listee seulement si meilleure que l'UE courante) + x_mod.append(x_eval) + x_eval.append( + Element( + "note", + value=scu.fmt_note( + results.modimpls_evals_notes[ + e.moduleimpl_id + ][e.id][etud.id], + note_max=e.note_max, + ), + ) + ) + # XXX TODO: Evaluations incomplètes ou futures: XXX + # XXX TODO UE capitalisee (listee seulement si meilleure que l'UE courante) # --- Absences if sco_preferences.get_preference("bul_show_abs", formsemestre_id): - nbabs, nbabsjust = sem.get_abs_count(etud.id) + nbabs, nbabsjust = formsemestre.get_abs_count(etud.id) doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust))) # -------- LA SUITE EST COPIEE SANS MODIF DE sco_bulletins_xml.py --------- From acdd037483d450b5b0eabae77b2c3f06b99d8034 Mon Sep 17 00:00:00 2001 From: lehmann Date: Sun, 9 Jan 2022 11:36:15 +0100 Subject: [PATCH 3/4] =?UTF-8?q?Relev=C3=A9=20:=20d=C3=A9cision=20jury=20+?= =?UTF-8?q?=20fl=C3=A8ches?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/css/releve-but.css | 38 +++++++++++++++++++++++++++++++++-- app/static/js/releve-but.js | 34 ++++++++++++++++--------------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/app/static/css/releve-but.css b/app/static/css/releve-but.css index 60d4f01c..3f132d6a 100644 --- a/app/static/css/releve-but.css +++ b/app/static/css/releve-but.css @@ -77,6 +77,17 @@ section>div:nth-child(1){ display: flex !important; } +.listeOff .ue::before, +.listeOff .module::before, +.moduleOnOff .ue::before, +.moduleOnOff .module::before{ + transform: rotate(0); +} +.listeOff .moduleOnOff .ue::before, +.listeOff .moduleOnOff .module::before{ + transform: rotate(180deg) !important; +} + /***********************/ /* Options d'affichage */ /***********************/ @@ -118,11 +129,16 @@ section>div:nth-child(1){ /************/ /* Semestre */ /************/ +.flex{ + display: flex; + gap: 16px; +} .infoSemestre{ display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; + flex: none; } .infoSemestre>div{ border: 1px solid var(--couleurIntense); @@ -141,7 +157,12 @@ section>div:nth-child(1){ .rang{ text-decoration: underline var(--couleurIntense); } - +.decision{ + margin: 5px 0; + font-weight: bold; + font-size: 20px; + text-decoration: underline var(--couleurIntense); +} .enteteSemestre{ color: black; font-weight: bold; @@ -174,8 +195,21 @@ section>div:nth-child(1){ display: flex; gap: 16px; margin: 4px 0 2px 0; - overflow: auto; + overflow-x: auto; + overflow-y: hidden; cursor: pointer; + position: relative; +} +.module::before, .ue::before { + content:url("data:image/svg+xml;utf8,"); + width: 26px; + height: 26px; + position: absolute; + bottom: 0; + left: 50%; + margin-left: -13px; + transform: rotate(180deg); + transition: 0.2s; } h3{ display: flex; diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js index 126cb219..5042031b 100644 --- a/app/static/js/releve-but.js +++ b/app/static/js/releve-but.js @@ -75,10 +75,15 @@ class releveBUT extends HTMLElement {

Semestre

-
Inscrit le
- Les moyennes servent à situer l'étudiant dans la promotion et ne correspondent pas à des validations de - compétences ou d'UE. -
+
+
+
+
Validé !
+
Inscrit le
+ Les moyennes servent à situer l'étudiant dans la promotion et ne correspondent pas à des validations de compétences ou d'UE. +
+
+
@@ -91,8 +96,7 @@ class releveBUT extends HTMLElement { La moyenne des ressources dans une UE dépend des poids donnés aux évaluations.
- Liste + Liste
@@ -107,8 +111,7 @@ class releveBUT extends HTMLElement {

Ressources

- Liste + Liste
@@ -120,8 +123,7 @@ class releveBUT extends HTMLElement {

SAÉ

- Liste + Liste
@@ -187,9 +189,9 @@ class releveBUT extends HTMLElement {
Max. promo. :
${data.semestre.notes.max}
Moy. promo. :
${data.semestre.notes.moy}
Min. promo. :
${data.semestre.notes.min}
-
- ${data.semestre.groupes.map(groupe => { - return ` +
`; + /*${data.semestre.groupes.map(groupe => { + return `
Groupe
${groupe.nom}
Rang :
${groupe.rang.value} / ${groupe.rang.total}
@@ -198,10 +200,10 @@ class releveBUT extends HTMLElement {
Min. groupe :
${groupe.notes.min}
`; - }).join("") - } - `; + }).join("") + }*/ this.shadow.querySelector(".infoSemestre").innerHTML = output; + this.shadow.querySelector(".decision").innerHTML = data.semestre.decision.code; } /*******************************/ From 0a1264051c2f6ed010e37c9406be90c03ca2cb84 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 9 Jan 2022 21:48:58 +0100 Subject: [PATCH 4/4] =?UTF-8?q?Bulletin=20BUT:=20n'affiche=20que=20les=20m?= =?UTF-8?q?odules=20auxquels=20l'=C3=A9tudiant=20est=20inscrit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/but/bulletin_but.py | 70 ++++++++++++++++++++------------------- app/models/moduleimpls.py | 3 ++ app/scodoc/sco_edit_ue.py | 8 ++++- sco_version.py | 2 +- 4 files changed, 47 insertions(+), 36 deletions(-) diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 54d08400..eac6d52c 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -107,17 +107,18 @@ class ResultatsSemestreBUT: ue_idx = self.modimpl_coefs_df.index.get_loc(ue.id) etud_moy_module = self.sem_cube[etud_idx] # module x UE for mi in modimpls: - coef = self.modimpl_coefs_df[mi.id][ue.id] - if coef > 0: - d[mi.module.code] = { - "id": mi.id, - "coef": coef, - "moyenne": fmt_note( - etud_moy_module[self.modimpl_coefs_df.columns.get_loc(mi.id)][ - ue_idx - ] - ), - } + if self.modimpl_inscr_df[str(mi.id)][etud.id]: # si inscrit + coef = self.modimpl_coefs_df[mi.id][ue.id] + if coef > 0: + d[mi.module.code] = { + "id": mi.id, + "coef": coef, + "moyenne": fmt_note( + etud_moy_module[ + self.modimpl_coefs_df.columns.get_loc(mi.id) + ][ue_idx] + ), + } return d def etud_ue_results(self, etud, ue): @@ -163,29 +164,30 @@ class ResultatsSemestreBUT: # moy_indicative_mod = np.nanmean(self.sem_cube[etud_idx, mod_idx]) # except RuntimeWarning: # all nans in np.nanmean # pass - d[mi.module.code] = { - "id": mi.id, - "titre": mi.module.titre, - "code_apogee": mi.module.code_apogee, - "url": url_for( - "notes.moduleimpl_status", - scodoc_dept=g.scodoc_dept, - moduleimpl_id=mi.id, - ), - "moyenne": { - # # moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan) - # "value": fmt_note(moy_indicative_mod), - # "min": fmt_note(moyennes_etuds.min()), - # "max": fmt_note(moyennes_etuds.max()), - # "moy": fmt_note(moyennes_etuds.mean()), - }, - "evaluations": [ - self.etud_eval_results(etud, e) - for eidx, e in enumerate(mi.evaluations) - if e.visibulletin - and self.modimpls_evaluations_complete[mi.id][eidx] - ], - } + if self.modimpl_inscr_df[str(mi.id)][etud.id]: # si inscrit + d[mi.module.code] = { + "id": mi.id, + "titre": mi.module.titre, + "code_apogee": mi.module.code_apogee, + "url": url_for( + "notes.moduleimpl_status", + scodoc_dept=g.scodoc_dept, + moduleimpl_id=mi.id, + ), + "moyenne": { + # # moyenne indicative de module: moyenne des UE, ignorant celles sans notes (nan) + # "value": fmt_note(moy_indicative_mod), + # "min": fmt_note(moyennes_etuds.min()), + # "max": fmt_note(moyennes_etuds.max()), + # "moy": fmt_note(moyennes_etuds.mean()), + }, + "evaluations": [ + self.etud_eval_results(etud, e) + for eidx, e in enumerate(mi.evaluations) + if e.visibulletin + and self.modimpls_evaluations_complete[mi.id][eidx] + ], + } return d def etud_eval_results(self, etud, e) -> dict: diff --git a/app/models/moduleimpls.py b/app/models/moduleimpls.py index 172fe076..1b888c54 100644 --- a/app/models/moduleimpls.py +++ b/app/models/moduleimpls.py @@ -44,6 +44,9 @@ class ModuleImpl(db.Model): def __init__(self, **kwargs): super(ModuleImpl, self).__init__(**kwargs) + def __repr__(self): + return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>" + def get_evaluations_poids(self) -> pd.DataFrame: """Les poids des évaluations vers les UE (accès via cache)""" evaluations_poids = df_cache.EvaluationsPoidsCache.get(self.id) diff --git a/app/scodoc/sco_edit_ue.py b/app/scodoc/sco_edit_ue.py index abda43bd..a52826b7 100644 --- a/app/scodoc/sco_edit_ue.py +++ b/app/scodoc/sco_edit_ue.py @@ -470,7 +470,13 @@ def ue_delete(ue_id=None, delete_validations=False, dialog_confirmed=False): if ue.modules.all(): raise ScoValueError( f"""Suppression de l'UE {ue.titre} impossible car - des modules (ou SAÉ ou ressources) lui sont rattachés.""" + des modules (ou SAÉ ou ressources) lui sont rattachés.""", + dest_url=url_for( + "notes.ue_table", + scodoc_dept=g.scodoc_dept, + formation_id=ue.formation.id, + semestre_idx=ue.semestre_idx, + ), ) if not can_delete_ue(ue): raise ScoNonEmptyFormationObject( diff --git a/sco_version.py b/sco_version.py index aabbc6ca..ace35b65 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.1.20" +SCOVERSION = "9.1.21" SCONAME = "ScoDoc"