diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py index 5e102c834..422dc6760 100644 --- a/app/scodoc/gen_tables.py +++ b/app/scodoc/gen_tables.py @@ -676,6 +676,7 @@ class GenTable: fmt="html", page_title="", filename=None, + cssstyles=[], javascripts=[], with_html_headers=True, publish=True, @@ -696,6 +697,7 @@ class GenTable: H.append( self.html_header or html_sco_header.sco_header( + cssstyles=cssstyles, page_title=page_title, javascripts=javascripts, init_qtip=init_qtip, @@ -721,7 +723,7 @@ class GenTable: ) else: return pdf_doc - elif fmt == "xls" or fmt == "xlsx": # dans les 2 cas retourne du xlsx + elif fmt in ("xls", "xlsx"): # dans les 2 cas retourne du xlsx xls = self.excel() if publish: return scu.send_file( @@ -730,8 +732,7 @@ class GenTable: suffix=scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE, ) - else: - return xls + return xls elif fmt == "text": return self.text() elif fmt == "csv": diff --git a/app/scodoc/sco_groups_view.py b/app/scodoc/sco_groups_view.py index ebfffab38..707a41af0 100644 --- a/app/scodoc/sco_groups_view.py +++ b/app/scodoc/sco_groups_view.py @@ -453,6 +453,10 @@ class DisplayedGroupsInfos: for i in to_remove: del T[i] + def get_etudids(self) -> set[int]: + "Les etudids des groupes choisis" + return {member["etudid"] for member in self.members} + def get_form_elem(self): """html hidden input with groups""" H = [] diff --git a/app/scodoc/sco_lycee.py b/app/scodoc/sco_lycee.py index da274dc9f..d9c27633b 100644 --- a/app/scodoc/sco_lycee.py +++ b/app/scodoc/sco_lycee.py @@ -34,22 +34,28 @@ from operator import itemgetter from flask import url_for, g, request import app -import app.scodoc.sco_utils as scu -from app.scodoc import html_sco_header -from app.scodoc import sco_formsemestre -from app.scodoc import sco_preferences -from app.scodoc import sco_report -from app.scodoc import sco_etud -import sco_version +from app.scodoc import ( + html_sco_header, + sco_formsemestre, + sco_groups_view, + sco_preferences, + sco_report, + sco_etud, +) +from app.models import FormSemestre from app.scodoc.gen_tables import GenTable +import app.scodoc.sco_utils as scu +import sco_version def formsemestre_table_etuds_lycees( - formsemestre_id, group_lycees=True, only_primo=False + formsemestre: FormSemestre, groups_infos, group_lycees=True, only_primo=False ): """Récupère liste d'etudiants avec etat et decision.""" - sem = sco_formsemestre.get_formsemestre(formsemestre_id) - etuds = sco_report.tsp_etud_list(formsemestre_id, only_primo=only_primo)[0] + sem = sco_formsemestre.get_formsemestre(formsemestre.id) + etuds = sco_report.tsp_etud_list( + formsemestre.id, groups_infos=groups_infos, only_primo=only_primo + )[0] if only_primo: primostr = "primo-entrants du " else: @@ -59,7 +65,7 @@ def formsemestre_table_etuds_lycees( etuds, group_lycees, title, - sco_preferences.SemPreferences(formsemestre_id), + sco_preferences.SemPreferences(formsemestre.id), ) @@ -180,13 +186,20 @@ def _table_etuds_lycees(etuds, group_lycees, title, preferences, no_links=False) def formsemestre_etuds_lycees( formsemestre_id, + group_ids: list[int] = None, # si indiqué, ne prend que ces groupes fmt="html", only_primo=False, no_grouping=False, ): """Table des lycées d'origine""" + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, + formsemestre_id=formsemestre.id, + select_all_when_unspecified=True, + ) tab, etuds_by_lycee = formsemestre_table_etuds_lycees( - formsemestre_id, only_primo=only_primo, group_lycees=not no_grouping + formsemestre, groups_infos, only_primo=only_primo, group_lycees=not no_grouping ) tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id) if only_primo: @@ -196,13 +209,19 @@ def formsemestre_etuds_lycees( t = tab.make_page(fmt=fmt, with_html_headers=False) if fmt != "html": return t - F = [sco_report.tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt)] + F = [ + sco_report.tsp_form_primo_group( + only_primo, no_grouping, formsemestre_id, fmt, groups_infos=groups_infos + ) + ] H = [ html_sco_header.sco_header( page_title=tab.page_title, init_google_maps=True, init_qtip=True, - javascripts=["js/etud_info.js", "js/map_lycees.js"], + cssstyles=sco_groups_view.CSSSTYLES, + javascripts=sco_groups_view.JAVASCRIPTS + + ["js/etud_info.js", "js/map_lycees.js"], ), """

Lycées d'origine des étudiants

""", "\n".join(F), diff --git a/app/scodoc/sco_report.py b/app/scodoc/sco_report.py index 75caad140..cc66ec071 100644 --- a/app/scodoc/sco_report.py +++ b/app/scodoc/sco_report.py @@ -40,6 +40,7 @@ from operator import itemgetter from flask import url_for, g, request import pydot +from app import log from app.but import jury_but from app.comp import res_sem from app.comp.res_compat import NotesTableCompat @@ -47,18 +48,21 @@ from app.models import FormSemestre, ScolarAutorisationInscription from app.models import FormationModalite from app.models.etudiants import Identite -import app.scodoc.sco_utils as scu -from app.scodoc import notesdb as ndb -from app.scodoc import html_sco_header -from app.scodoc import codes_cursus -from app.scodoc import sco_etud -from app.scodoc import sco_formsemestre -from app.scodoc import sco_formsemestre_inscriptions -from app.scodoc import sco_preferences -import sco_version +from app.scodoc import ( + codes_cursus, + html_sco_header, + sco_etud, + sco_formsemestre, + sco_formsemestre_inscriptions, + sco_groups_view, + sco_preferences, +) from app.scodoc.gen_tables import GenTable -from app import log from app.scodoc.codes_cursus import code_semestre_validant +from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc import notesdb as ndb +import app.scodoc.sco_utils as scu +import sco_version MAX_ETUD_IN_DESCR = 20 @@ -68,21 +72,25 @@ LEGENDES_CODES_BUT = { } -def formsemestre_etuds_stats(sem: dict, only_primo=False): +def formsemestre_etuds_stats( + formsemestre: FormSemestre, + only_primo=False, + groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None, +): """Récupère liste d'etudiants avec etat et decision.""" - formsemestre: FormSemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - - T = nt.get_table_moyennes_triees() - + etudids = groups_infos.get_etudids() if groups_infos else set() + rows = nt.get_table_moyennes_triees() # Décisions de jury BUT pour les semestres pairs seulement jury_but_mode = ( formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0 ) # Construit liste d'étudiants du semestre avec leur decision etuds = [] - for t in T: + for t in rows: etudid = t[-1] + if etudids and etudid not in etudids: + continue etudiant = Identite.get_etud(etudid) etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] etud["annee_admission"] = etud["annee"] # plus explicite @@ -96,7 +104,7 @@ def formsemestre_etuds_stats(sem: dict, only_primo=False): etud["codedecision"] = "(nd)" # pas de decision jury # Ajout devenir (autorisations inscriptions), utile pour stats passage aut_list = ScolarAutorisationInscription.query.filter_by( - etudid=etudid, origin_formsemestre_id=sem["formsemestre_id"] + etudid=etudid, origin_formsemestre_id=formsemestre.id ).all() autorisations = [f"S{a.semestre_id}" for a in aut_list] autorisations.sort() @@ -115,27 +123,27 @@ def formsemestre_etuds_stats(sem: dict, only_primo=False): bs.append(etud["specialite"]) etud["bac-specialite"] = " ".join(bs) # - if (not only_primo) or is_primo_etud(etud, sem): + if (not only_primo) or is_primo_etud(etud, formsemestre): etuds.append(etud) return etuds -def is_primo_etud(etud: dict, sem: dict): +def is_primo_etud(etud: dict, formsemestre: FormSemestre): """Determine si un (filled) etud a été inscrit avant ce semestre. Regarde la liste des semestres dans lesquels l'étudiant est inscrit. Si semestre pair, considère comme primo-entrants ceux qui étaient primo dans le précédent (S_{2n-1}). """ - debut_cur = sem["date_debut_iso"] + debut_cur_iso = formsemestre.date_debut.isoformat() # si semestre impair et sem. précédent contigu, recule date debut if ( (len(etud["sems"]) > 1) - and (sem["semestre_id"] % 2 == 0) - and (etud["sems"][1]["semestre_id"] == (sem["semestre_id"] - 1)) + and (formsemestre.semestre_id % 2 == 0) + and (etud["sems"][1]["semestre_id"] == (formsemestre.semestre_id - 1)) ): - debut_cur = etud["sems"][1]["date_debut_iso"] + debut_cur_iso = etud["sems"][1]["date_debut_iso"] for s in etud["sems"]: # le + recent d'abord - if s["date_debut_iso"] < debut_cur: + if s["date_debut_iso"] < debut_cur_iso: return False return True @@ -274,22 +282,6 @@ def formsemestre_report( return tab -# def formsemestre_report_bacs(formsemestre_id, fmt='html'): -# """ -# Tableau sur résultats par type de bac -# """ -# sem = sco_formsemestre.get_formsemestre( formsemestre_id) -# title = 'Statistiques bacs ' + sem['titreannee'] -# etuds = formsemestre_etuds_stats(sem) -# tab = formsemestre_report(formsemestre_id, etuds, -# category='bac', result='codedecision', -# category_name='Bac', -# title=title) -# return tab.make_page( -# title = """

