diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py index 4379fdac5..f64769fc9 100644 --- a/app/but/bulletin_but.py +++ b/app/but/bulletin_but.py @@ -111,9 +111,10 @@ class BulletinBUT: d["modules"] = self.etud_mods_results(etud, modimpls_spo) return d - def etud_mods_results(self, etud, modimpls) -> dict: + def etud_mods_results(self, etud, modimpls, version="long") -> dict: """dict synthèse résultats des modules indiqués, - avec évaluations de chacun.""" + avec évaluations de chacun (sauf si version == "short") + """ res = self.res d = {} # etud_idx = self.etud_index[etud.id] @@ -154,12 +155,14 @@ class BulletinBUT: "evaluations": [ self.etud_eval_results(etud, e) for e in modimpl.evaluations - if e.visibulletin + if (e.visibulletin or version == "long") and ( modimpl_results.evaluations_etat[e.id].is_complete or self.prefs["bul_show_all_evals"] ) - ], + ] + if version != "short" + else [], } return d @@ -217,9 +220,17 @@ class BulletinBUT: return f"Bonus de {fmt_note(bonus_vect.iloc[0])}" def bulletin_etud( - self, etud: Identite, formsemestre: FormSemestre, force_publishing=False + self, + etud: Identite, + formsemestre: FormSemestre, + force_publishing=False, + version="long", ) -> dict: """Le bulletin de l'étudiant dans ce semestre: dict pour la version JSON / HTML. + - version: + "long", "selectedevals": toutes les infos (notes des évaluations) + "short" : ne descend pas plus bas que les modules. + - Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai (bulletins non publiés). """ @@ -282,8 +293,10 @@ class BulletinBUT: ) d.update( { - "ressources": self.etud_mods_results(etud, res.ressources), - "saes": self.etud_mods_results(etud, res.saes), + "ressources": self.etud_mods_results( + etud, res.ressources, version=version + ), + "saes": self.etud_mods_results(etud, res.saes, version=version), "ues": { ue.acronyme: self.etud_ue_results(etud, ue) for ue in res.ues diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 953fb280a..060debc3b 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -160,6 +160,7 @@ class Identite(db.Model): "etudid": self.id, "nom": self.nom_disp(), "prenom": self.prenom, + "nomprenom": self.nomprenom, } if include_urls: d["fiche_url"] = url_for( diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index ce162d249..d92c375fb 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -22,6 +22,7 @@ from app.scodoc import sco_codes_parcours from app.scodoc import sco_preferences from app.scodoc.sco_vdi import ApoEtapeVDI from app.scodoc.sco_permissions import Permission +from app.scodoc.sco_utils import MONTH_NAMES_ABBREV class FormSemestre(db.Model): @@ -162,8 +163,8 @@ class FormSemestre(db.Model): d["periode"] = 2 # typiquement, début en février: S2, S4... d["titre_num"] = self.titre_num() d["titreannee"] = self.titre_annee() - d["mois_debut"] = f"{self.date_debut.month} {self.date_debut.year}" - d["mois_fin"] = f"{self.date_fin.month} {self.date_fin.year}" + d["mois_debut"] = self.mois_debut() + d["mois_fin"] = self.mois_fin() d["titremois"] = "%s %s (%s - %s)" % ( d["titre_num"], self.modalite or "", @@ -293,6 +294,7 @@ class FormSemestre(db.Model): """chaîne "J. Dupond, X. Martin" ou "Jacques Dupond, Xavier Martin" """ + # was "nomcomplet" if not self.responsables: return "" if abbrev_prenom: @@ -304,6 +306,14 @@ class FormSemestre(db.Model): "2021 - 2022" return scu.annee_scolaire_repr(self.date_debut.year, self.date_debut.month) + def mois_debut(self) -> str: + "Oct 2021" + return f"{MONTH_NAMES_ABBREV[self.date_debut.month - 1]} {self.date_debut.year}" + + def mois_fin(self) -> str: + "Jul 2022" + return f"{MONTH_NAMES_ABBREV[self.date_fin.month - 1]} {self.date_debut.year}" + def session_id(self) -> str: """identifiant externe de semestre de formation Exemple: RT-DUT-FI-S1-ANNEE diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index 046c81468..b21404276 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -793,13 +793,14 @@ def etud_descr_situation_semestre( def formsemestre_bulletinetud( etudid=None, formsemestre_id=None, - format="html", + format=None, version="long", xml_with_decisions=False, force_publishing=False, # force publication meme si semestre non publie sur "portail" prefer_mail_perso=False, ): "page bulletin de notes" + format = format or "html" etud: Identite = Identite.query.get_or_404(etudid) formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id) if not formsemestre: @@ -879,7 +880,7 @@ def do_formsemestre_bulletinetud( formsemestre: FormSemestre, etudid: int, version="long", # short, long, selectedevals - format="html", + format=None, nohtml=False, xml_with_decisions=False, # force décisions dans XML force_publishing=False, # force publication meme si semestre non publié sur "portail" @@ -890,6 +891,7 @@ def do_formsemestre_bulletinetud( où bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json) et filigranne est un message à placer en "filigranne" (eg "Provisoire"). """ + format = format or "html" if format == "xml": bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud( formsemestre.id, @@ -1258,7 +1260,7 @@ def make_menu_autres_operations( "enabled": sco_permissions_check.can_validate_sem(formsemestre.id), }, { - "title": "Editer PV jury", + "title": "Éditer PV jury", "endpoint": "notes.formsemestre_pvjury_pdf", "args": { "formsemestre_id": formsemestre.id, diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py index 7f6a53c60..2aeb792fb 100644 --- a/app/scodoc/sco_bulletins_generator.py +++ b/app/scodoc/sco_bulletins_generator.py @@ -297,7 +297,11 @@ def register_bulletin_class(klass): def bulletin_class_descriptions(): - return [x.description for x in BULLETIN_CLASSES.values()] + return [ + BULLETIN_CLASSES[class_name].description + for class_name in BULLETIN_CLASSES + if BULLETIN_CLASSES[class_name].list_in_menu + ] def bulletin_class_names() -> list[str]: diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 2080e9572..11e665d92 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -31,7 +31,7 @@ from flask import current_app from flask import g from flask import request -from flask import url_for +from flask import render_template, url_for from flask_login import current_user from app import log @@ -411,7 +411,7 @@ def formsemestre_status_menubar(sem): "enabled": sco_permissions_check.can_validate_sem(formsemestre_id), }, { - "title": "Editer les PV et archiver les résultats", + "title": "Éditer les PV et archiver les résultats", "endpoint": "notes.formsemestre_archive", "args": {"formsemestre_id": formsemestre_id}, "enabled": sco_permissions_check.can_edit_pv(formsemestre_id), @@ -445,6 +445,7 @@ def retreive_formsemestre_from_request() -> int: """Cherche si on a de quoi déduire le semestre affiché à partir des arguments de la requête: formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id + Returns None si pas défini. """ if request.method == "GET": args = request.args @@ -505,34 +506,17 @@ def formsemestre_page_title(): return "" try: formsemestre_id = int(formsemestre_id) - sem = sco_formsemestre.get_formsemestre(formsemestre_id).copy() + formsemestre = FormSemestre.query.get(formsemestre_id) except: log("can't find formsemestre_id %s" % formsemestre_id) return "" - fill_formsemestre(sem) - - h = f"""
-
- {sem['titre']}{sem['num_sem']}{sem['modalitestr']}{sem['mois_debut']} - {sem['mois_fin']}{sem['resp']}{sem['nbinscrits']} inscrits{sem['locklink']}{sem['eyelink']} -
- {formsemestre_status_menubar(sem)} -
- """ + h = render_template( + "formsemestre_page_title.html", + formsemestre=formsemestre, + scu=scu, + sem_menu_bar=formsemestre_status_menubar(formsemestre.to_dict()), + ) return h diff --git a/app/scodoc/sco_photos.py b/app/scodoc/sco_photos.py index cf66a8526..0dfeaafe3 100644 --- a/app/scodoc/sco_photos.py +++ b/app/scodoc/sco_photos.py @@ -175,7 +175,7 @@ def etud_photo_is_local(etud: dict, size="small"): return photo_pathname(etud["photo_filename"], size=size) -def etud_photo_html(etud=None, etudid=None, title=None, size="small"): +def etud_photo_html(etud: dict = None, etudid=None, title=None, size="small"): """HTML img tag for the photo, either in small size (h90) or original size (size=="orig") """ diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index b30c493ca..ba7fd504a 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -619,7 +619,7 @@ def bul_filename_old(sem: dict, etud: dict, format): def bul_filename(formsemestre, etud, format): """Build a filename for this bulletin""" dt = time.strftime("%Y-%m-%d") - filename = f"bul-{formsemestre.sem.titre_num}-{dt}-{etud.nom}.{format}" + filename = f"bul-{formsemestre.titre_num()}-{dt}-{etud.nom}.{format}" filename = make_filename(filename) return filename diff --git a/app/static/css/releve-but.css b/app/static/css/releve-but.css index 02cceb94c..d9756ace4 100644 --- a/app/static/css/releve-but.css +++ b/app/static/css/releve-but.css @@ -14,16 +14,25 @@ } main{ --couleurPrincipale: rgb(240,250,255); - --couleurFondTitresUE: rgb(206,255,235); - --couleurFondTitresRes: rgb(125, 170, 255); - --couleurFondTitresSAE: rgb(211, 255, 255); + --couleurFondTitresUE: #b6ebff; + --couleurFondTitresRes: #f8c844; + --couleurFondTitresSAE: #c6ffab; --couleurSecondaire: #fec; - --couleurIntense: #c09; - --couleurSurlignage: rgba(232, 255, 132, 0.47); + --couleurIntense: rgb(4, 16, 159);; + --couleurSurlignage: rgba(255, 253, 110, 0.49); max-width: 1000px; margin: auto; display: none; } +.releve a, .releve a:visited { + color: navy; + text-decoration: none; +} +.releve a:hover { + color: red; + text-decoration: underline; +} + .ready .wait{display: none;} .ready main{display: block;} h2{ @@ -152,12 +161,14 @@ section>div:nth-child(1){ column-gap: 4px; flex: none; } -.infoSemestre>div:nth-child(1){ - margin-right: auto; -} + .infoSemestre>div>div:nth-child(even){ text-align: right; } +.photo { + border: none; + margin-left: auto; +} .rang{ font-weight: bold; } @@ -213,7 +224,6 @@ section>div:nth-child(1){ scroll-margin-top: 60px; } .module, .ue { - background: var(--couleurSecondaire); color: #000; padding: 4px 32px; border-radius: 4px; @@ -225,6 +235,15 @@ section>div:nth-child(1){ cursor: pointer; position: relative; } +.ue { + background: var(--couleurFondTitresRes); +} +.module { + background: var(--couleurFondTitresRes); +} +.module h3 { + background: var(--couleurFondTitresRes); +} .module::before, .ue::before { content:url("data:image/svg+xml;utf8,"); width: 26px; diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index a9390db24..31603a34b 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1963,7 +1963,9 @@ table.notes_recapcomplet a:hover { div.notes_bulletin { margin-right: 5px; } - +div.bulletin_menubar { + margin-right: 2em; +} table.notes_bulletin { border-collapse: collapse; border: 2px solid rgb(100,100,240); diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js index c7c649e51..97f97e29d 100644 --- a/app/static/js/releve-but.js +++ b/app/static/js/releve-but.js @@ -79,8 +79,8 @@ class releveBUT extends HTMLElement {
-

Semestre

-
+

+
@@ -97,7 +97,7 @@ class releveBUT extends HTMLElement {
-

Synthèse

+

Unités d'enseignement

La moyenne des ressources dans une UE dépend des poids donnés aux évaluations.
@@ -126,7 +126,7 @@ class releveBUT extends HTMLElement {
-

SAÉ

+

Situations d'apprentissage et d'évaluation (SAÉ)

Liste @@ -192,7 +192,8 @@ class releveBUT extends HTMLElement { /* Information sur le semestre */ /*******************************/ showSemestre(data) { - this.shadow.querySelector("h2").innerHTML += data.semestre.numero; + + this.shadow.querySelector("#identite_etudiant").innerHTML = ` ${data.etudiant.nomprenom} `; this.shadow.querySelector(".dateInscription").innerHTML += this.ISOToDate(data.semestre.inscription); let output = `
@@ -206,7 +207,9 @@ class releveBUT extends HTMLElement {
Absences
N.J. ${data.semestre.absences?.injustifie ?? "-"}
Total ${data.semestre.absences?.total ?? "-"}
-
`; +
+ photo de l'étudiant + `; /*${data.semestre.groupes.map(groupe => { return `
diff --git a/app/templates/bul_head.html b/app/templates/bul_head.html index 5211fcd4f..12df3c4f8 100644 --- a/app/templates/bul_head.html +++ b/app/templates/bul_head.html @@ -5,10 +5,12 @@ +{% if not is_apc %} +{% endif %}
+{% if not is_apc %}

{{etud.nomprenom}}

+{% endif %}
Bulletin
{{etud.photo_html(title="fiche de " + etud["nom"])|safe}}
diff --git a/app/templates/but/bulletin.html b/app/templates/but/bulletin.html index 02a09e848..c3e8c834e 100644 --- a/app/templates/but/bulletin.html +++ b/app/templates/but/bulletin.html @@ -6,8 +6,8 @@ {% endblock %} {% block app_content %} -

Totoro

+{% include 'bul_head.html' %} diff --git a/app/templates/formsemestre_page_title.html b/app/templates/formsemestre_page_title.html new file mode 100644 index 000000000..70fef579d --- /dev/null +++ b/app/templates/formsemestre_page_title.html @@ -0,0 +1,50 @@ +{# -*- mode: jinja-html -*- #} +{# Element HTML decrivant un semestre (barre de menu et infos) #} +{# was formsemestre_page_title #} + + \ No newline at end of file diff --git a/app/views/__init__.py b/app/views/__init__.py index 3d8af0777..f3c8e13d4 100644 --- a/app/views/__init__.py +++ b/app/views/__init__.py @@ -50,27 +50,29 @@ def close_dept_db_connection(arg): class ScoData: """Classe utilisée pour passer des valeurs aux vues (templates)""" - def __init__(self): + def __init__(self, etud=None, formsemestre=None): # Champs utilisés par toutes les pages ScoDoc (sidebar, en-tête) self.Permission = Permission self.scu = scu self.SCOVERSION = sco_version.SCOVERSION # -- Informations étudiant courant, si sélectionné: - etudid = g.get("etudid", None) - if not etudid: - if request.method == "GET": - etudid = request.args.get("etudid", None) - elif request.method == "POST": - etudid = request.form.get("etudid", None) - - if etudid: + if etud is None: + etudid = g.get("etudid", None) + if etudid is None: + if request.method == "GET": + etudid = request.args.get("etudid", None) + elif request.method == "POST": + etudid = request.form.get("etudid", None) + if etudid is not None: + etud = Identite.query.get_or_404(etudid) + self.etud = etud + if etud is not None: # Infos sur l'étudiant courant - self.etud = Identite.query.get_or_404(etudid) ins = self.etud.inscription_courante() if ins: self.etud_cur_sem = ins.formsemestre self.nbabs, self.nbabsjust = sco_abs.get_abs_count_in_interval( - etudid, + etud.id, self.etud_cur_sem.date_debut.isoformat(), self.etud_cur_sem.date_fin.isoformat(), ) @@ -80,17 +82,22 @@ class ScoData: else: self.etud = None # --- Informations sur semestre courant, si sélectionné - formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request() - if formsemestre_id is None: + if formsemestre is None: + formsemestre_id = ( + sco_formsemestre_status.retreive_formsemestre_from_request() + ) + if formsemestre_id is not None: + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) + if formsemestre is None: self.sem = None self.sem_menu_bar = None else: - self.sem = FormSemestre.query.get_or_404(formsemestre_id) + self.sem = formsemestre self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar( self.sem.to_dict() ) # --- Préférences - self.prefs = sco_preferences.SemPreferences(formsemestre_id) + self.prefs = sco_preferences.SemPreferences(formsemestre.id) from app.views import scodoc, notes, scolar, absences, users, pn_modules, refcomp diff --git a/app/views/notes.py b/app/views/notes.py index 6cbf0be43..5ca463b4c 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -32,6 +32,7 @@ Emmanuel Viennet, 2021 """ from operator import itemgetter +import time from xml.etree import ElementTree import flask @@ -276,7 +277,7 @@ sco_publish( def formsemestre_bulletinetud( etudid=None, formsemestre_id=None, - format="html", + format=None, version="long", xml_with_decisions=False, force_publishing=False, @@ -284,6 +285,7 @@ def formsemestre_bulletinetud( code_nip=None, code_ine=None, ): + format = format or "html" if not formsemestre_id: flask.abort(404, "argument manquant: formsemestre_id") if not isinstance(formsemestre_id, int): @@ -311,12 +313,16 @@ def formsemestre_bulletinetud( if format == "json": r = bulletin_but.BulletinBUT(formsemestre) return jsonify( - r.bulletin_etud(etud, formsemestre, force_publishing=force_publishing) + r.bulletin_etud( + etud, + formsemestre, + force_publishing=force_publishing, + version=version, + ) ) elif format == "html": return render_template( "but/bulletin.html", - title=f"Bul. {etud.nom} - BUT", bul_url=url_for( "notes.formsemestre_bulletinetud", scodoc_dept=g.scodoc_dept, @@ -324,8 +330,19 @@ def formsemestre_bulletinetud( etudid=etudid, format="json", force_publishing=1, # pour ScoDoc lui même + version=version, ), - sco=ScoData(), + etud=etud, + formsemestre=formsemestre, + is_apc=formsemestre.formation.is_apc(), + menu_autres_operations=sco_bulletins.make_menu_autres_operations( + formsemestre, etud, "notes.formsemestre_bulletinetud", version + ), + sco=ScoData(etud=etud), + scu=scu, + time=time, + title=f"Bul. {etud.nom} - BUT", + version=version, ) if not (etudid or code_nip or code_ine):