forked from ScoDoc/ScoDoc
257 lines
8.6 KiB
Django/Jinja
257 lines
8.6 KiB
Django/Jinja
{% 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>
|
|
|
|
// Récupération des statistiques d'assiduité et affichage
|
|
// Fonction appelée lors du clic sur le bouton "Actualiser"
|
|
// Et au chargement de la page
|
|
function stats() {
|
|
// On prend les dates de début et de fin
|
|
// (format DD/MM/YYYY) et on les convertit en Date()
|
|
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;
|
|
}
|
|
// On met les dates à 00h et 23h59 pour avoir la journée entière
|
|
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;
|
|
}
|
|
// Appel à l'api et affichage des stats sur la page
|
|
countAssiduites(date_debut.toFakeIso(), date_fin.toFakeIso())
|
|
|
|
}
|
|
// Appel à l'api pour récupérer les statistiques d'assiduité
|
|
// Effectue l'action passée en paramètre sur les données récupérées
|
|
// dateDeb : date de début au format ISO
|
|
// dateFin : date de fin au format ISO
|
|
// action : fonction à appeler sur les données récupérées
|
|
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){
|
|
// Initialisation d'un objet contenant les résultats
|
|
// Sera mis à jour avec le reste des valeurs dans la suite
|
|
// du code
|
|
const counter = {
|
|
"present": {
|
|
"total": data["present"],
|
|
},
|
|
"retard": {
|
|
"total": data["retard"],
|
|
"justi": data["retard"]["justifie"],
|
|
},
|
|
"absent": {
|
|
"total": data["absent"],
|
|
"justi": data["absent"]["justifie"],
|
|
}
|
|
}
|
|
// Reset du DOM
|
|
const values = document.querySelector('.stats-values');
|
|
values.innerHTML = "";
|
|
// Pour chaque état d'assiduité (present, retard, absent)
|
|
Object.keys(counter).forEach((key) => {
|
|
|
|
// On créé les éléments HTML qui serviront d'affichage
|
|
const item = document.createElement('div');
|
|
item.classList.add('stats-values-item');
|
|
|
|
const div = document.createElement('div');
|
|
div.classList.add('stats-values-part');
|
|
|
|
// Fonction anonyme pour éviter de réécrire tout le temps un test
|
|
// Si l'état est "present" alors cela renvoie "" (=> pas de nb justifié)
|
|
// Sinon cela renvoie "dont X justifiées"
|
|
const withJusti = (key, metric) => {
|
|
if (key == "present") return "";
|
|
return ` dont ${counter[key].justi[metric]} justifiées`
|
|
}
|
|
// HEURE : aroundie à 2 décimales.
|
|
const heure = document.createElement('span');
|
|
heure.textContent = `${counter[key].total.heure.toFixed(2)} heure(s)${withJusti(key, "heure")}`;
|
|
// DEMI-JOURNEE
|
|
const demi = document.createElement('span');
|
|
demi.textContent = `${counter[key].total.demi} demi-journée(s)${withJusti(key, "demi")}`;
|
|
// JOURNEE
|
|
const jour = document.createElement('span');
|
|
jour.textContent = `${counter[key].total.journee} journée(s)${withJusti(key, "journee")}`;
|
|
|
|
// On met à jour le DOM avec les valeurs calculées
|
|
// On met l'état en Titre pour chaque partie
|
|
div.append(jour, demi, heure);
|
|
const title = document.createElement('h5');
|
|
title.textContent = key.capitalize();
|
|
|
|
item.append(title, div)
|
|
|
|
values.appendChild(item);
|
|
});
|
|
|
|
// On vérifie si l'étudiant a trop d'absences
|
|
const nbAbs = data["absent"]["non_justifie"][assi_metric];
|
|
if (nbAbs > assi_seuil) {
|
|
// L'étudiant est au dessus du seuil (défini dans les préférences du département)
|
|
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);
|
|
}
|
|
// Table de conversion des métriques
|
|
// Utilisé pour afficher les valeurs en fonction de la métrique
|
|
const metriques = {
|
|
"heure": "H.",
|
|
"demi": "1/2 J.",
|
|
"journee": "J."
|
|
}
|
|
|
|
|
|
// Récupération des données obligatoires pour les fonctions
|
|
// Depuis le contexte de la page (Jinja2)
|
|
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}}";
|
|
|
|
// Au chargement de la page, on met les dates par défaut
|
|
// Et on appelle la fonction stats() pour afficher les stats
|
|
window.addEventListener('load', () => {
|
|
document.getElementById('stats_date_fin').value = assi_date_fin;
|
|
document.getElementById('stats_date_debut').value = assi_date_debut;
|
|
stats();
|
|
})
|
|
|
|
</script>
|
|
{% endblock %}
|
|
|
|
|