Résultats de %(titreannee)s

""" % sem, -# fmt=fmt, page_title = title) - - def formsemestre_report_counts( formsemestre_id: int, fmt="html", @@ -297,6 +289,7 @@ def formsemestre_report_counts( result: str = None, allkeys: bool = False, only_primo: bool = False, + group_ids: list[int] = None, # si indiqué, ne prend que ces groupes ): """ Tableau comptage avec choix des categories @@ -307,8 +300,12 @@ def formsemestre_report_counts( si vrai, toutes les valeurs présentes dans les données sinon liste prédéfinie (voir ci-dessous) """ - formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) - sem = sco_formsemestre.get_formsemestre(formsemestre_id) + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, + formsemestre_id=formsemestre.id, + select_all_when_unspecified=True, + ) # Décisions de jury BUT pour les semestres pairs seulement jury_but_mode = ( formsemestre.formation.is_apc() and formsemestre.semestre_id % 2 == 0 @@ -319,7 +316,9 @@ def formsemestre_report_counts( category_name = category.capitalize() title = "Comptages " + category_name - etuds = formsemestre_etuds_stats(sem, only_primo=only_primo) + etuds = formsemestre_etuds_stats( + formsemestre, groups_infos=groups_infos, only_primo=only_primo + ) tab = formsemestre_report( formsemestre_id, etuds, @@ -329,7 +328,7 @@ def formsemestre_report_counts( title=title, only_primo=only_primo, ) - if not etuds: + if len(formsemestre.inscriptions) == 0: F = ["""

Aucun étudiant

"""] else: if allkeys: @@ -357,9 +356,10 @@ def formsemestre_report_counts( keys += ["nb_rcue_valides", "decision_annee"] keys.sort(key=scu.heterogeneous_sorting_key) F = [ - """

- Colonnes: + """ ] for k in keys: if k == result: @@ -381,30 +381,38 @@ def formsemestre_report_counts( '' % (k, selected, LEGENDES_CODES_BUT.get(k, k)) ) - F.append("") - if only_primo: - checked = 'checked="1"' - else: - checked = "" F.append( - '
Restreindre aux primo-entrants' - % checked + f""" + +

+ + Restreindre aux primo-entrants + + Restreindre au(x) groupe(s) : + {sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True) + if groups_infos else ''} + + +
+
+ """ ) - F.append( - '' % formsemestre_id - ) - F.append("

") - t = tab.make_page( - title="""

