forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -126,6 +126,7 @@ class CountCalculator:
|
|||||||
self.add_half_day(period[0].date())
|
self.add_half_day(period[0].date())
|
||||||
|
|
||||||
while pointer_date < assi.date_fin.date():
|
while pointer_date < assi.date_fin.date():
|
||||||
|
# TODO : Utiliser la préférence de département : workdays
|
||||||
if pointer_date.weekday() < (6 - self.skip_saturday):
|
if pointer_date.weekday() < (6 - self.skip_saturday):
|
||||||
self.add_day(pointer_date)
|
self.add_day(pointer_date)
|
||||||
self.add_half_day(pointer_date)
|
self.add_half_day(pointer_date)
|
||||||
|
@ -835,11 +835,11 @@ def _make_listes_sem(formsemestre: FormSemestre, with_absences=True):
|
|||||||
first_monday = sco_abs.ddmmyyyy(
|
first_monday = sco_abs.ddmmyyyy(
|
||||||
formsemestre.date_debut.strftime("%d/%m/%Y")
|
formsemestre.date_debut.strftime("%d/%m/%Y")
|
||||||
).prev_monday()
|
).prev_monday()
|
||||||
form_abs_tmpl = """
|
form_abs_tmpl = f"""
|
||||||
<!-- <td>
|
|
||||||
<a class="btn" href="%(url_etat)s"><button>Voir l'assiduité</button></a>
|
|
||||||
</td> -->
|
|
||||||
<td>
|
<td>
|
||||||
|
<a class="btn" href="{
|
||||||
|
url_for("assiduites.visu_assi_group", scodoc_dept=g.scodoc_dept)
|
||||||
|
}?group_ids=%(group_id)s&date_debut={formsemestre.date_debut.isoformat()}&date_fin={formsemestre.date_fin.isoformat()}"><button>Visualiser l'assiduité</button></a>
|
||||||
"""
|
"""
|
||||||
form_abs_tmpl += f"""
|
form_abs_tmpl += f"""
|
||||||
<a class="btn" href="{
|
<a class="btn" href="{
|
||||||
|
146
app/tables/visu_assiduites.py
Normal file
146
app/tables/visu_assiduites.py
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Liste simple d'étudiants
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import g, url_for
|
||||||
|
from app.models import Identite, Assiduite, Justificatif
|
||||||
|
from app.tables import table_builder as tb
|
||||||
|
import app.scodoc.sco_assiduites as scass
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
|
|
||||||
|
|
||||||
|
class TableAssi(tb.Table):
|
||||||
|
"""Table listant l'assiduité des étudiants
|
||||||
|
L'id de la ligne est etuid, et le row stocke etud.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
etuds: list[Identite] = None,
|
||||||
|
dates: tuple[str, str] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows
|
||||||
|
classes = ["gt_table", "gt_left"]
|
||||||
|
self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"]
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
row_class=RowAssi,
|
||||||
|
classes=classes,
|
||||||
|
**kwargs,
|
||||||
|
with_foot_titles=False,
|
||||||
|
)
|
||||||
|
self.add_etuds(etuds)
|
||||||
|
|
||||||
|
def add_etuds(self, etuds: list[Identite]):
|
||||||
|
"Ajoute des étudiants à la table"
|
||||||
|
for etud in etuds:
|
||||||
|
row = self.row_class(self, etud)
|
||||||
|
row.add_etud_cols()
|
||||||
|
self.add_row(row)
|
||||||
|
|
||||||
|
|
||||||
|
class RowAssi(tb.Row):
|
||||||
|
"Ligne de la table assiduité"
|
||||||
|
|
||||||
|
# pour le moment très simple, extensible (codes, liens bulletins, ...)
|
||||||
|
def __init__(self, table: TableAssi, etud: Identite, *args, **kwargs):
|
||||||
|
super().__init__(table, etud.id, *args, **kwargs)
|
||||||
|
self.etud = etud
|
||||||
|
self.dates = table.dates
|
||||||
|
|
||||||
|
def add_etud_cols(self):
|
||||||
|
"""Ajoute les colonnes"""
|
||||||
|
etud = self.etud
|
||||||
|
self.table.group_titles.update(
|
||||||
|
{
|
||||||
|
"etud_codes": "Codes",
|
||||||
|
"identite_detail": "",
|
||||||
|
"identite_court": "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
bilan_etud = f"{url_for('assiduites.bilan_etud', scodoc_dept=g.scodoc_dept)}?etudid={etud.id}"
|
||||||
|
self.add_cell(
|
||||||
|
"nom_disp",
|
||||||
|
"Nom",
|
||||||
|
etud.nom_disp(),
|
||||||
|
"identite_detail",
|
||||||
|
data={"order": etud.sort_key},
|
||||||
|
target=bilan_etud,
|
||||||
|
target_attrs={"class": "discretelink", "id": str(etud.id)},
|
||||||
|
)
|
||||||
|
self.add_cell(
|
||||||
|
"prenom",
|
||||||
|
"Prénom",
|
||||||
|
etud.prenom,
|
||||||
|
"identite_detail",
|
||||||
|
data={"order": etud.sort_key},
|
||||||
|
target=bilan_etud,
|
||||||
|
target_attrs={"class": "discretelink", "id": str(etud.id)},
|
||||||
|
)
|
||||||
|
stats = self._get_etud_stats(etud)
|
||||||
|
for key, value in stats.items():
|
||||||
|
self.add_cell(key, value[0], f"{value[1] - value[2]}", "assi_stats")
|
||||||
|
self.add_cell(
|
||||||
|
key + "_justi",
|
||||||
|
value[0] + " Justifiée(s)",
|
||||||
|
f"{value[2]}",
|
||||||
|
"assi_stats",
|
||||||
|
)
|
||||||
|
|
||||||
|
compte_justificatifs = scass.filter_by_date(
|
||||||
|
etud.justificatifs, Justificatif, self.dates[0], self.dates[1]
|
||||||
|
).count()
|
||||||
|
|
||||||
|
self.add_cell("justificatifs", "Justificatifs", f"{compte_justificatifs}")
|
||||||
|
|
||||||
|
def _get_etud_stats(self, etud: Identite) -> dict[str, list[str, float, float]]:
|
||||||
|
retour: dict[str, tuple[str, float, float]] = {
|
||||||
|
"present": ["Présence(s)", 0.0, 0.0],
|
||||||
|
"retard": ["Retard(s)", 0.0, 0.0],
|
||||||
|
"absent": ["Absence(s)", 0.0, 0.0],
|
||||||
|
}
|
||||||
|
|
||||||
|
assi_metric = {
|
||||||
|
"H.": "heure",
|
||||||
|
"J.": "journee",
|
||||||
|
"1/2 J.": "demi",
|
||||||
|
}.get(sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id))
|
||||||
|
|
||||||
|
for etat, valeur in retour.items():
|
||||||
|
compte_etat = scass.get_assiduites_stats(
|
||||||
|
assiduites=etud.assiduites,
|
||||||
|
metric=assi_metric,
|
||||||
|
filtered={
|
||||||
|
"date_debut": self.dates[0],
|
||||||
|
"date_fin": self.dates[1],
|
||||||
|
"etat": etat,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
compte_etat_just = scass.get_assiduites_stats(
|
||||||
|
assiduites=etud.assiduites,
|
||||||
|
metric=assi_metric,
|
||||||
|
filtered={
|
||||||
|
"date_debut": self.dates[0],
|
||||||
|
"date_fin": self.dates[1],
|
||||||
|
"etat": etat,
|
||||||
|
"est_just": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
valeur[1] = compte_etat[assi_metric]
|
||||||
|
valeur[2] = compte_etat_just[assi_metric]
|
||||||
|
return retour
|
||||||
|
|
||||||
|
|
||||||
|
def etuds_sorted_from_ids(etudids) -> list[Identite]:
|
||||||
|
"Liste triée d'etuds à partir d'une collections d'etudids"
|
||||||
|
etuds = [Identite.get_etud(etudid) for etudid in etudids]
|
||||||
|
return sorted(etuds, key=lambda etud: etud.sort_key)
|
29
app/templates/assiduites/pages/visu_assi.j2
Normal file
29
app/templates/assiduites/pages/visu_assi.j2
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<h2>Visualisation de l'assiduité {{gr_tit|safe}}</h2>
|
||||||
|
|
||||||
|
<div class="stats-inputs">
|
||||||
|
<label class="stats-label"> Date de début<input type="date" name="stats_date_debut" id="stats_date_debut"
|
||||||
|
value="{{date_debut}}"></label>
|
||||||
|
<label class="stats-label"> Date de fin<input type="date" name="stats_date_fin" id="stats_date_fin"
|
||||||
|
value="{{date_fin}}"></label>
|
||||||
|
<button onclick="stats()">Changer la période</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{tableau | safe}}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const date_debut = "{{date_debut}}"
|
||||||
|
const date_fin = "{{date_fin}}"
|
||||||
|
const group_ids = "{{group_ids}}"
|
||||||
|
|
||||||
|
function stats() {
|
||||||
|
const deb = document.querySelector('#stats_date_debut').value
|
||||||
|
const fin = document.querySelector('#stats_date_fin').value
|
||||||
|
location.href = `VisualisationAssiduitesGroupe?group_ids=${group_ids}&date_debut=${deb}&date_fin=${fin}`
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
document.querySelector('#stats_date_debut').value = date_debut;
|
||||||
|
document.querySelector('#stats_date_fin').value = date_fin;
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
@ -26,6 +26,8 @@ from flask_login import current_user
|
|||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
from app.scodoc import sco_assiduites as scass
|
from app.scodoc import sco_assiduites as scass
|
||||||
|
|
||||||
|
from app.tables.visu_assiduites import TableAssi, etuds_sorted_from_ids
|
||||||
|
|
||||||
|
|
||||||
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
|
CSSSTYLES = html_sco_header.BOOTSTRAP_MULTISELECT_CSS
|
||||||
|
|
||||||
@ -639,6 +641,60 @@ def get_etat_abs_date():
|
|||||||
).build()
|
).build()
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/VisualisationAssiduitesGroupe")
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
def visu_assi_group():
|
||||||
|
dates = {
|
||||||
|
"debut": request.args.get("date_debut"),
|
||||||
|
"fin": request.args.get("date_fin"),
|
||||||
|
}
|
||||||
|
|
||||||
|
group_ids: list[int] = request.args.get("group_ids", None)
|
||||||
|
etudiants: list[dict] = []
|
||||||
|
|
||||||
|
if group_ids is None:
|
||||||
|
group_ids = []
|
||||||
|
else:
|
||||||
|
group_ids = group_ids.split(",")
|
||||||
|
map(str, group_ids)
|
||||||
|
|
||||||
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||||
|
|
||||||
|
etuds = etuds_sorted_from_ids([m["etudid"] for m in groups_infos.members])
|
||||||
|
|
||||||
|
header: str = html_sco_header.sco_header(
|
||||||
|
page_title="Visualisation des assiduités",
|
||||||
|
init_qtip=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
table: TableAssi = TableAssi(etuds=etuds, dates=list(dates.values()))
|
||||||
|
|
||||||
|
if groups_infos.tous_les_etuds_du_sem:
|
||||||
|
gr_tit = "en"
|
||||||
|
else:
|
||||||
|
if len(groups_infos.group_ids) > 1:
|
||||||
|
grp = "des groupes"
|
||||||
|
else:
|
||||||
|
grp = "du groupe"
|
||||||
|
gr_tit = (
|
||||||
|
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
||||||
|
)
|
||||||
|
|
||||||
|
return HTMLBuilder(
|
||||||
|
header,
|
||||||
|
render_template(
|
||||||
|
"assiduites/pages/visu_assi.j2",
|
||||||
|
tableau=table.html(),
|
||||||
|
gr_tit=gr_tit,
|
||||||
|
date_debut=dates["debut"],
|
||||||
|
date_fin=dates["fin"],
|
||||||
|
group_ids=request.args.get("group_ids", None),
|
||||||
|
),
|
||||||
|
html_sco_header.sco_footer(),
|
||||||
|
).build()
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/SignalAssiduiteDifferee")
|
@bp.route("/SignalAssiduiteDifferee")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoAssiduiteChange)
|
@permission_required(Permission.ScoAssiduiteChange)
|
||||||
|
Loading…
Reference in New Issue
Block a user