Assiduite: visu_assi_group avec restriction aux modules du semestre.

This commit is contained in:
Emmanuel Viennet 2024-02-08 15:56:58 +01:00
parent c8ac796347
commit 9ec0ef27ba
5 changed files with 115 additions and 14 deletions

View File

@ -1,22 +1,28 @@
""" """
Ecrit par Matthias Hartmann. Ecrit par Matthias Hartmann.
""" """
from datetime import date, datetime, time, timedelta from datetime import date, datetime, time, timedelta
from pytz import UTC from pytz import UTC
from flask import g
from flask_sqlalchemy.query import Query from flask_sqlalchemy.query import Query
from app import log, db, set_sco_dept from app import log, db, set_sco_dept
import app.scodoc.sco_utils as scu from app.models import (
Identite,
FormSemestre,
FormSemestreInscription,
ModuleImpl,
ModuleImplInscription,
ScoDocSiteConfig,
)
from app.models.assiduites import Assiduite, Justificatif, compute_assiduites_justified from app.models.assiduites import Assiduite, Justificatif, compute_assiduites_justified
from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre, FormSemestreInscription
from app.scodoc import sco_formsemestre_inscriptions from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_cache from app.scodoc import sco_cache
from app.scodoc import sco_etud from app.scodoc import sco_etud
from app.models import ScoDocSiteConfig import app.scodoc.sco_utils as scu
from flask import g
class CountCalculator: class CountCalculator:
@ -93,9 +99,9 @@ class CountCalculator:
evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00") evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00")
) )
self.non_work_days: list[ self.non_work_days: list[scu.NonWorkDays] = (
scu.NonWorkDays scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
] = scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id) )
delta_total: timedelta = datetime.combine( delta_total: timedelta = datetime.combine(
date.min, self.evening date.min, self.evening
@ -371,6 +377,10 @@ def get_assiduites_stats(
assiduites = filter_by_formsemestre( assiduites = filter_by_formsemestre(
assiduites, Assiduite, filtered[key] assiduites, Assiduite, filtered[key]
) )
case "formsemestre_modimpls":
assiduites = filter_by_modimpls(
assiduites, Assiduite, filtered[key]
)
case "est_just": case "est_just":
assiduites = filter_assiduites_by_est_just( assiduites = filter_assiduites_by_est_just(
assiduites, filtered[key] assiduites, filtered[key]
@ -489,7 +499,9 @@ def filter_by_formsemestre(
formsemestre: FormSemestre, formsemestre: FormSemestre,
) -> Query: ) -> Query:
""" """
Filtrage d'une collection en fonction d'un formsemestre Filtrage d'une collection : conserve les élements
- si l'étudiant est inscrit au formsemestre
- Et que la plage de dates est complètement incluse dans le semestre (bizarre!)
""" """
if formsemestre is None: if formsemestre is None:
@ -514,6 +526,41 @@ def filter_by_formsemestre(
return collection_result.filter(collection_class.date_fin <= form_date_fin) return collection_result.filter(collection_class.date_fin <= form_date_fin)
def filter_by_modimpls(
collection_query: Assiduite | Justificatif,
collection_class: Assiduite | Justificatif,
formsemestre: FormSemestre,
) -> Query:
"""
Filtrage d'une collection d'assiduités: conserve les élements
- si l'étudiant est inscrit au formsemestre
- Et que l'assiduité concerne un moduleimpl de ce formsemestre
Ne fait rien sur les justificatifs.
Ne fait rien si formsemestre is None
"""
if (collection_class != Assiduite) or (formsemestre is None):
return collection_query
# restreint aux inscrits:
collection_result = (
collection_query.join(Identite, collection_class.etudid == Identite.id)
.join(
FormSemestreInscription,
Identite.id == FormSemestreInscription.etudid,
)
.filter(FormSemestreInscription.formsemestre_id == formsemestre.id)
)
collection_result = (
collection_result.join(ModuleImpl)
.join(ModuleImplInscription)
.filter(ModuleImplInscription.etudid == collection_class.etudid)
)
return collection_result
def justifies(justi: Justificatif, obj: bool = False) -> list[int] | Query: def justifies(justi: Justificatif, obj: bool = False) -> list[int] | Query:
""" """
Retourne la liste des assiduite_id qui sont justifié par la justification Retourne la liste des assiduite_id qui sont justifié par la justification

View File

@ -133,7 +133,15 @@ def do_ue_create(args, allow_empty_ue_code=False):
break break
args["ue_code"] = code args["ue_code"] = code
# last checks
if not args.get("acronyme"):
raise ScoValueError("acronyme vide")
args["coefficient"] = args.get("coefficient", None)
if args["coefficient"] == "":
args["coefficient"] = None
# create # create
# XXX TODO utiliser UniteEns.create_from_dict
ue_id = _ueEditor.create(cnx, args) ue_id = _ueEditor.create(cnx, args)
log(f"do_ue_create: created {ue_id} with {args}") log(f"do_ue_create: created {ue_id} with {args}")
@ -303,11 +311,13 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
<p class="help">Note: sauf exception, l'UE n'a pas de coefficient associé. <p class="help">Note: sauf exception, l'UE n'a pas de coefficient associé.
Seuls les <em>modules</em> ont des coefficients. Seuls les <em>modules</em> ont des coefficients.
</p>""", </p>""",
(
f""" f"""
<h4>UE du semestre S{ue.semestre_idx}</h4> <h4>UE du semestre S{ue.semestre_idx}</h4>
""" """
if is_apc and ue if is_apc and ue
else "", else ""
),
] ]
ue_types = cursus.ALLOWED_UE_TYPES ue_types = cursus.ALLOWED_UE_TYPES

View File

@ -18,7 +18,12 @@ from app.scodoc import sco_utils as scu
class TableAssi(tb.Table): class TableAssi(tb.Table):
"""Table listant les statistiques d'assiduité des étudiants """Table listant les statistiques d'assiduité des étudiants
L'id de la ligne est etuid, et le row stocke etud. L'id de la ligne est etudid, et le row stocke etud.
On considère les assiduités entre les dates indiquées.
Si formsemestre_modimpls est spécifié, restreint aux assiduités associées à des
moduleimpls de ce formsemestre.
Si convert_values, transforme les nombre en chaines ("12.34"), pour le html. Si convert_values, transforme les nombre en chaines ("12.34"), pour le html.
""" """
@ -28,6 +33,7 @@ class TableAssi(tb.Table):
etuds: list[Identite] = None, etuds: list[Identite] = None,
dates: tuple[str, str] = None, dates: tuple[str, str] = None,
formsemestre: FormSemestre = None, formsemestre: FormSemestre = None,
formsemestre_modimpls: FormSemestre | None = None,
convert_values=False, convert_values=False,
**kwargs, **kwargs,
): ):
@ -35,6 +41,7 @@ class TableAssi(tb.Table):
classes = ["gt_table"] classes = ["gt_table"]
self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"] self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"]
self.formsemestre = formsemestre self.formsemestre = formsemestre
self.formsemestre_modimpls = formsemestre_modimpls
if convert_values: if convert_values:
self.fmt_num = lambda x: f"{x:2.3g}" self.fmt_num = lambda x: f"{x:2.3g}"
else: else:
@ -140,6 +147,7 @@ class RowAssi(tb.Row):
""" """
Renvoie le comptage (dans la métrique du département) des différents états Renvoie le comptage (dans la métrique du département) des différents états
d'assiduité d'un étudiant. d'assiduité d'un étudiant.
Considère les dates.
Returns : Returns :
{ {
@ -167,6 +175,7 @@ class RowAssi(tb.Row):
"date_debut": self.dates[0], "date_debut": self.dates[0],
"date_fin": self.dates[1], "date_fin": self.dates[1],
"etat": "absent,present,retard", # pour tout compter d'un coup "etat": "absent,present,retard", # pour tout compter d'un coup
"formsemestre_modimpls": self.table.formsemestre_modimpls,
"split": 1, # afin d'avoir la division des stats en état, etatjust, etatnonjust "split": 1, # afin d'avoir la division des stats en état, etatjust, etatnonjust
}, },
) )

