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.
"""
from datetime import date, datetime, time, timedelta
from pytz import UTC
from flask import g
from flask_sqlalchemy.query import Query
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.etudiants import Identite
from app.models.formsemestre import FormSemestre, FormSemestreInscription
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_preferences
from app.scodoc import sco_cache
from app.scodoc import sco_etud
from app.models import ScoDocSiteConfig
from flask import g
import app.scodoc.sco_utils as scu
class CountCalculator:
@ -93,9 +99,9 @@ class CountCalculator:
evening if evening else ScoDocSiteConfig.get("assi_afternoon_time", "18:00")
)
self.non_work_days: list[
scu.NonWorkDays
] = scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
self.non_work_days: list[scu.NonWorkDays] = (
scu.NonWorkDays.get_all_non_work_days(dept_id=g.scodoc_dept_id)
)
delta_total: timedelta = datetime.combine(
date.min, self.evening
@ -371,6 +377,10 @@ def get_assiduites_stats(
assiduites = filter_by_formsemestre(
assiduites, Assiduite, filtered[key]
)
case "formsemestre_modimpls":
assiduites = filter_by_modimpls(
assiduites, Assiduite, filtered[key]
)
case "est_just":
assiduites = filter_assiduites_by_est_just(
assiduites, filtered[key]
@ -489,7 +499,9 @@ def filter_by_formsemestre(
formsemestre: FormSemestre,
) -> 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:
@ -514,6 +526,41 @@ def filter_by_formsemestre(
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:
"""
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
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
# XXX TODO utiliser UniteEns.create_from_dict
ue_id = _ueEditor.create(cnx, 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é.
Seuls les <em>modules</em> ont des coefficients.
</p>""",
(
f"""
<h4>UE du semestre S{ue.semestre_idx}</h4>
"""
if is_apc and ue
else "",
else ""
),
]
ue_types = cursus.ALLOWED_UE_TYPES

View File

@ -18,7 +18,12 @@ from app.scodoc import sco_utils as scu
class TableAssi(tb.Table):
"""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.
"""
@ -28,6 +33,7 @@ class TableAssi(tb.Table):
etuds: list[Identite] = None,
dates: tuple[str, str] = None,
formsemestre: FormSemestre = None,
formsemestre_modimpls: FormSemestre | None = None,
convert_values=False,
**kwargs,
):
@ -35,6 +41,7 @@ class TableAssi(tb.Table):
classes = ["gt_table"]
self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"]
self.formsemestre = formsemestre
self.formsemestre_modimpls = formsemestre_modimpls
if convert_values:
self.fmt_num = lambda x: f"{x:2.3g}"
else:
@ -140,6 +147,7 @@ class RowAssi(tb.Row):
"""
Renvoie le comptage (dans la métrique du département) des différents états
d'assiduité d'un étudiant.
Considère les dates.
Returns :
{
@ -167,6 +175,7 @@ class RowAssi(tb.Row):
"date_debut": self.dates[0],
"date_fin": self.dates[1],
"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
},
)

View File

@ -8,6 +8,13 @@
{% block app_content %}
<style>
label.stats_checkbox {
font-weight: normal;
margin-left: 16px;
}
</style>
<h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2>
<div class="stats-inputs">
@ -18,6 +25,12 @@
<button onclick="stats()">Changer</button>
<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>
{{tableau | safe}}
@ -40,6 +53,19 @@
window.addEventListener('load', () => {
document.querySelector('#stats_date_debut').value = date_debut;
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>

View File

@ -1301,6 +1301,8 @@ def visu_assi_group():
- date_debut, date_fin (format ISO)
- fmt : format d'export, html (défaut) ou xls
- 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
@ -1308,6 +1310,12 @@ def visu_assi_group():
"debut": request.args.get("date_debut"),
"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")
group_ids: list[int] = request.args.get("group_ids", None)
@ -1327,7 +1335,8 @@ def visu_assi_group():
etuds=etuds,
dates=list(dates.values()),
formsemestre=formsemestre,
convert_values=fmt == "html",
formsemestre_modimpls=formsemestre_modimpls,
convert_values=(fmt == "html"),
)
# Export en XLS