{% extends "sco_page.j2" %} {% block title %} Bilan assiduité de {{sco.etud.nomprenom}} {% endblock title %} {% block styles %} {{ super() }} <link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css"> <style> .stats-values-item { display: flex; justify-content: space-evenly; align-items: center; flex-direction: column; } .stats { border: 1px solid #333; padding: 5px 2px; width: fit-content; } .stats-values { display: flex; justify-content: flex-start; gap: 15px; } .stats-values-item h5 { font-weight: bold; text-decoration-line: underline; } .stats-values-part { display: flex; flex-direction: column; } .scobox.alerte { text-align: center; border-radius: 7px; background-color: var(--color-error); } .alerte.invisible { display: none; } .alerte p { font-size: larger; color: whitesmoke; } .suppr { margin: 5px 0; } </style> {% endblock styles %} {% block app_content %} <div class="pageContent"> <h2>Bilan de l'assiduité de {{sco.etud.html_link_fiche()|safe}}</span></h2> <div class="scobox alerte invisible"> <p>Attention, cet étudiant a trop d'absences</p> </div> <div class="scobox"> <!-- Statistiques d'assiduité (nb pres, nb retard, nb absence) + nb justifié --> <h4>Statistiques d'assiduité</h4> <div class="stats-inputs"> <label class="stats-label"> Date de début <input type="text" class="datepicker" name="stats_date_debut" id="stats_date_debut" value="{{date_debut}}"></label> <label class="stats-label"> Date de fin <input type="text" class="datepicker" name="stats_date_fin" id="stats_date_fin" value="{{date_fin}}"></label> <button onclick="stats()">Actualiser</button> </div> <div class="stats-values"> </div> </div> <div class="scobox"> <section class="nonvalide"> {{tableau | safe }} </section> </div> <div class="legende"> <h3>Statistiques</h3> <p>Un message d'alerte apparait si le nombre d'absence dépasse le seuil (indiqué dans les préférences du département)</p> <p>Les statistiques sont calculées entre les deux dates sélectionnées. Après modification des dates, appuyer sur le bouton "Actualiser"</p> {% include "assiduites/explication_etats_justifs.j2" %} </div> </div> {% endblock app_content %} {% block scripts %} {{ super() }} <script src="{{scu.STATIC_DIR}}/js/assiduites.js"></script> <script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script> <script> function stats() { const dd_val = document.getElementById('stats_date_debut').value; const df_val = document.getElementById('stats_date_fin').value; let date_debut = new Date(Date.fromFRA(dd_val)); let date_fin = new Date(Date.fromFRA(df_val)); if (dd_val == "" || df_val == "" || !date_debut.isValid() || !date_debut.isValid()) { openAlertModal("Dates invalides", document.createTextNode('Les dates sélectionnées sont invalides')); return; } date_debut = date_debut.startOf("day") date_fin = date_fin.endOf("day") if (date_debut.isAfter(date_fin)) { openAlertModal("Dates invalides", document.createTextNode('La date de début se situe après la date de fin.')); return; } countAssiduites(date_debut.toFakeIso(), date_fin.toFakeIso()) } function getAssiduitesCount(dateDeb, dateFin, action) { const url_api = `../../api/assiduites/${etudid}/count/query?date_debut=${dateDeb}&date_fin=${dateFin}&etat=absent,retard,present&split`; async_get( url_api, action, ()=>{}, ); } function showStats(data){ const counter = { "present": { "total": data["present"], }, "retard": { "total": data["retard"], "justi": data["retard"]["justifie"], }, "absent": { "total": data["absent"], "justi": data["absent"]["justifie"], } } const values = document.querySelector('.stats-values'); values.innerHTML = ""; Object.keys(counter).forEach((key) => { const item = document.createElement('div'); item.classList.add('stats-values-item'); const div = document.createElement('div'); div.classList.add('stats-values-part'); const withJusti = (key, metric) => { if (key == "present") return ""; return ` dont ${counter[key].justi[metric]} justifiées` } const heure = document.createElement('span'); heure.textContent = `${counter[key].total.heure.toFixed(2)} heure(s)${withJusti(key, "heure")}`; const demi = document.createElement('span'); demi.textContent = `${counter[key].total.demi} demi-journée(s)${withJusti(key, "demi")}`; const jour = document.createElement('span'); jour.textContent = `${counter[key].total.journee} journée(s)${withJusti(key, "journee")}`; div.append(jour, demi, heure); const title = document.createElement('h5'); title.textContent = key.capitalize(); item.append(title, div) values.appendChild(item); }); const nbAbs = data["absent"]["non_justifie"][assi_metric]; if (nbAbs > assi_seuil) { document.querySelector('.alerte').classList.remove('invisible'); document.querySelector('.alerte p').textContent = `Attention, cet étudiant a trop d'absences ${nbAbs} / ${assi_seuil} (${metriques[assi_metric]})` } else { document.querySelector('.alerte').classList.add('invisible'); } } function countAssiduites(dateDeb, dateFin) { getAssiduitesCount(dateDeb, dateFin, showStats); } const metriques = { "heure": "H.", "demi": "1/2 J.", "journee": "J." } const etudid = {{ sco.etud.id }}; const assi_metric = "{{ assi_metric | safe }}"; const assi_seuil = {{ assi_seuil }}; const assi_date_debut = "{{date_debut}}"; const assi_date_fin = "{{date_fin}}"; window.addEventListener('load', () => { document.getElementById('stats_date_fin').value = assi_date_fin; document.getElementById('stats_date_debut').value = assi_date_debut; stats(); }) </script> {% endblock %}