View File

@ -8,6 +8,13 @@
{% block app_content %} {% block app_content %}
<style>
label.stats_checkbox {
font-weight: normal;
margin-left: 16px;
}
</style>
<h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2> <h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2>
<div class="stats-inputs"> <div class="stats-inputs">
@ -18,6 +25,12 @@
<button onclick="stats()">Changer</button> <button onclick="stats()">Changer</button>
<a style="margin-left:32px;" href="{{request.url}}&fmt=xlsx">{{scu.ICON_XLS|safe}}</a> <a style="margin-left:32px;" href="{{request.url}}&fmt=xlsx">{{scu.ICON_XLS|safe}}</a>
<label class="stats_checkbox">
<input type="checkbox" id="formsemestre_modimpls_box"> restreindre l'assiduité aux
modules de ce semestre
</label>
</div> </div>
{{tableau | safe}} {{tableau | safe}}
@ -40,6 +53,19 @@
window.addEventListener('load', () => { window.addEventListener('load', () => {
document.querySelector('#stats_date_debut').value = date_debut; document.querySelector('#stats_date_debut').value = date_debut;
document.querySelector('#stats_date_fin').value = date_fin; document.querySelector('#stats_date_fin').value = date_fin;
// La checkbox pour restreindre aux modules du semestre:
var url = new URL(window.location.href);
var checkbox = document.getElementById('formsemestre_modimpls_box');
checkbox.checked = url.searchParams.has('formsemestre_modimpls_id');
checkbox.addEventListener('change', function() {
if (this.checked) {
url.searchParams.set('formsemestre_modimpls_id', {{sco.formsemestre.id}});
} else {
url.searchParams.delete('formsemestre_modimpls_id');
}
window.location.href = url.href;
});
}) })
</script> </script>

