1
0
forked from ScoDoc/ScoDoc

Filtrage par groupes dans els pages statistiques: fix #791

This commit is contained in:
Emmanuel Viennet 2024-03-31 23:04:54 +02:00
parent 126ea0741a
commit 28d46e413d
7 changed files with 356 additions and 355 deletions

View File

@ -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":

View File

@ -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 = []

View File

@ -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"],
),
"""<h2 class="formsemestre">Lycées d'origine des étudiants</h2>""",
"\n".join(F),

View File

@ -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 = """<h2>Résultats de <a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a></h2>""" % 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 = ["""<p><em>Aucun étudiant</em></p>"""]
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 = [
"""<form name="f" method="get" action="%s"><p>
Colonnes: <select name="result" onchange="document.f.submit()">"""
% request.base_url
f"""<form id="group_selector" name="f" method="get" action="{request.base_url}">
Colonnes:
<select name="result" onchange="document.f.submit()">
"""
]
for k in keys:
if k == result:
@ -381,30 +381,38 @@ def formsemestre_report_counts(
'<option value="%s" %s>%s</option>'
% (k, selected, LEGENDES_CODES_BUT.get(k, k))
)
F.append("</select>")
if only_primo:
checked = 'checked="1"'
else:
checked = ""
F.append(
'<br><input type="checkbox" name="only_primo" onchange="document.f.submit()" %s>Restreindre aux primo-entrants</input>'
% checked
f"""
</select>
<div style="margin-top:12px;">
<input type="checkbox" name="only_primo" onchange="document.f.submit()"
{'checked' if only_primo else ''}>
Restreindre aux primo-entrants</input>
<span style="margin: 12px;">
Restreindre au(x) groupe(s)&nbsp;:
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
if groups_infos else ''}
</span>
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
</div>
</form>
"""
)
F.append(
'<input type="hidden" name="formsemestre_id" value="%s"/>' % formsemestre_id
)
F.append("</p></form>")
t = tab.make_page(
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
tableau = tab.make_page(
fmt=fmt,
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
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),
"""<p class="help">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 = ["<h3>Semestres associés à chaque date:</h3><ul>"]
for p in P:
expl.append("<li><b>%s</b>:" % p.datedebut.strftime("%d/%m/%y"))
expl.append(f"""<li><b>{p.datedebut.strftime("%d/%m/%y")}</b>:""")
ls = []
for s in p.sems:
ls.append(
'<a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a>'
% s
)
ls.append(formsemestre.html_link_status())
expl.append(", ".join(ls) + "</li>")
expl.append("</ul>")
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 = '<p><a href="%s&percent=0">Afficher les résultats bruts</a></p>' % burl
pplink = f"""<p><a class="stdlink"
href="{burl}&percent=0">Afficher les résultats bruts</a></p>"""
else:
pplink = (
'<p><a href="%s&percent=1">Afficher les résultats en pourcentages</a></p>'
% burl
)
pplink = f"""<p><a class="stdlink"
href="{burl}&percent=1">Afficher les résultats en pourcentages</a></p>"""
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,
),
"""<h2 class="formsemestre">Suivi cohorte: devenir des étudiants de ce semestre</h2>""",
_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"""<form id="f" method="get" action="{request.base_url}">
<p>Bac: <select name="bac" onchange="javascript: submit(this);">
<option value="" {selected}>tous</option>
f"""<form id="group_selector" name="f" method="get" action="{request.base_url}">
<div>Bac:
<select name="bac" onchange="javascript: submit(this);">
<option value="" {selected}>tous</option>
"""
]
for b in bacs:
@ -894,7 +913,8 @@ def _gen_form_selectetuds(
else:
selected = 'selected="selected"'
F.append(
f"""&nbsp; Bac/Specialité: <select name="bacspecialite" onchange="javascript: submit(this);">
f"""&nbsp; Bac/Specialité:
<select name="bacspecialite" onchange="javascript: submit(this);">
<option value="" {selected}>tous</option>
"""
)
@ -938,17 +958,24 @@ def _gen_form_selectetuds(
else:
selected = ""
F.append(f'<option value="{b}" {selected}>{b}</option>')
F.append("</select>")
F.append(
f"""<br>
<input type="checkbox" name="only_primo"
onchange="javascript: submit(this);"
{'checked="1"' if only_primo else ""}/>Restreindre aux primo-entrants
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
<input type="hidden" name="percent" value="{percent}"/>
f"""
</select>
<div style="margin-top:12px;">
<input type="checkbox" name="only_primo"
onchange="javascript: submit(this);"
{'checked="1"' if only_primo else ""}/>Restreindre aux primo-entrants
</p>
<span style="margin: 12px;">
Restreindre au(x) groupe(s)&nbsp;:
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
if groups_infos else ''}
</span>
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
<input type="hidden" name="percent" value="{percent}"/>
</div>
</div>
</form>
"""
)
@ -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 = ["""<form name="f" method="get" action="%s">""" % request.base_url]
if only_primo:
checked = 'checked="1"'
else:
checked = ""
F.append(
'<input type="checkbox" name="only_primo" onchange="document.f.submit()" %s>Restreindre aux primo-entrants</input>'
% checked
)
if no_grouping:
checked = 'checked="1"'
else:
checked = ""
F.append(
'<input type="checkbox" name="no_grouping" onchange="document.f.submit()" %s>Lister chaque étudiant</input>'
% checked
)
F.append(
'<input type="hidden" name="formsemestre_id" value="%s"/>' % formsemestre_id
)
F.append('<input type="hidden" name="fmt" value="%s"/>' % fmt)
F.append("""</form>""")
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"""
<form id="group_selector" name="f" method="get" action="{request.base_url}">
<input type="checkbox" name="only_primo"
onchange="document.f.submit()" {primo_checked}>Restreindre aux primo-entrants</input>
<input type="checkbox" name="no_grouping" onchange="document.f.submit()"
{no_grouping_checked}>Lister chaque étudiant</input>
<span style="margin: 12px;">
Restreindre au(x) groupe(s)&nbsp;:
{sco_groups_view.menu_groups_choice(groups_infos, submit_on_change=True)
if groups_infos else ''}
</span>
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"/>
<input type="hidden" name="fmt" value="{fmt}"/>
</form>
"""
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,
),
"""<h2 class="formsemestre">Cursus des étudiants de ce semestre</h2>""",
doc,
"<p>%d étudiants sélectionnés</p>" % len(etuds),
f"<p>{len(etuds)} étudiants sélectionnés</p>",
_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).
</p>
<p class="help">
Le menu <em>Restreindre au(x) groupe(s)</em> permet de restreindre l'étude aux
étudiants appartenant aux groupes indiqués <em>dans le semestre d'origine</em>.
</p>
""",
html_sco_header.sco_footer(),
]

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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;