diff --git a/app/models/events.py b/app/models/events.py index 4e3fcb2b..e499dc74 100644 --- a/app/models/events.py +++ b/app/models/events.py @@ -249,9 +249,10 @@ class ScolarNews(db.Model): news_list = cls.last_news(n=n) if not news_list: return "" + dept_news_url = url_for("scolar.dept_news", scodoc_dept=g.scodoc_dept) H = [ f"""
Dernières opérations
") + H.append("
") # Informations générales H.append( f"""
- Pour en savoir plus sur ScoDoc voir le site - scodoc.org. + Pour en savoir plus sur ScoDoc voir + scodoc.org
""" ) - H.append("") return "\n".join(H) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 00bac5c2..84ba4c37 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -247,7 +247,7 @@ class FormSemestre(models.ScoDocModel): def to_dict_api(self): """ - Un dict avec les informations sur le semestre destiné à l'api + Un dict avec les informations sur le semestre destinées à l'api """ d = dict(self.__dict__) d.pop("_sa_instance_state", None) diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index 2196ab8e..97fce15c 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -3,7 +3,7 @@ ############################################################################## # -# Gestion scolarite IUT +# ScoDoc # # Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved. # @@ -28,93 +28,119 @@ """Page accueil département (liste des semestres, etc) """ -from flask import g -from flask import url_for +from sqlalchemy import desc +from flask import g, url_for, render_template from flask_login import current_user +from flask_sqlalchemy.query import Query import app from app import log -from app.models import ScolarNews +from app.models import FormSemestre, ScolarNews import app.scodoc.sco_utils as scu from app.scodoc.gen_tables import GenTable from app.scodoc.sco_permissions import Permission -from app.scodoc import html_sco_header import app.scodoc.notesdb as ndb -from app.scodoc import sco_formsemestre -from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_modalites from app.scodoc import sco_preferences from app.scodoc import sco_users +from app.views import ScoData def index_html(showcodes=0, showsemtable=0): "Page accueil département (liste des semestres)" showcodes = int(showcodes) showsemtable = int(showsemtable) - H = [] - # News: - H.append(ScolarNews.scolar_news_summary_html()) + # Liste tous les formsemestres du dept, le plus récent d'abord + current_formsemestres = ( + FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id, etat=True) + .filter(FormSemestre.modalite != "EXT") + .order_by(desc(FormSemestre.date_debut)) + ) + locked_formsemestres = ( + FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id, etat=False) + .filter(FormSemestre.modalite != "EXT") + .order_by(desc(FormSemestre.date_debut)) + ) + formsemestres = ( + FormSemestre.query.filter_by(dept_id=g.scodoc_dept_id) + .filter(FormSemestre.modalite != "EXT") + .order_by(desc(FormSemestre.date_debut)) + ) + if showsemtable: # table de tous les formsemestres + html_table_formsemestres = _sem_table_gt( + formsemestres, showcodes=showcodes + ).html() + else: + html_table_formsemestres = None - # Avertissement de mise à jour: - H.append("""
""") + return render_template( + "scolar/index.j2", + current_user=current_user, + dept_name=sco_preferences.get_preference("DeptName"), + formsemestres=formsemestres, + html_current_formsemestres=_show_current_formsemestres( + current_formsemestres, showcodes + ), + html_table_formsemestres=html_table_formsemestres, + locked_formsemestres=locked_formsemestres, + 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(), + showsemtable=showsemtable, + sco=ScoData(), + ) - # Liste de toutes les sessions: - sems = sco_formsemestre.do_formsemestre_list() - cursems = [] # semestres "courants" - othersems = [] # autres (verrouillés) + +def _convert_formsemestres_to_dicts( + formsemestres: Query, showcodes: bool +) -> list[dict]: + """ """ # icon image: groupicon = scu.icontag("groupicon_img", title="Inscrits", border="0") emptygroupicon = scu.icontag( "emptygroupicon_img", title="Pas d'inscrits", border="0" ) lockicon = scu.icontag("lock32_img", title="verrouillé", border="0") - # Sélection sur l'etat du semestre - for sem in sems: - if sem["etat"] and sem["modalite"] != "EXT": - sem["lockimg"] = "" - cursems.append(sem) - else: - sem["lockimg"] = lockicon - othersems.append(sem) - # Responsable de formation: - sco_formsemestre.sem_set_responsable_name(sem) + # génère liste de dict + sems = [] + for formsemestre in formsemestres: + nb_inscrits = len(formsemestre.inscriptions) + sem = { + "anneescolaire": formsemestre.annee_scolaire(), + "bul_hide_xml": formsemestre.bul_hide_xml, + "dateord": formsemestre.date_debut, + "elt_annee_apo": formsemestre.elt_annee_apo, + "elt_sem_apo": formsemestre.elt_sem_apo, + "etapes_apo_str": formsemestre.etapes_apo_str(), + "formsemestre_id": formsemestre.id, + "groupicon": groupicon if nb_inscrits > 0 else emptygroupicon, + "lockimg": lockicon, + "modalite": formsemestre.modalite, + "mois_debut": formsemestre.mois_debut(), + "mois_fin": formsemestre.mois_fin(), + "nb_inscrits": nb_inscrits, + "responsable_name": formsemestre.responsables_str(), + "semestre_id": formsemestre.semestre_id, + "session_id": formsemestre.session_id(), + "titre_num": formsemestre.titre_num(), + "tmpcode": ( + f"{sem['formsemestre_id']}" if showcodes else "" + ), + } + sems.append(sem) + return sems - if showcodes: - sem["tmpcode"] = f"{sem['formsemestre_id']}" - else: - sem["tmpcode"] = "" - # Nombre d'inscrits: - args = {"formsemestre_id": sem["formsemestre_id"]} - ins = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(args=args) - nb = len(ins) # nb etudiants - sem["nb_inscrits"] = nb - if nb > 0: - sem["groupicon"] = groupicon - else: - sem["groupicon"] = emptygroupicon - # S'il n'y a pas d'utilisateurs dans la base, affiche message - if not sco_users.get_users_count(dept=g.scodoc_dept): - H.append( - """

Aucun utilisateur défini !

Pour définir des utilisateurs - passez par la page Utilisateurs. -
- Définissez au moins un utilisateur avec le rôle AdminXXX - (le responsable du département XXX). -

- """ - ) +def _show_current_formsemestres(formsemestres: Query, showcodes: bool) -> str: + """html div avec les formsemestres courants de la page d'accueil""" - H.append("""
""") - # Liste des formsemestres "courants" - if cursems: - H.append( - f""" -
Sessions en cours
- { _sem_table(cursems) } - """ - ) + H = [] + if formsemestres.count(): + H.append("""
Sessions en cours
""") + H.append(_sem_table(_convert_formsemestres_to_dicts(formsemestres, showcodes))) else: # aucun semestre courant: affiche aide H.append( @@ -127,125 +153,16 @@ def index_html(showcodes=0, showsemtable=0): "Mettre en place un nouveau semestre de formation..."

""" ) - - if showsemtable: - H.append( - f""" -
Semestres de {sco_preferences.get_preference("DeptName")}
- """ - ) - H.append(_sem_table_gt(sems, showcodes=showcodes).html()) - H.append("") - else: - H.append( - f""" -

Voir table des semestres (dont {len(othersems)} - verrouillé{'s' if len(othersems) else ''}) -

""" - ) - - H.append( - f""" -
- Chercher étape courante: - -
-
- """ - ) - # - H.append( - """ -
-
Gestion des étudiants
-
") - # - if current_user.has_permission(Permission.EditApogee): - H.append( - f""" -
-
Exports Apogée
- -
- """ - ) - # - H.append( - """ -
-
Assistance
- -
- """ - ) - # - return ( - html_sco_header.sco_header( - page_title=f"ScoDoc {g.scodoc_dept}", javascripts=["js/scolar_index.js"] - ) - + "\n".join(H) - + html_sco_header.sco_footer() - ) + return "\n".join(H) -def _sem_table(sems): +def _sem_table(sems: list[dict]) -> str: """Affiche liste des semestres, utilisée pour semestres en cours""" - tmpl = """%(tmpcode)s - %(lockimg)s %(groupicon)s + tmpl = f"""%(tmpcode)s + %(lockimg)s %(groupicon)s %(mois_debut)s - %(mois_fin)s - %(titre_num)s + %(titre_num)s (%(responsable_name)s) @@ -267,19 +184,26 @@ def _sem_table(sems): cur_idx = sem["semestre_id"] else: sem["trclass"] = "" - sem["notes_url"] = scu.NotesURL() H.append(tmpl % sem) H.append("") return "\n".join(H) -def _sem_table_gt(sems, showcodes=False): - """Nouvelle version de la table des semestres +def _sem_table_gt(formsemestres: Query, showcodes=False) -> GenTable: + """Table des semestres Utilise une datatables. """ - _style_sems(sems) + sems = _style_sems(_convert_formsemestres_to_dicts(formsemestres, showcodes)) + sems.sort( + key=lambda s: ( + -s["anneescolaire"], + s["semestre_id"] if s["semestre_id"] > 0 else -s["semestre_id"] * 1000, + s["modalite"], + ) + ) columns_ids = ( "lockimg", + "published", "semestre_id_n", "modalite", #'mois_debut', @@ -327,22 +251,35 @@ def _sem_table_gt(sems, showcodes=False): return tab -def _style_sems(sems): +def _style_sems(sems: list[dict]) -> list[dict]: """ajoute quelques attributs de présentation pour la table""" for sem in sems: - sem["notes_url"] = scu.NotesURL() - sem["_groupicon_target"] = ( - "%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s" - % sem + status_url = url_for( + "notes.formsemestre_status", + scodoc_dept=g.scodoc_dept, + formsemestre_id=sem["formsemestre_id"], ) + sem["_groupicon_target"] = status_url sem["_formsemestre_id_class"] = "blacktt" sem["dash_mois_fin"] = ' %(anneescolaire)s' % sem sem["_dash_mois_fin_class"] = "datesem" sem["titre_resp"] = ( - """%(titre_num)s - (%(responsable_name)s)""" - % sem + f"""{sem['titre_num']} ({sem['responsable_name']})""" ) + sem["published"] = ( + scu.icontag( + "hide_img", + border="0", + title="Bulletins NON publiés sur la passerelle étudiants", + ) + if sem["bul_hide_xml"] + else scu.icontag( + "eye_img", + border="0", + title="Bulletins publiés sur la passerelle étudiants", + ) + ) + sem["_css_row_class"] = "css_S%d css_M%s" % ( sem["semestre_id"], sem["modalite"], @@ -363,6 +300,7 @@ def _style_sems(sems): sem["_elt_sem_apo_td_attrs"] = ( f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """ ) + return sems def delete_dept(dept_id: int) -> str: diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 437969eb..bebe7fcc 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -494,7 +494,7 @@ def table_formsemestres( ): """Une table presentant des semestres""" for sem in sems: - sem_set_responsable_name(sem) + sem_set_responsable_name(sem) # TODO utiliser formsemestre.responsables_str() sem["_titre_num_target"] = url_for( "notes.formsemestre_status", scodoc_dept=g.scodoc_dept, diff --git a/app/scodoc/sco_modalites.py b/app/scodoc/sco_modalites.py index 805d0d48..621373f7 100644 --- a/app/scodoc/sco_modalites.py +++ b/app/scodoc/sco_modalites.py @@ -50,8 +50,8 @@ def list_formsemestres_modalites(sems): return modalites -def group_sems_by_modalite(sems): - """Given the list of fromsemestre, group them by modalite, +def group_sems_by_modalite(sems: list[dict]): + """Given the list of formsemestre, group them by modalite, sorted in each one by semestre id and date """ sems_by_mod = collections.defaultdict(list) diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 6987406e..eb6793e5 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -40,6 +40,10 @@ h3 { font-weight: bold; } +details > summary:first-of-type { + display: list-item!important; +} + div.container { margin-bottom: 24px; } @@ -592,10 +596,6 @@ table.listesems tr.firstsem td { padding-top: 0.8em; } -td.datesem { - font-size: 80%; - white-space: nowrap; -} h2.listesems { padding-top: 10px; @@ -683,27 +683,33 @@ table.semlist tbody tr td.modalite { } div#gtrcontent table.semlist tbody tr.css_S-1 td { - background-color: rgb(251, 250, 216); + background-color: rgb(211, 213, 255); } div#gtrcontent table.semlist tbody tr.css_S1 td { - background-color: rgb(92%, 95%, 94%); + background-color:#e9efef; } div#gtrcontent table.semlist tbody tr.css_S2 td { - background-color: rgb(214, 223, 236); + background-color: #d4ebd7; } div#gtrcontent table.semlist tbody tr.css_S3 td { - background-color: rgb(167, 216, 201); + background-color: #bedebe; } div#gtrcontent table.semlist tbody tr.css_S4 td { - background-color: rgb(131, 225, 140); + background-color: #afd7ad; +} +div#gtrcontent table.semlist tbody tr.css_S5 td { + background-color: #a0cd9a; +} +div#gtrcontent table.semlist tbody tr.css_S6 td { + background-color: #7dcf78; } div#gtrcontent table.semlist tbody tr.css_MEXT td { - color: #0b6e08; + color: #fefcdf; } /* ----- Liste des news ----- */ @@ -2019,7 +2025,8 @@ ul.ue_inscr_list li.etud { .sem-groups-partition .stdlink, .sem-groups-partition .stdlink:visited { color: black; - text-decoration-style: dashed; + text-decoration-style: dotted; + text-underline-offset: 3px; } .sem-groups-list .stdlink, .sem-groups-list .stdlink:visited { color:rgb(0, 0, 192); diff --git a/app/templates/scolar/index.j2 b/app/templates/scolar/index.j2 new file mode 100644 index 00000000..09c3e9de --- /dev/null +++ b/app/templates/scolar/index.j2 @@ -0,0 +1,127 @@ +{# page accueil département #} + +{% extends "sco_page.j2" %} + +{% block app_content %} + + +{# News #} +{{scolar_news_summary|safe}} + +{# Avertissement de mise à jour: #} +
+ +{% if nb_user_accounts == 0 %} +

Aucun utilisateur défini !

+

Pour définir des utilisateurs passez par la page Utilisateurs.
+ Définissez au moins un utilisateur avec le rôle AdminXXX + (le responsable du département XXX). +

+{% endif %} + +{# Les semestres courants (cad non verrouillés) #} +
+ {{html_current_formsemestres|safe}} +
+ +{# Table de tous les semestres #} + +{% if html_table_formsemestres %} +
+ + Semestres de {{dept_name}} + + {{ html_table_formsemestres|safe }} +
+{% else %} +

Voir table des {{formsemestres.count()}} semestres + {% if nb_locked %} + (dont {{nb_locked}} verrouillé{{'s' if nb_locked > 1 else ''}}) + {%endif%} + +

+{% endif %} + + +{# Recherche d'un semestre par code Apogée #} +
+ Chercher étape courante: + +
+ +{# Gestion des étudiants #} +
+
Gestion des étudiants
+ +
+ +{# Apogée #} +{% if current_user.has_permission(Permission.EditApogee) %} +
+
Exports Apogée
+ +
+{% endif %} + +{# Assistance #} +
+
Assistance
+ +
+ +{% endblock app_content %} + +{% block scripts %} +{{ super() }} + +{% endblock scripts %} \ No newline at end of file