View File

@ -1301,6 +1301,8 @@ def visu_assi_group():
- date_debut, date_fin (format ISO) - date_debut, date_fin (format ISO)
- fmt : format d'export, html (défaut) ou xls - fmt : format d'export, html (défaut) ou xls
- group_ids : liste des groupes - group_ids : liste des groupes
- formsemestre_modimpls_id: id d'un formasemestre, si fournit restreint les
comptages aux assiduités liées à des modules de ce formsemestre.
""" """
# Récupération des paramètres de la requête # Récupération des paramètres de la requête
@ -1308,6 +1310,12 @@ def visu_assi_group():
"debut": request.args.get("date_debut"), "debut": request.args.get("date_debut"),
"fin": request.args.get("date_fin"), "fin": request.args.get("date_fin"),
} }
formsemestre_modimpls_id = request.args.get("formsemestre_modimpls_id")
formsemestre_modimpls = (
None
if formsemestre_modimpls_id is None
else FormSemestre.get_formsemestre(formsemestre_modimpls_id)
)
fmt = request.args.get("fmt", "html") fmt = request.args.get("fmt", "html")
group_ids: list[int] = request.args.get("group_ids", None) group_ids: list[int] = request.args.get("group_ids", None)
@ -1327,7 +1335,8 @@ def visu_assi_group():
etuds=etuds, etuds=etuds,
dates=list(dates.values()), dates=list(dates.values()),
formsemestre=formsemestre, formsemestre=formsemestre,
convert_values=fmt == "html", formsemestre_modimpls=formsemestre_modimpls,
convert_values=(fmt == "html"),
) )
# Export en XLS # Export en XLS