Comptes croisés

""", + tableau = tab.make_page( fmt=fmt, + title="""

Comptes croisés

""", with_html_headers=False, ) if fmt != "html": - return t + return tableau H = [ - html_sco_header.sco_header(page_title=title), - t, + html_sco_header.sco_header( + cssstyles=sco_groups_view.CSSSTYLES, + javascripts=sco_groups_view.JAVASCRIPTS, + page_title=title, + ), + tableau, "\n".join(F), """

Le tableau affiche le nombre d'étudiants de ce semestre dans chacun des cas choisis: à l'aide des deux menus, vous pouvez choisir les catégories utilisées @@ -418,7 +426,8 @@ def formsemestre_report_counts( # -------------------------------------------------------------------------- def table_suivi_cohorte( - formsemestre_id, + formsemestre: FormSemestre, + groups_infos, percent=False, bac="", # selection sur type de bac bacspecialite="", @@ -441,9 +450,8 @@ def table_suivi_cohorte( Determination des dates: on regroupe les semestres commençant à des dates proches """ - sem = sco_formsemestre.get_formsemestre( - formsemestre_id - ) # sem est le semestre origine + sem = sco_formsemestre.get_formsemestre(formsemestre.id) + # sem est le semestre origine t0 = time.time() def logt(op): @@ -452,12 +460,12 @@ def table_suivi_cohorte( logt("table_suivi_cohorte: start") # 1-- Liste des semestres posterieurs dans lesquels ont été les etudiants de sem - formsemestre = FormSemestre.get_formsemestre(formsemestre_id) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - etudids = nt.get_etudids() - + etudids_inscrits = {ins.etudid for ins in formsemestre.inscriptions} + etudids_groups = groups_infos.get_etudids() + etudids = etudids_inscrits.intersection(etudids_groups) logt("A: orig etuds set") - S = {formsemestre_id: sem} # ensemble de formsemestre_id + S = {formsemestre.id: sem} # ensemble de formsemestre_id orig_set = set() # ensemble d'etudid du semestre d'origine bacs = set() bacspecialites = set() @@ -479,7 +487,7 @@ def table_suivi_cohorte( ) and (not civilite or (civilite == etud["civilite"])) and (not statut or (statut == etud["statut"])) - and (not only_primo or is_primo_etud(etud, sem)) + and (not only_primo or is_primo_etud(etud, formsemestre)) ): orig_set.add(etudid) # semestres suivants: @@ -524,17 +532,15 @@ def table_suivi_cohorte( s["nb_dipl"] = nb_dipl # 3-- Regroupe les semestres par date de debut - P = [] # liste de periodsem - class PeriodSem: - pass + def __init__(self, datedebut: datetime.datetime, sems: list[dict]): + self.datedebut = datedebut + self.sems = sems # semestre de depart: - porigin = PeriodSem() d, m, y = [int(x) for x in sem["date_debut"].split("/")] - porigin.datedebut = datetime.datetime(y, m, d) - porigin.sems = [sem] - + porigin = PeriodSem(datetime.datetime(y, m, d), [sem]) + P = [] # liste de periodsem # tolerance = datetime.timedelta(days=45) for s in sems: @@ -545,9 +551,7 @@ def table_suivi_cohorte( merged = True break if not merged: - p = PeriodSem() - p.datedebut = s["date_debut_dt"] - p.sems = [s] + p = PeriodSem(s["date_debut_dt"], [s]) P.append(p) # 4-- regroupe par indice de semestre S_i @@ -602,7 +606,7 @@ def table_suivi_cohorte( L.append(d) # Compte nb de démissions et de ré-orientation par période logt("D: cout dems reos") - sem["dems"], sem["reos"] = _count_dem_reo(formsemestre_id, sem["members"]) + sem["dems"], sem["reos"] = _count_dem_reo(formsemestre.id, sem["members"]) for p in P: p.dems = set() p.reos = set() @@ -703,7 +707,7 @@ def table_suivi_cohorte( caption="Suivi cohorte " + pp + sem["titreannee"] + dbac, page_title="Suivi cohorte " + sem["titreannee"], html_class="table_cohorte", - preferences=sco_preferences.SemPreferences(formsemestre_id), + preferences=sco_preferences.SemPreferences(formsemestre.id), ) # Explication: liste des semestres associés à chaque date if not P: @@ -713,13 +717,10 @@ def table_suivi_cohorte( else: expl = ["

Semestres associés à chaque date:

") return ( @@ -737,6 +738,7 @@ def table_suivi_cohorte( def formsemestre_suivi_cohorte( formsemestre_id, fmt="html", + group_ids: list[int] = None, # si indiqué, ne prend que ces groupes percent=1, bac="", bacspecialite="", @@ -747,9 +749,19 @@ def formsemestre_suivi_cohorte( only_primo=False, ) -> str: """Affiche suivi cohortes par numero de semestre""" - annee_bac = str(annee_bac or "") - annee_admission = str(annee_admission or "") - percent = int(percent) + try: + annee_bac = str(annee_bac or "") + annee_admission = str(annee_admission or "") + percent = int(percent) + except ValueError as exc: + raise ScoValueError("formsemestre_suivi_cohorte: argument invalide") from exc + + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, + formsemestre_id=formsemestre.id, + select_all_when_unspecified=True, + ) ( tab, expl, @@ -760,7 +772,8 @@ def formsemestre_suivi_cohorte( civilites, statuts, ) = table_suivi_cohorte( - formsemestre_id, + formsemestre, + groups_infos=groups_infos, percent=percent, bac=bac, bacspecialite=bacspecialite, @@ -772,7 +785,7 @@ def formsemestre_suivi_cohorte( ) tab.base_url = ( "%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s" - % (request.base_url, formsemestre_id, percent, bac, bacspecialite, civilite) + % (request.base_url, formsemestre.id, percent, bac, bacspecialite, civilite) ) if only_primo: tab.base_url += "&only_primo=on" @@ -783,25 +796,29 @@ def formsemestre_suivi_cohorte( base_url = request.base_url burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % ( base_url, - formsemestre_id, + formsemestre.id, bac, bacspecialite, civilite, statut, ) if percent: - pplink = '

Afficher les résultats bruts

' % burl + pplink = f"""

Afficher les résultats bruts

""" else: - pplink = ( - '

Afficher les résultats en pourcentages

' - % burl - ) + pplink = f"""

Afficher les résultats en pourcentages

""" H = [ - html_sco_header.sco_header(page_title=tab.page_title), + html_sco_header.sco_header( + cssstyles=sco_groups_view.CSSSTYLES, + javascripts=sco_groups_view.JAVASCRIPTS, + page_title=tab.page_title, + ), """

Suivi cohorte: devenir des étudiants de ce semestre

""", _gen_form_selectetuds( - formsemestre_id, + formsemestre.id, + groups_infos=groups_infos, only_primo=only_primo, bac=bac, bacspecialite=bacspecialite, @@ -854,6 +871,7 @@ def _gen_form_selectetuds( annee_admissions=None, civilites=None, statuts=None, + groups_infos: sco_groups_view.DisplayedGroupsInfos = None, ): """HTML form pour choix criteres selection etudiants""" annee_bacs = annee_bacs or [] @@ -877,9 +895,10 @@ def _gen_form_selectetuds( else: selected = 'selected="selected"' F = [ - f"""
-

Bac: + """ ] for b in bacs: @@ -894,7 +913,8 @@ def _gen_form_selectetuds( else: selected = 'selected="selected"' F.append( - f"""  Bac/Specialité: """ ) @@ -938,17 +958,24 @@ def _gen_form_selectetuds( else: selected = "" F.append(f'') - F.append("") F.append( - f"""
- Restreindre aux primo-entrants - - + f""" + +

+ Restreindre aux primo-entrants -

+ + Restreindre au(x) groupe(s) : + {sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True) + if groups_infos else ''} + + + +
+
""" ) @@ -1002,17 +1029,6 @@ def _count_dem_reo(formsemestre_id, etudids): return dems, reos -"""OLDGEA: -27s pour S1 F.I. classique Semestre 1 2006-2007 -B 2.3s -C 5.6s -D 5.9s -Z 27s => cache des semestres pour nt - -à chaud: 3s -B: etuds sets: 2.4s => lent: N x getEtudInfo (non caché) -""" - EXP_LIC = re.compile(r"licence", re.I) EXP_LPRO = re.compile(r"professionnelle", re.I) @@ -1125,6 +1141,7 @@ def get_code_cursus_etud( def tsp_etud_list( formsemestre_id, + groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None, only_primo=False, bac="", # selection sur type de bac bacspecialite="", @@ -1136,11 +1153,13 @@ def tsp_etud_list( """Liste des etuds a considerer dans table suivi cursus ramene aussi ensembles des bacs, genres, statuts de (tous) les etudiants """ - # log('tsp_etud_list(%s, bac="%s")' % (formsemestre_id,bac)) - sem = sco_formsemestre.get_formsemestre(formsemestre_id) formsemestre = FormSemestre.get_formsemestre(formsemestre_id) - nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - etudids = nt.get_etudids() + etudids_inscrits = {ins.etudid for ins in formsemestre.inscriptions} + if groups_infos: + etudids_groups = groups_infos.get_etudids() + etudids = etudids_inscrits.intersection(etudids_groups) + else: + etudids = etudids_inscrits etuds = [] bacs = set() bacspecialites = set() @@ -1162,7 +1181,7 @@ def tsp_etud_list( ) and (not civilite or (civilite == etud["civilite"])) and (not statut or (statut == etud["statut"])) - and (not only_primo or is_primo_etud(etud, sem)) + and (not only_primo or is_primo_etud(etud, formsemestre)) ): etuds.append(etud) @@ -1173,7 +1192,6 @@ def tsp_etud_list( civilites.add(etud["civilite"]) if etud["statut"]: # ne montre pas les statuts non renseignés statuts.add(etud["statut"]) - # log('tsp_etud_list: %s etuds' % len(etuds)) return etuds, bacs, bacspecialites, annee_bacs, annee_admissions, civilites, statuts @@ -1290,31 +1308,30 @@ def table_suivi_cursus(formsemestre_id, only_primo=False, grouped_parcours=True) return tab -def tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt): - """Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees""" - F = ["""
""" % request.base_url] - if only_primo: - checked = 'checked="1"' - else: - checked = "" - F.append( - 'Restreindre aux primo-entrants' - % checked - ) - if no_grouping: - checked = 'checked="1"' - else: - checked = "" - F.append( - 'Lister chaque étudiant' - % checked - ) - F.append( - '' % formsemestre_id - ) - F.append('' % fmt) - F.append("""
""") - return "\n".join(F) +def tsp_form_primo_group( + only_primo, no_grouping, formsemestre_id, fmt, groups_infos=None +) -> str: + """Element de formulaire pour choisir si restriction aux primos entrants, + groupement par lycees et groupes + """ + primo_checked = 'checked="1"' if only_primo else "" + no_grouping_checked = 'checked="1"' if no_grouping else "" + return f""" +
+ Restreindre aux primo-entrants + Lister chaque étudiant + + + Restreindre au(x) groupe(s) : + {sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True) + if groups_infos else ''} + + + +
+ """ def formsemestre_suivi_cursus( @@ -1337,7 +1354,11 @@ def formsemestre_suivi_cursus( t = tab.make_page(fmt=fmt, with_html_headers=False) if fmt != "html": return t - F = [tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, fmt)] + F = [ + tsp_form_primo_group( + only_primo, no_grouping, formsemestre_id, fmt, groups_infos=None + ) + ] H = [ html_sco_header.sco_header( @@ -1356,6 +1377,7 @@ def formsemestre_suivi_cursus( # ------------- def graph_cursus( formsemestre_id, + groups_infos: sco_groups_view.DisplayedGroupsInfos | None = None, fmt="svg", only_primo=False, bac="", # selection sur type de bac @@ -1376,6 +1398,7 @@ def graph_cursus( statuts, ) = tsp_etud_list( formsemestre_id, + groups_infos=groups_infos, only_primo=only_primo, bac=bac, bacspecialite=bacspecialite, @@ -1606,6 +1629,7 @@ def graph_cursus( def formsemestre_graph_cursus( formsemestre_id, + group_ids: list[int] = None, # si indiqué, ne prend que ces groupes fmt="html", only_primo=False, bac="", # selection sur type de bac @@ -1619,7 +1643,12 @@ def formsemestre_graph_cursus( """Graphe suivi cohortes""" annee_bac = str(annee_bac or "") annee_admission = str(annee_admission or "") - # log("formsemestre_graph_cursus") + formsemestre = FormSemestre.get_formsemestre(formsemestre_id) + groups_infos = sco_groups_view.DisplayedGroupsInfos( + group_ids, + formsemestre_id=formsemestre.id, + select_all_when_unspecified=True, + ) sem = sco_formsemestre.get_formsemestre(formsemestre_id) if fmt == "pdf": ( @@ -1633,6 +1662,7 @@ def formsemestre_graph_cursus( statuts, ) = graph_cursus( formsemestre_id, + groups_infos=groups_infos, fmt="pdf", only_primo=only_primo, bac=bac, @@ -1657,6 +1687,7 @@ def formsemestre_graph_cursus( statuts, ) = graph_cursus( formsemestre_id, + groups_infos=groups_infos, fmt="png", only_primo=only_primo, bac=bac, @@ -1695,6 +1726,7 @@ def formsemestre_graph_cursus( statuts, ) = graph_cursus( formsemestre_id, + groups_infos=groups_infos, only_primo=only_primo, bac=bac, bacspecialite=bacspecialite, @@ -1706,14 +1738,17 @@ def formsemestre_graph_cursus( H = [ html_sco_header.sco_header( + cssstyles=sco_groups_view.CSSSTYLES, + javascripts=sco_groups_view.JAVASCRIPTS, page_title="Graphe cursus de %(titreannee)s" % sem, no_side_bar=True, ), """

Cursus des étudiants de ce semestre

""", doc, - "

%d étudiants sélectionnés

" % len(etuds), + f"

{len(etuds)} étudiants sélectionnés

", _gen_form_selectetuds( formsemestre_id, + groups_infos=groups_infos, only_primo=only_primo, bac=bac, bacspecialite=bacspecialite, @@ -1743,6 +1778,10 @@ def formsemestre_graph_cursus( passant d'un semestre à l'autre (s'il y en a moins de {MAX_ETUD_IN_DESCR}, vous pouvez visualiser leurs noms en passant le curseur sur le chiffre).

+

+ Le menu Restreindre au(x) groupe(s) permet de restreindre l'étude aux + étudiants appartenant aux groupes indiqués dans le semestre d'origine. +

""", html_sco_header.sco_footer(), ] diff --git a/app/static/css/gt_table.css b/app/static/css/gt_table.css index d40c493c4..075fb3b83 100644 --- a/app/static/css/gt_table.css +++ b/app/static/css/gt_table.css @@ -141,111 +141,111 @@ table.dataTable.with-highlight tr:hover td { background-color: rgba(255, 255, 0, 0.415); } -table.dataTable.order-column tbody tr > .sorting_1, -table.dataTable.order-column tbody tr > .sorting_2, -table.dataTable.order-column tbody tr > .sorting_3, -table.dataTable.display tbody tr > .sorting_1, -table.dataTable.display tbody tr > .sorting_2, -table.dataTable.display tbody tr > .sorting_3 { +table.dataTable.order-column tbody tr>.sorting_1, +table.dataTable.order-column tbody tr>.sorting_2, +table.dataTable.order-column tbody tr>.sorting_3, +table.dataTable.display tbody tr>.sorting_1, +table.dataTable.display tbody tr>.sorting_2, +table.dataTable.display tbody tr>.sorting_3 { background-color: #f9f9f9; } -table.dataTable.order-column tbody tr.selected > .sorting_1, -table.dataTable.order-column tbody tr.selected > .sorting_2, -table.dataTable.order-column tbody tr.selected > .sorting_3, -table.dataTable.display tbody tr.selected > .sorting_1, -table.dataTable.display tbody tr.selected > .sorting_2, -table.dataTable.display tbody tr.selected > .sorting_3 { +table.dataTable.order-column tbody tr.selected>.sorting_1, +table.dataTable.order-column tbody tr.selected>.sorting_2, +table.dataTable.order-column tbody tr.selected>.sorting_3, +table.dataTable.display tbody tr.selected>.sorting_1, +table.dataTable.display tbody tr.selected>.sorting_2, +table.dataTable.display tbody tr.selected>.sorting_3 { background-color: #acbad4; } -table.dataTable.display tbody tr.odd > .sorting_1, -table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 { +table.dataTable.display tbody tr.odd>.sorting_1, +table.dataTable.order-column.stripe tbody tr.odd>.sorting_1 { background-color: #f1f1f1; } -table.dataTable.display tbody tr.odd > .sorting_2, -table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 { +table.dataTable.display tbody tr.odd>.sorting_2, +table.dataTable.order-column.stripe tbody tr.odd>.sorting_2 { background-color: #f3f3f3; } -table.dataTable.display tbody tr.odd > .sorting_3, -table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 { +table.dataTable.display tbody tr.odd>.sorting_3, +table.dataTable.order-column.stripe tbody tr.odd>.sorting_3 { background-color: whitesmoke; } -table.dataTable.display tbody tr.odd.selected > .sorting_1, -table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 { +table.dataTable.display tbody tr.odd.selected>.sorting_1, +table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1 { background-color: #a6b3cd; } -table.dataTable.display tbody tr.odd.selected > .sorting_2, -table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 { +table.dataTable.display tbody tr.odd.selected>.sorting_2, +table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2 { background-color: #a7b5ce; } -table.dataTable.display tbody tr.odd.selected > .sorting_3, -table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 { +table.dataTable.display tbody tr.odd.selected>.sorting_3, +table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3 { background-color: #a9b6d0; } -table.dataTable.display tbody tr.even > .sorting_1, -table.dataTable.order-column.stripe tbody tr.even > .sorting_1 { +table.dataTable.display tbody tr.even>.sorting_1, +table.dataTable.order-column.stripe tbody tr.even>.sorting_1 { background-color: #f9f9f9; } -table.dataTable.display tbody tr.even > .sorting_2, -table.dataTable.order-column.stripe tbody tr.even > .sorting_2 { +table.dataTable.display tbody tr.even>.sorting_2, +table.dataTable.order-column.stripe tbody tr.even>.sorting_2 { background-color: #fbfbfb; } -table.dataTable.display tbody tr.even > .sorting_3, -table.dataTable.order-column.stripe tbody tr.even > .sorting_3 { +table.dataTable.display tbody tr.even>.sorting_3, +table.dataTable.order-column.stripe tbody tr.even>.sorting_3 { background-color: #fdfdfd; } -table.dataTable.display tbody tr.even.selected > .sorting_1, -table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 { +table.dataTable.display tbody tr.even.selected>.sorting_1, +table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1 { background-color: #acbad4; } -table.dataTable.display tbody tr.even.selected > .sorting_2, -table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 { +table.dataTable.display tbody tr.even.selected>.sorting_2, +table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2 { background-color: #adbbd6; } -table.dataTable.display tbody tr.even.selected > .sorting_3, -table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 { +table.dataTable.display tbody tr.even.selected>.sorting_3, +table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3 { background-color: #afbdd8; } -table.dataTable.display tbody tr:hover > .sorting_1, -table.dataTable.order-column.hover tbody tr:hover > .sorting_1 { +table.dataTable.display tbody tr:hover>.sorting_1, +table.dataTable.order-column.hover tbody tr:hover>.sorting_1 { background-color: #eaeaea; } -table.dataTable.display tbody tr:hover > .sorting_2, -table.dataTable.order-column.hover tbody tr:hover > .sorting_2 { +table.dataTable.display tbody tr:hover>.sorting_2, +table.dataTable.order-column.hover tbody tr:hover>.sorting_2 { background-color: #ebebeb; } -table.dataTable.display tbody tr:hover > .sorting_3, -table.dataTable.order-column.hover tbody tr:hover > .sorting_3 { +table.dataTable.display tbody tr:hover>.sorting_3, +table.dataTable.order-column.hover tbody tr:hover>.sorting_3 { background-color: #eeeeee; } -table.dataTable.display tbody tr:hover.selected > .sorting_1, -table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 { +table.dataTable.display tbody tr:hover.selected>.sorting_1, +table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1 { background-color: #a1aec7; } -table.dataTable.display tbody tr:hover.selected > .sorting_2, -table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 { +table.dataTable.display tbody tr:hover.selected>.sorting_2, +table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2 { background-color: #a2afc8; } -table.dataTable.display tbody tr:hover.selected > .sorting_3, -table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 { +table.dataTable.display tbody tr:hover.selected>.sorting_3, +table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3 { background-color: #a4b2cb; } @@ -420,13 +420,11 @@ table.dataTable td { color: #333333 !important; border: 1px solid #979797; background-color: white; - background: -webkit-gradient( - linear, - left top, - left bottom, - color-stop(0%, white), - color-stop(100%, gainsboro) - ); + background: -webkit-gradient(linear, + left top, + left bottom, + color-stop(0%, white), + color-stop(100%, gainsboro)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, white 0%, gainsboro 100%); /* Chrome10+,Safari5.1+ */ @@ -454,13 +452,11 @@ table.dataTable td { color: white !important; border: 1px solid #111111; background-color: #585858; - background: -webkit-gradient( - linear, - left top, - left bottom, - color-stop(0%, #585858), - color-stop(100%, #111111) - ); + background: -webkit-gradient(linear, + left top, + left bottom, + color-stop(0%, #585858), + color-stop(100%, #111111)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #585858 0%, #111111 100%); /* Chrome10+,Safari5.1+ */ @@ -477,13 +473,11 @@ table.dataTable td { .dataTables_wrapper .dataTables_paginate .paginate_button:active { outline: none; background-color: #2b2b2b; - background: -webkit-gradient( - linear, - left top, - left bottom, - color-stop(0%, #2b2b2b), - color-stop(100%, #0c0c0c) - ); + background: -webkit-gradient(linear, + left top, + left bottom, + color-stop(0%, #2b2b2b), + color-stop(100%, #0c0c0c)); /* Chrome,Safari4+ */ background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%); /* Chrome10+,Safari5.1+ */ @@ -514,50 +508,38 @@ table.dataTable td { text-align: center; font-size: 1.2em; background-color: white; - background: -webkit-gradient( - linear, - left top, - right top, - color-stop(0%, rgba(255, 255, 255, 0)), - color-stop(25%, rgba(255, 255, 255, 0.9)), - color-stop(75%, rgba(255, 255, 255, 0.9)), - color-stop(100%, rgba(255, 255, 255, 0)) - ); - background: -webkit-linear-gradient( - left, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.9) 25%, - rgba(255, 255, 255, 0.9) 75%, - rgba(255, 255, 255, 0) 100% - ); - background: -moz-linear-gradient( - left, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.9) 25%, - rgba(255, 255, 255, 0.9) 75%, - rgba(255, 255, 255, 0) 100% - ); - background: -ms-linear-gradient( - left, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.9) 25%, - rgba(255, 255, 255, 0.9) 75%, - rgba(255, 255, 255, 0) 100% - ); - background: -o-linear-gradient( - left, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.9) 25%, - rgba(255, 255, 255, 0.9) 75%, - rgba(255, 255, 255, 0) 100% - ); - background: linear-gradient( - to right, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.9) 25%, - rgba(255, 255, 255, 0.9) 75%, - rgba(255, 255, 255, 0) 100% - ); + background: -webkit-gradient(linear, + left top, + right top, + color-stop(0%, rgba(255, 255, 255, 0)), + color-stop(25%, rgba(255, 255, 255, 0.9)), + color-stop(75%, rgba(255, 255, 255, 0.9)), + color-stop(100%, rgba(255, 255, 255, 0))); + background: -webkit-linear-gradient(left, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.9) 25%, + rgba(255, 255, 255, 0.9) 75%, + rgba(255, 255, 255, 0) 100%); + background: -moz-linear-gradient(left, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.9) 25%, + rgba(255, 255, 255, 0.9) 75%, + rgba(255, 255, 255, 0) 100%); + background: -ms-linear-gradient(left, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.9) 25%, + rgba(255, 255, 255, 0.9) 75%, + rgba(255, 255, 255, 0) 100%); + background: -o-linear-gradient(left, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.9) 25%, + rgba(255, 255, 255, 0.9) 75%, + rgba(255, 255, 255, 0) 100%); + background: linear-gradient(to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.9) 25%, + rgba(255, 255, 255, 0.9) 75%, + rgba(255, 255, 255, 0) 100%); } .dataTables_wrapper .dataTables_length, @@ -577,69 +559,17 @@ table.dataTable td { -webkit-overflow-scrolling: touch; } -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > thead - > tr - > th, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > thead - > tr - > td, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > tbody - > tr - > th, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > tbody - > tr - > td { +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td { vertical-align: middle; } -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > thead - > tr - > th - > div.dataTables_sizing, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > thead - > tr - > td - > div.dataTables_sizing, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > tbody - > tr - > th - > div.dataTables_sizing, -.dataTables_wrapper - .dataTables_scroll - div.dataTables_scrollBody - > table - > tbody - > tr - > td - > div.dataTables_sizing { +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th>div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td>div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th>div.dataTables_sizing, +.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td>div.dataTables_sizing { height: 0; overflow: hidden; margin: 0 !important; @@ -650,8 +580,8 @@ table.dataTable td { border-bottom: 1px solid #111111; } -.dataTables_wrapper.no-footer div.dataTables_scrollHead > table, -.dataTables_wrapper.no-footer div.dataTables_scrollBody > table { +.dataTables_wrapper.no-footer div.dataTables_scrollHead>table, +.dataTables_wrapper.no-footer div.dataTables_scrollBody>table { border-bottom: none; } @@ -664,6 +594,7 @@ table.dataTable td { } @media screen and (max-width: 767px) { + .dataTables_wrapper .dataTables_info, .dataTables_wrapper .dataTables_paginate { float: none; @@ -676,6 +607,7 @@ table.dataTable td { } @media screen and (max-width: 640px) { + .dataTables_wrapper .dataTables_length, .dataTables_wrapper .dataTables_filter { float: none; @@ -703,6 +635,10 @@ table.table_leftalign tr td { text-align: left; } +p.gt_caption { + margin-top: 8px; +} + /* Ligne(s) de titre */ table.dataTable thead tr th { background-color: rgb(90%, 90%, 90%); @@ -757,7 +693,8 @@ table.dataTable.gt_table { table.dataTable.gt_table.gt_left { margin-left: 16px; } + table.dataTable.gt_table.gt_left td, table.dataTable.gt_table.gt_left th { text-align: left; -} +} \ No newline at end of file diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 5a91809bd..105b2e36d 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -1188,10 +1188,11 @@ a.discretelink:hover { .help { max-width: var(--sco-content-max-width); + font-style: italic; } -.help { - font-style: italic; +.help em { + font-style: normal; } .help_important { diff --git a/app/static/js/groups_view.js b/app/static/js/groups_view.js index 74433b643..e555a9eb7 100644 --- a/app/static/js/groups_view.js +++ b/app/static/js/groups_view.js @@ -23,7 +23,7 @@ function groups_view_url() { url.param()["formsemestre_id"] = $("#group_selector")[0].formsemestre_id.value; - var selected_groups = $("#group_selector select").val(); + var selected_groups = $("#group_selector select#group_ids_sel").val(); url.param()["group_ids"] = selected_groups; // remplace par groupes selectionnes return url;