diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index 22d40785..6831f092 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -274,6 +274,11 @@ class ApcReferentielCompetences(db.Model, XMLModel): return "type_departement mismatch" # Table d'équivalences entre refs: equiv = self._load_config_equivalences() + # Même specialité (ou alias) ? + if self.specialite != other.specialite and other.specialite not in equiv.get( + "alias", [] + ): + return "specialite mismatch" # mêmes parcours ? eq_parcours = equiv.get("parcours", {}) parcours_by_code_1 = {eq_parcours.get(p.code, p.code): p for p in self.parcours} @@ -317,6 +322,9 @@ class ApcReferentielCompetences(db.Model, XMLModel): def _load_config_equivalences(self) -> dict: """Load config file ressources/referentiels/equivalences.yaml used to define equivalences between distinct referentiels + return a dict, with optional keys: + alias: list of equivalent names for speciality (eg SD == STID) + parcours: dict with equivalent parcours acronyms """ try: with open(REFCOMP_EQUIVALENCE_FILENAME, encoding="utf-8") as f: diff --git a/app/models/etudiants.py b/app/models/etudiants.py index a81ac73d..30e42d25 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -199,6 +199,11 @@ class Identite(models.ScoDocModel): @classmethod def get_etud(cls, etudid: int) -> "Identite": """Etudiant ou 404, cherche uniquement dans le département courant""" + if not isinstance(etudid, int): + try: + etudid = int(etudid) + except (TypeError, ValueError): + abort(404, "etudid invalide") if g.scodoc_dept: return cls.query.filter_by( id=etudid, dept_id=g.scodoc_dept_id diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 2bd32f2b..fc7df731 100755 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -884,21 +884,6 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str: """ ) - if can_edit_abs: - H.append( - f""" -
- """ - ) - H.append("") # /sem-groups-assi if partition_is_empty: H.append( diff --git a/app/scodoc/sco_photos.py b/app/scodoc/sco_photos.py index 5c4bfbdc..35b2ea12 100755 --- a/app/scodoc/sco_photos.py +++ b/app/scodoc/sco_photos.py @@ -96,13 +96,16 @@ def photo_portal_url(code_nip: str): return None -def get_etud_photo_url(etudid, size="small"): +def get_etud_photo_url(etudid, size="small", seed=None): + "L'URL scodoc vers la photo de l'étudiant" + kwargs = {"seed": seed} if seed else {} return ( url_for( "scolar.get_photo_image", scodoc_dept=g.scodoc_dept, etudid=etudid, size=size, + **kwargs, ) if has_request_context() else "" @@ -114,9 +117,11 @@ def etud_photo_url(etud: dict, size="small", fast=False) -> str: If ScoDoc doesn't have an image and a portal is configured, link to it. """ - photo_url = get_etud_photo_url(etud["etudid"], size=size) if fast: - return photo_url + return get_etud_photo_url(etud["etudid"], size=size) + photo_url = get_etud_photo_url( + etud["etudid"], size=size, seed=hash(etud.get("photo_filename")) + ) path = photo_pathname(etud["photo_filename"], size=size) if not path: # Portail ? @@ -374,7 +379,15 @@ def copy_portal_photo_to_fs(etudid: int): portal_timeout = sco_preferences.get_preference("portal_timeout") error_message = None try: - r = requests.get(url, timeout=portal_timeout) + r = requests.get( + url, + timeout=portal_timeout, + params={ + "nom": etud.nom or "", + "prenom": etud.prenom or "", + "civilite": etud.civilite, + }, + ) except requests.ConnectionError: error_message = "ConnectionError" except requests.Timeout: diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 0f152235..c4d55976 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1085,18 +1085,35 @@ span.spanlink:hover { } .trombi_box { - display: inline-block; - width: 110px; - vertical-align: top; margin-left: 5px; margin-top: 5px; + width: 140px; + /* Constant width for the box */ + display: inline-flex; + flex-direction: column; + /* Ensures trombi-photo is above trombi_legend */ + align-items: center; + /* Centers content horizontally */ } -span.trombi_legend { - display: inline-block; +.trombi-photo { + display: flex; + justify-content: center; + /* Centers image horizontally within the photo container */ + margin-bottom: 10px; + /* Adds some space between the photo and the legend */ } -span.trombi-photo { +.trombi-photo img { + width: auto; + /* Maintains aspect ratio */ + height: 120px; + /* Sets the height to 90px */ + max-width: 100%; + /* Ensures the image doesn't exceed the container's width */ +} + +/* span.trombi_legend { display: inline-block; } @@ -1106,7 +1123,9 @@ span.trombi_box a { span.trombi_box a img { display: inline-block; -} + height: 128px; + width: auto; +} */ .trombi_nom { display: block; diff --git a/app/templates/assiduites/pages/signal_assiduites_hebdo.j2 b/app/templates/assiduites/pages/signal_assiduites_hebdo.j2 index 8422b3b2..a312fb6c 100644 --- a/app/templates/assiduites/pages/signal_assiduites_hebdo.j2 +++ b/app/templates/assiduites/pages/signal_assiduites_hebdo.j2 @@ -214,7 +214,7 @@ ] // [0]=Lundi ... [6]=Dimanche -> à 00h00 //Une fonction d'action quand un bouton est cliqué - // 3 possibilités : + // 3 possibilités : // - assiduite_id = null -> créer nv assi avec état du bouton // - assiduite_id non null et bouton coché == etat assi -> suppression de l'assiduité // - assiduite_id non null et bouton coché != etat assi -> modification de l'assiduité @@ -418,14 +418,14 @@ // Peuplement des boutons en fonction des assiduités let boutons = ` - - ` if (!non_present) { - boutons = ``+boutons; } @@ -437,8 +437,8 @@ const deb = new Date(assi.date_debut); const fin = new Date(assi.date_fin); - // si dates == periode -> cocher bouton correspondant - // Sinon supprimer boutons et mettre case "rouge" + tooltip + // si dates == periode -> cocher bouton correspondant + // Sinon supprimer boutons et mettre case "rouge" + tooltip if (deb.isSame(morningPeriod.deb, "minutes") && fin.isSame(morningPeriod.fin, "minutes")) { let etat = assi.etat.toLowerCase(); @@ -468,7 +468,7 @@ const deb = new Date(assi.date_debut); const fin = new Date(assi.date_fin); - // si dates == periode -> cocher bouton correspondant + // si dates == periode -> cocher bouton correspondant // Sinon supprimer boutons et mettre case "rouge" + tooltip if (deb.isSame(afternoonPeriod.deb, "minutes") && fin.isSame(afternoonPeriod.fin, "minutes")) { @@ -504,7 +504,7 @@ let target = e.target; let parent = target.parentElement; - + let isCancelled = await actionButton(target, !target.checked); if (isCancelled) { e.preventDefault(); @@ -690,7 +690,7 @@ } } - + document.getElementById("text-matin").addEventListener("click", (e)=>{ e.preventDefault(); @@ -801,6 +801,7 @@ document.addEventListener("DOMContentLoaded", ()=>{ {{moduleimpl_select | safe}} + autre semaine