forked from ScoDoc/ScoDoc
Assiduité : traitement des justificatifs closes #818
This commit is contained in:
parent
30560e5860
commit
b2e6ef63b9
@ -660,6 +660,24 @@ class Justificatif(ScoDocModel):
|
|||||||
|
|
||||||
return assiduites_dejustifiees
|
return assiduites_dejustifiees
|
||||||
|
|
||||||
|
def get_assiduites(self) -> Query:
|
||||||
|
"""
|
||||||
|
get_assiduites Récupère les assiduités qui sont concernées par le justificatif
|
||||||
|
(Concernée ≠ Justifiée, mais qui sont sur la même période)
|
||||||
|
Ne prends pas en compte les Présences
|
||||||
|
Returns:
|
||||||
|
Query: Les assiduités concernées
|
||||||
|
"""
|
||||||
|
|
||||||
|
assiduites_query = Assiduite.query.filter(
|
||||||
|
Assiduite.etudid == self.etudid,
|
||||||
|
Assiduite.date_debut >= self.date_debut,
|
||||||
|
Assiduite.date_fin <= self.date_fin,
|
||||||
|
Assiduite.etat != EtatAssiduite.PRESENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
return assiduites_query
|
||||||
|
|
||||||
|
|
||||||
def is_period_conflicting(
|
def is_period_conflicting(
|
||||||
date_debut: datetime,
|
date_debut: datetime,
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""Tableau de bord semestre
|
"""Tableau de bord semestre"""
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@ -1128,6 +1127,19 @@ def formsemestre_status(formsemestre_id=None, check_parcours=True):
|
|||||||
_make_listes_sem(formsemestre),
|
_make_listes_sem(formsemestre),
|
||||||
"</div>",
|
"</div>",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# --- Lien Traitement Justificatifs:
|
||||||
|
|
||||||
|
if current_user.has_permission(Permission.AbsJustifView):
|
||||||
|
H.append(
|
||||||
|
f"""<p>
|
||||||
|
<a class="stdlink" href="{url_for('assiduites.traitement_justificatifs',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id)}">
|
||||||
|
Traitement des justificatifs d'absence</a>
|
||||||
|
</p>"""
|
||||||
|
)
|
||||||
|
|
||||||
# --- Lien mail enseignants:
|
# --- Lien mail enseignants:
|
||||||
adrlist = list(mails_enseignants - {None, ""})
|
adrlist = list(mails_enseignants - {None, ""})
|
||||||
if adrlist:
|
if adrlist:
|
||||||
|
@ -757,3 +757,11 @@ tr.row-justificatif.non_valide td.assi-type {
|
|||||||
var(--color-justi-attente) 4px,
|
var(--color-justi-attente) 4px,
|
||||||
var(--color-justi-attente) 7px) !important;
|
var(--color-justi-attente) 7px) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#gtrcontent .pdp {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gtrcontent[data-pdp="true"] .pdp {
|
||||||
|
display: block;
|
||||||
|
}
|
@ -908,3 +908,19 @@ function setupAssiduiteBubble(el, assiduite) {
|
|||||||
|
|
||||||
el.appendChild(bubble);
|
el.appendChild(bubble);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permet d'afficher ou non les photos des étudiants
|
||||||
|
* @param {boolean} checked
|
||||||
|
*/
|
||||||
|
function afficherPDP(checked) {
|
||||||
|
if (checked) {
|
||||||
|
gtrcontent.setAttribute("data-pdp", "true");
|
||||||
|
} else {
|
||||||
|
gtrcontent.removeAttribute("data-pdp");
|
||||||
|
}
|
||||||
|
|
||||||
|
// On sauvegarde le choix dans le localStorage
|
||||||
|
localStorage.setItem("scodoc-etud-pdp", `${checked}`);
|
||||||
|
pdp.checked = checked;
|
||||||
|
}
|
||||||
|
@ -49,14 +49,6 @@
|
|||||||
width: 10em;
|
width: 10em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#gtrcontent .pdp {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#gtrcontent[data-pdp="true"] .pdp {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tableau-periode {
|
#tableau-periode {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -142,22 +134,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
/**
|
|
||||||
* Permet d'afficher ou non les photos des étudiants
|
|
||||||
* @param {boolean} checked
|
|
||||||
*/
|
|
||||||
function afficherPDP(checked) {
|
|
||||||
if (checked) {
|
|
||||||
gtrcontent.setAttribute("data-pdp", "true");
|
|
||||||
} else {
|
|
||||||
gtrcontent.removeAttribute("data-pdp");
|
|
||||||
}
|
|
||||||
|
|
||||||
// On sauvegarde le choix dans le localStorage
|
|
||||||
localStorage.setItem("scodoc-signal_assiduites_diff-pdp", `${checked}`);
|
|
||||||
pdp.checked = checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Permet d'ajouter une nouvelle période au tableau
|
* Permet d'ajouter une nouvelle période au tableau
|
||||||
* Par défaut la période est générèe avec les valeurs des inputs
|
* Par défaut la période est générèe avec les valeurs des inputs
|
||||||
@ -515,7 +491,7 @@ if (window.forceModule) {
|
|||||||
* - On vérifie si la date est un jour travaillé
|
* - On vérifie si la date est un jour travaillé
|
||||||
*/
|
*/
|
||||||
async function main() {
|
async function main() {
|
||||||
const checked = localStorage.getItem("scodoc-signal_assiduites_diff-pdp") == "true";
|
const checked = localStorage.getItem("scodoc-etud-pdp") == "true";
|
||||||
afficherPDP(checked);
|
afficherPDP(checked);
|
||||||
$("#date").on("change", async function (d) {
|
$("#date").on("change", async function (d) {
|
||||||
// On vérifie si la date est un jour travaillé
|
// On vérifie si la date est un jour travaillé
|
||||||
|
405
app/templates/assiduites/pages/traitement_justificatifs.j2
Normal file
405
app/templates/assiduites/pages/traitement_justificatifs.j2
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
{% extends "sco_page.j2" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.ligne.valide, .ligne.non_valide{
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 0.5fr 1.5fr 2fr 1fr 1fr;
|
||||||
|
gap: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne.head {
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 2px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne>div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne>div:not(:last-of-type) {
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdp {
|
||||||
|
width: 50px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.etud {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.validation{
|
||||||
|
flex-direction: row !important;
|
||||||
|
justify-content: space-evenly !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne button[etat]{
|
||||||
|
border: 1px solid #444;
|
||||||
|
color: #444;
|
||||||
|
padding: 2px 6px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
margin: 2px 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ligne.attente button[etat="attente"]{
|
||||||
|
color: whitesmoke;
|
||||||
|
background-color: var(--color-retard);
|
||||||
|
}
|
||||||
|
.ligne.valide button[etat="valide"]{
|
||||||
|
color: whitesmoke;
|
||||||
|
background-color: var(--color-present);
|
||||||
|
}
|
||||||
|
.ligne.non_valide button[etat="non_valide"]{
|
||||||
|
color: whitesmoke;
|
||||||
|
background-color: var(--color-absent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hint{
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.entry_date::before{
|
||||||
|
content: "Saisie le ";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.sco-drop {
|
||||||
|
border: 1px solid #e1e1e1;
|
||||||
|
/* Couleur de bordure plus douce */
|
||||||
|
border-radius: 8px;
|
||||||
|
/* Coins plus arrondis */
|
||||||
|
background-color: #fafafa;
|
||||||
|
/* Couleur de fond légère */
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
/* Ombre douce pour de la profondeur */
|
||||||
|
width: 100%;
|
||||||
|
/* Adaptation à la largeur de son conteneur */
|
||||||
|
max-width: 600px;
|
||||||
|
/* Largeur maximale pour une meilleure apparence sur grands écrans */
|
||||||
|
margin: 10px auto;
|
||||||
|
/* Centrage avec une marge */
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop[open] {
|
||||||
|
z-index: 2;
|
||||||
|
/* Empilement au-dessus des autres détails */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop summary {
|
||||||
|
font-weight: 600;
|
||||||
|
/* Texte plus épais */
|
||||||
|
color: #333;
|
||||||
|
/* Couleur de texte plus foncée pour le contraste */
|
||||||
|
padding: 7px 10px;
|
||||||
|
/* Plus de padding pour une meilleure ergonomie */
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
/* Enlève les puces */
|
||||||
|
outline: none;
|
||||||
|
/* Supprime la bordure de focus par défaut pour un look plus net */
|
||||||
|
user-select: none;
|
||||||
|
/* Empêche la sélection du texte */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
/* Cache le triangle par défaut sur Chrome/Safari */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop summary:focus {
|
||||||
|
outline: none;
|
||||||
|
/* Plus propre sans contour lors du focus */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop ul {
|
||||||
|
list-style: none;
|
||||||
|
/* Enlève les puces */
|
||||||
|
margin: 5px 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
/* Arrière-plan blanc pour le contenu */
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
border: 1px solid #e1e1e1;
|
||||||
|
/* Bordure plus douce */
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
/* Ombre douce pour de la profondeur */
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-height: 150px;
|
||||||
|
/* Hauteur maximale pour une meilleure apparence sur grands écrans */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop li {
|
||||||
|
padding: 10px 20px;
|
||||||
|
/* Espacement intérieur pour les éléments de liste */
|
||||||
|
border-top: 1px solid #e1e1e1;
|
||||||
|
/* Séparateur subtil entre les éléments */
|
||||||
|
}
|
||||||
|
|
||||||
|
.sco-drop li:first-child {
|
||||||
|
border-top: none;
|
||||||
|
/* Pas de bordure en haut du premier élément */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
{% endblock styles %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/js/assiduites.js"></script>
|
||||||
|
<script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function changerEtatJustificatif(justifId, etat) {
|
||||||
|
const ligne = document.getElementById("justi-" + justifId);
|
||||||
|
// Mettre à jour le justificatif
|
||||||
|
async_post(
|
||||||
|
"../../api/justificatif/" + justifId + "/edit",
|
||||||
|
{ etat: etat },
|
||||||
|
() => {
|
||||||
|
// Mettre à jour la ligne
|
||||||
|
ligne.classList.remove("attente", "modifie", "valide", "non_valide");
|
||||||
|
ligne.classList.add(etat);
|
||||||
|
|
||||||
|
// Afficher le toast
|
||||||
|
const p = document.createElement("span");
|
||||||
|
let color = "";
|
||||||
|
|
||||||
|
switch (etat) {
|
||||||
|
case "attente":
|
||||||
|
color = "var(--color-retard)";
|
||||||
|
p.textContent = "Justificatif mis en attente";
|
||||||
|
break;
|
||||||
|
case "valide":
|
||||||
|
color = "var(--color-present)";
|
||||||
|
p.textContent = "Justificatif validé";
|
||||||
|
break;
|
||||||
|
case "non_valide":
|
||||||
|
color = "var(--color-absent)";
|
||||||
|
p.textContent = "Justificatif invalidé";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = "gray";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const toast = generateToast(p, color, 3);
|
||||||
|
pushToast(toast);
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Filtre les lignes en fonction des états demandés
|
||||||
|
* @param {Array} etats
|
||||||
|
*/
|
||||||
|
function filtrerLignes() {
|
||||||
|
const etats = [
|
||||||
|
att.checked ? "attente" : null,
|
||||||
|
modif.checked ? "modifie" : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Sauvegarde des paramètres
|
||||||
|
localStorage.setItem("scodoc-etud-att", `${att.checked}`);
|
||||||
|
localStorage.setItem("scodoc-etud-modif", `${modif.checked}`);
|
||||||
|
|
||||||
|
document.querySelectorAll(".ligne").forEach((el) => {
|
||||||
|
if ((el.id == "")) return;
|
||||||
|
|
||||||
|
// Si au moins un état se trouve dans la classe de l'élément
|
||||||
|
// Alors on laisse affiché cet élément
|
||||||
|
if (
|
||||||
|
etats.some((e) => {
|
||||||
|
return el.classList.contains(e);
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
el.classList.remove("hidden");
|
||||||
|
} else {
|
||||||
|
el.classList.add("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const checked = localStorage.getItem("scodoc-etud-pdp") == "true";
|
||||||
|
afficherPDP(checked);
|
||||||
|
|
||||||
|
// Gestion des filtres
|
||||||
|
att.checked = localStorage.getItem("scodoc-etud-att") == "true";
|
||||||
|
modif.checked = localStorage.getItem("scodoc-etud-modif") == "true";
|
||||||
|
att.addEventListener("change", filtrerLignes);
|
||||||
|
modif.addEventListener("change", filtrerLignes);
|
||||||
|
filtrerLignes()
|
||||||
|
|
||||||
|
// Gestion des dropdowns
|
||||||
|
document.body.addEventListener("click", (e) => {
|
||||||
|
if (!e.target.matches(".sco-drop, .sco-drop *")) {
|
||||||
|
document.querySelectorAll(".sco-drop").forEach((drop) => {
|
||||||
|
drop.removeAttribute("open");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
|
||||||
|
<h2>Traitement des justificatifs <span class="rouge">{{formsemestre.titre_num()}}</span></h2>
|
||||||
|
|
||||||
|
<div class="scobox">
|
||||||
|
<label for="att">
|
||||||
|
Justificatifs en attente
|
||||||
|
<input type="checkbox" id="att" checked>
|
||||||
|
</label>
|
||||||
|
<label for="modif">
|
||||||
|
Justificatifs modifié
|
||||||
|
<input type="checkbox" id="modif" checked>
|
||||||
|
</label>
|
||||||
|
<label for="pdp">
|
||||||
|
Photo des étudiants :
|
||||||
|
<input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="scobox">
|
||||||
|
<div class="ligne head">
|
||||||
|
<div>Etudiant</div>
|
||||||
|
<div>Abs</div>
|
||||||
|
<div>Plage</div>
|
||||||
|
<div>Description</div>
|
||||||
|
<div>Fichiers</div>
|
||||||
|
<div>Validation</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% for ligne in lignes %}
|
||||||
|
<div class="ligne {{ligne.etat}}" id="justi-{{ligne.justif.justif_id}}">
|
||||||
|
<div class="etud">
|
||||||
|
<img src="../../api/etudiant/etudid/{{ligne.etud.id}}/photo?size=small" alt="{{ligne.etud.nomprenom}}"
|
||||||
|
class="pdp">
|
||||||
|
<span class="etudinfo" id="{{ligne.justif.justif_id}}-{{ligne.etud.id}}">{{ ligne.etud.nomprenom }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="stats">
|
||||||
|
<span>
|
||||||
|
NJ : {{ligne.etud.stats[0]}}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
J : {{ligne.etud.stats[1]}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="plage">
|
||||||
|
{% if ligne.justif.date_debut.date() == ligne.justif.date_fin.date() %}
|
||||||
|
<span class="date">
|
||||||
|
{{ligne.justif.date_debut.strftime("%d/%m/%y")}} de {{ligne.justif.date_debut.strftime("%Hh%M")}} à
|
||||||
|
{{ligne.justif.date_fin.strftime("%Hh%M")}}
|
||||||
|
</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="date_debut">
|
||||||
|
du {{ligne.justif.date_debut.strftime("%d/%m/%y")}}
|
||||||
|
</span>
|
||||||
|
<span class="date_fin">
|
||||||
|
au {{ligne.justif.date_fin.strftime("%d/%m/%y")}}
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
<span class="entry_date hint">
|
||||||
|
{{ligne.justif.entry_date.strftime("%d/%m/%y %Hh%M")}}
|
||||||
|
</span>
|
||||||
|
{% if ligne.assiduites.__len__() == 0 %}
|
||||||
|
<p>Aucune assiduité concernée</p>
|
||||||
|
{% else %}
|
||||||
|
<details class="sco-drop">
|
||||||
|
<summary>
|
||||||
|
Assiduités concernées
|
||||||
|
</summary>
|
||||||
|
<ul>
|
||||||
|
{% for assi in ligne.assiduites %}
|
||||||
|
<li>
|
||||||
|
{{scu.EtatAssiduite(assi.etat).version_lisible()}}
|
||||||
|
{% if assi.date_debut.date() == assi.date_fin.date() %}
|
||||||
|
du {{assi.date_debut.strftime("%d/%m/%y")}} de {{assi.date_debut.strftime("%Hh%M")}} à
|
||||||
|
{{assi.date_fin.strftime("%Hh%M")}}
|
||||||
|
{% else %}
|
||||||
|
du {{assi.date_debut.strftime("%d/%m/%y %Hh%M")}} au {{assi.date_fin.strftime("%d/%m/%y
|
||||||
|
%Hh%M")}}
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="desc">
|
||||||
|
<p>{{ligne.justif.raison}}</p>
|
||||||
|
{% if ligne.etat == "modifie" %}
|
||||||
|
<p class="hint">le justificatif a été modifié</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="fichiers">
|
||||||
|
{% if ligne.fichiers.total == 0 %}
|
||||||
|
<p>Aucun fichier joint</p>
|
||||||
|
{% else %}
|
||||||
|
<details class="sco-drop">
|
||||||
|
<summary>Fichiers joints</summary>
|
||||||
|
<ul>
|
||||||
|
{% for filename in ligne.fichiers.filenames %}
|
||||||
|
<li>
|
||||||
|
<a href="{{url_for('apiweb.justif_export',justif_id=ligne.justif.justif_id,
|
||||||
|
filename=filename, scodoc_dept=g.scodoc_dept)}}">{{filename}}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="validation">
|
||||||
|
|
||||||
|
<button type="button" etat="valide" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'valide')">OUI</button>
|
||||||
|
<button type="button" etat="non_valide" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'non_valide')">NON</button>
|
||||||
|
<button type="button" etat="attente" onclick="changerEtatJustificatif({{ligne.justif.justif_id}}, 'attente')">ATT</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% include "assiduites/widgets/toast.j2" %}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -10,17 +10,18 @@
|
|||||||
width: 20vw;
|
width: 20vw;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
align-items: end;
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast {
|
.toast {
|
||||||
margin: 0.5vh 0;
|
margin: 0.5vh 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: fit-content;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -2036,6 +2036,75 @@ def signale_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("traitement_justificatifs")
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.AbsJustifView)
|
||||||
|
def traitement_justificatifs():
|
||||||
|
"""Page de traitement des justificatifs
|
||||||
|
On traite les justificatifs par formsemestre
|
||||||
|
On peut Valider, Invalider ou mettre en ATT
|
||||||
|
"""
|
||||||
|
# Récupération du formsemestre
|
||||||
|
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
||||||
|
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
|
|
||||||
|
lignes: list[dict] = []
|
||||||
|
|
||||||
|
# Récupération des justificatifs
|
||||||
|
justificatifs_query: Query = scass.filter_by_formsemestre(
|
||||||
|
Justificatif.query, Justificatif, formsemestre
|
||||||
|
)
|
||||||
|
justificatifs_query = justificatifs_query.filter(
|
||||||
|
Justificatif.etat.in_(
|
||||||
|
[scu.EtatJustificatif.ATTENTE, scu.EtatJustificatif.MODIFIE]
|
||||||
|
)
|
||||||
|
).order_by(Justificatif.date_debut)
|
||||||
|
|
||||||
|
justif: Justificatif
|
||||||
|
for justif in justificatifs_query:
|
||||||
|
etud: Identite = justif.etudiant
|
||||||
|
assi_stats: tuple[int, int, int] = scass.get_assiduites_count(
|
||||||
|
etud.id, formsemestre.to_dict()
|
||||||
|
)
|
||||||
|
etud_dict: dict = {
|
||||||
|
"id": etud.id,
|
||||||
|
"nom": etud.nom,
|
||||||
|
"prenom": etud.prenom,
|
||||||
|
"nomprenom": etud.nomprenom,
|
||||||
|
"stats": assi_stats,
|
||||||
|
"sort_key": etud.sort_key,
|
||||||
|
}
|
||||||
|
|
||||||
|
assiduites_justifiees: list[Assiduite] = justif.get_assiduites().all()
|
||||||
|
|
||||||
|
# fichiers justificatifs archivés:
|
||||||
|
filenames, nb_files = justif.get_fichiers()
|
||||||
|
fichiers = {
|
||||||
|
"total": nb_files,
|
||||||
|
"filenames": filenames,
|
||||||
|
}
|
||||||
|
|
||||||
|
lignes.append(
|
||||||
|
{
|
||||||
|
"etud": etud_dict,
|
||||||
|
"justif": justif,
|
||||||
|
"assiduites": assiduites_justifiees,
|
||||||
|
"fichiers": fichiers,
|
||||||
|
"etat": scu.EtatJustificatif(justif.etat).name.lower(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Tri en fonction du nom des étudiants
|
||||||
|
lignes = sorted(lignes, key=lambda x: x["etud"]["sort_key"])
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"assiduites/pages/traitement_justificatifs.j2",
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
|
lignes=lignes,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
def generate_bul_list(etud: Identite, semestre: FormSemestre) -> str:
|
||||||
"""Génère la liste des assiduités d'un étudiant pour le bulletin mail"""
|
"""Génère la liste des assiduités d'un étudiant pour le bulletin mail"""
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user