ScoDoc/app/templates/assiduites/pages/bilan_etud.j2

231 lines
6.8 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">
<div class="help">Le tableau n'affiche que les assiduités non justifiées
et les justificatifs soumis / modifiés
</div>
{{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>
</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 %}