diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 98ecc093f96820ae5066cd146abb95e4fe3168da..338b92239a614561953bfd014900b76afb9f605d 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -208,7 +208,7 @@ class FormSemestre(models.ScoDocModel): return cls.query.filter_by(id=formsemestre_id).first_or_404() def sort_key(self) -> tuple: - """clé pour tris par ordre alphabétique + """clé pour tris par ordre de date_debut, le plus ancien en tête (pour avoir le plus récent d'abord, sort avec reverse=True)""" return (self.date_debut, self.semestre_id) diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index 1f980c14691980277d66a01bd3fcf86863415c2e..fda86ac4c77a6efac22b90b6809db98a93019a2b 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -79,21 +79,30 @@ def index_html(showcodes=0, showsemtable=0, export_table_formsemestres=False): else: html_table_formsemestres = None + current_formsemestres_by_modalite, modalites = ( + sco_modalites.group_formsemestres_by_modalite(current_formsemestres) + ) + return render_template( "scolar/index.j2", current_user=current_user, + current_formsemestres=current_formsemestres, + current_formsemestres_by_modalite=current_formsemestres_by_modalite, dept_name=sco_preferences.get_preference("DeptName"), - formsemestres=formsemestres, - html_current_formsemestres=_show_current_formsemestres( - current_formsemestres, showcodes + emptygroupicon=scu.icontag( + "emptygroupicon_img", title="Pas d'inscrits", border="0" ), + formsemestres=formsemestres, + groupicon=scu.icontag("groupicon_img", title="Inscrits", border="0"), html_table_formsemestres=html_table_formsemestres, locked_formsemestres=locked_formsemestres, + modalites=modalites, nb_locked=locked_formsemestres.count(), nb_user_accounts=sco_users.get_users_count(dept=g.scodoc_dept), page_title=f"ScoDoc {g.scodoc_dept}", Permission=Permission, scolar_news_summary=ScolarNews.scolar_news_summary_html(), + showcodes=showcodes, showsemtable=showsemtable, sco=ScoData(), ) @@ -116,6 +125,7 @@ def _convert_formsemestres_to_dicts( lockicon = "X" # génère liste de dict sems = [] + formsemestre: FormSemestre for formsemestre in formsemestres: nb_inscrits = len(formsemestre.inscriptions) formation = formsemestre.formation @@ -151,61 +161,6 @@ def _convert_formsemestres_to_dicts( return sems -def _show_current_formsemestres(formsemestres: Query, showcodes: bool) -> str: - """html div avec les formsemestres courants de la page d'accueil""" - - H = [] - if formsemestres.count(): - H.append("""<div class="scobox-title">Sessions en cours</div>""") - H.append(_sem_table(_convert_formsemestres_to_dicts(formsemestres, showcodes))) - else: - # aucun semestre courant: affiche aide - H.append( - """ - <div class="scobox-title">Aucune session en cours !</div> - <p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Formations</a>, - choisissez une formation, puis suivez le lien "<em>UE, modules, semestres</em>". - </p> - <p>Là, en bas de page, suivez le lien - "<em>Mettre en place un nouveau semestre de formation...</em>" - </p>""" - ) - return "\n".join(H) - - -def _sem_table(sems: list[dict]) -> str: - """Affiche liste des semestres, utilisée pour semestres en cours""" - tmpl = f"""<tr class="%(trclass)s">%(tmpcode)s - <td class="semicon">%(lockimg)s <a href="{ - url_for("notes.formsemestre_status", scodoc_dept=g.scodoc_dept)}?formsemestre_id=%(formsemestre_id)s#groupes">%(groupicon)s</a></td> - <td class="datesem">%(mois_debut)s <a title="%(session_id)s">-</a> %(mois_fin)s</td> - <td class="titresem"><a class="stdlink" href="{url_for("notes.formsemestre_status", scodoc_dept=g.scodoc_dept)}?formsemestre_id=%(formsemestre_id)s">%(titre_num)s</a> - <span class="respsem">(%(responsable_name)s)</span> - </td> - </tr> - """ - - # Liste des semestres, groupés par modalités - sems_by_mod, modalites = sco_modalites.group_sems_by_modalite(sems) - - H = ['<table class="listesems">'] - for modalite in modalites: - if len(modalites) > 1: - H.append('<tr><th colspan="3">%s</th></tr>' % modalite["titre"]) - - if sems_by_mod[modalite["modalite"]]: - cur_idx = sems_by_mod[modalite["modalite"]][0]["semestre_id"] - for sem in sems_by_mod[modalite["modalite"]]: - if cur_idx != sem["semestre_id"]: - sem["trclass"] = "firstsem" # separe les groupes de semestres - cur_idx = sem["semestre_id"] - else: - sem["trclass"] = "" - H.append(tmpl % sem) - H.append("</table>") - return "\n".join(H) - - def _sem_table_gt(formsemestres: Query, showcodes=False, fmt="html") -> GenTable: """Table des semestres Utilise une datatables. diff --git a/app/scodoc/sco_modalites.py b/app/scodoc/sco_modalites.py index 621373f71f3be4990034e7be05e55cd8857b4fde..075fa4476b52aa0607aa87ebe543354478eddb43 100644 --- a/app/scodoc/sco_modalites.py +++ b/app/scodoc/sco_modalites.py @@ -36,37 +36,45 @@ Elle n'est pas utilisée pour les parcours, ni pour rien d'autre import collections import app.scodoc.notesdb as ndb from app import log +from app.models import FormSemestre -def list_formsemestres_modalites(sems): +def list_formsemestres_modalites(formsemestres: list[FormSemestre]) -> list[dict]: """Liste ordonnée des modalités présentes dans ces formsemestres""" modalites = {} - for sem in sems: - if sem["modalite"] not in modalites: - m = do_modalite_list(args={"modalite": sem["modalite"]})[0] + for formsemestre in formsemestres: + if formsemestre.modalite not in modalites: + m = do_modalite_list(args={"modalite": formsemestre.modalite})[0] modalites[m["modalite"]] = m modalites = list(modalites.values()) modalites.sort(key=lambda x: x["numero"]) return modalites -def group_sems_by_modalite(sems: list[dict]): +def group_formsemestres_by_modalite( + formsemestres: list[FormSemestre], +) -> dict[str, list[FormSemestre]]: """Given the list of formsemestre, group them by modalite, sorted in each one by semestre id and date """ sems_by_mod = collections.defaultdict(list) - modalites = list_formsemestres_modalites(sems) - for modalite in modalites: - for sem in sems: - if sem["semestre_id"] < 0: # formations en un semestre - sem["sortkey"] = (-100 * sem["semestre_id"], sem["dateord"]) - else: - sem["sortkey"] = (sem["semestre_id"], sem["dateord"]) - if sem["modalite"] == modalite["modalite"]: - sems_by_mod[modalite["modalite"]].append(sem) + modalites = list_formsemestres_modalites(formsemestres) + sems_by_mod = { + modalite["modalite"]: [ + formsemestre + for formsemestre in formsemestres + if formsemestre.modalite == modalite["modalite"] + ] + for modalite in modalites + } # tri dans chaque modalité par indice de semestre et date debut for modalite in modalites: - sems_by_mod[modalite["modalite"]].sort(key=lambda x: x["sortkey"]) + sems_by_mod[modalite["modalite"]].sort( + key=lambda x: ( + x.semestre_id if x.semestre_id > 0 else -1000 * x.semestre_id, + x.date_debut, + ) + ) return sems_by_mod, modalites diff --git a/app/templates/scolar/index.j2 b/app/templates/scolar/index.j2 index 8b93dee0a95a4d4037e3132a266aa586d2b766fa..0e151691db9e1c589fbb33e3f80f6117908a6182 100644 --- a/app/templates/scolar/index.j2 +++ b/app/templates/scolar/index.j2 @@ -83,6 +83,70 @@ table.semlist tbody tr td.modalite { padding-right: 1em; } +div.modalite { + font-size: 16px; + font-weight: bold; +} +span.effectif { + display: inline-block; + min-width: 24px; + text-align: right; +} + +.cur-formsemestres { + width: max-content; /* Fits content, but respects max-width */ + max-width: 1024px; /* Maximum width */ + margin: 0 auto 0 0; /* Centers divs if they are narrower than 1024px */ +} +.cur-formsemestre { + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #ddd; + border-left: 1px solid #ddd; + border-right: 1px solid #ddd;; + background-color: rgb(246, 255, 254); + padding: 0px 8px 0px 8px; + margin: 0px; +} + +.cur-formsemestre.new-sem { + margin-top: 8px; + border-top: 1px solid #ddd;; +} + +.left-section { + display: flex; + align-items: center; +} + +.date { + display: flex; + flex-direction: column; + margin-left: 10px; + font-size: 12px; +} + +.cur-formsemestre .title { + flex-grow: 1; + text-align: left; + margin: 0 20px; + display: flex; + align-items: center; + font-size: 16px; +} + +.right-section { + display: flex; + flex-direction: column; + font-size: 14px; +} +.responsable { + font-weight: bold; + color: navy; +} + + </style> {# News #} @@ -103,7 +167,43 @@ table.semlist tbody tr td.modalite { {# Les semestres courants (cad non verrouillés) #} <div class="scobox"> - {{html_current_formsemestres|safe}} +{% if current_formsemestres.count() == 0 %} + <div class="scobox-title">Aucune session en cours !</div> + <p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Formations</a>, + choisissez une formation, puis suivez le lien "<em>UE, modules, semestres</em>". + </p> + <p>Là, en bas de page, suivez le lien + "<em>Mettre en place un nouveau semestre de formation...</em>" + </p> +{% else %} + <div class="scobox-title">Sessions en cours</div> + <div class="cur-formsemestres"> + {% for modalite in modalites %} + {% if modalites|length > 1 %} + <div class="modalite">{{modalite.titre}}</div> + {% endif %} + {% for formsemestre in current_formsemestres_by_modalite[modalite.modalite] %} + <div class="cur-formsemestre {{'new-sem' if loop.first or formsemestre.semestre_id != loop.previtem.semestre_id}}"> + <div class="left-section"> + {{groupicon|safe if formsemestre.inscriptions|length else emptygroupicon|safe}} + <div class="date"> + <div class="date-begin"><a title="{{formsemestre.session_id()}}">{{formsemestre.mois_debut()}}</a></div> + <div class="date-end">{{formsemestre.mois_fin()}}</div> + </div> + </div> + <div class="title"> + <a class="stdlink" href="{{ url_for("notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)}}">{{formsemestre.titre_num()}}</a> + </div> + <div class="right-section"> + <div class="responsable">{{formsemestre.responsables_str()}}</div> + <div class="effectif">{{formsemestre.inscriptions|length}} étuds</div> + </div> + </div> + {% endfor %} + {% endfor %} + </div> +{% endif %} </div> {# Table de tous les semestres #} diff --git a/sco_version.py b/sco_version.py index 144fe706edf81b9c50884e12adeaf5867febc4fd..e0361b0734dc9038373491253491c2f512bdd1aa 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.954" +SCOVERSION = "9.6.955" SCONAME = "ScoDoc"