forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: UTF-8 -*
|
# -*- coding: UTF-8 -*
|
||||||
"""Gestion de l'assiduité (assiduités + justificatifs)
|
"""Gestion de l'assiduité (assiduités + justificatifs)"""
|
||||||
"""
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
@ -336,13 +336,19 @@ class Assiduite(ScoDocModel):
|
|||||||
"""
|
"""
|
||||||
return get_formsemestre_from_data(self.to_dict())
|
return get_formsemestre_from_data(self.to_dict())
|
||||||
|
|
||||||
def get_module(self, traduire: bool = False) -> int | str:
|
def get_module(self, traduire: bool = False) -> Module | str:
|
||||||
"TODO documenter"
|
"""
|
||||||
|
Retourne le module associé à l'assiduité
|
||||||
|
Si traduire est vrai, retourne le titre du module précédé du code
|
||||||
|
Sinon rentourne l'objet Module ou None
|
||||||
|
"""
|
||||||
|
|
||||||
if self.moduleimpl_id is not None:
|
if self.moduleimpl_id is not None:
|
||||||
if traduire:
|
|
||||||
modimpl: ModuleImpl = ModuleImpl.query.get(self.moduleimpl_id)
|
modimpl: ModuleImpl = ModuleImpl.query.get(self.moduleimpl_id)
|
||||||
mod: Module = Module.query.get(modimpl.module_id)
|
mod: Module = Module.query.get(modimpl.module_id)
|
||||||
|
if traduire:
|
||||||
return f"{mod.code} {mod.titre}"
|
return f"{mod.code} {mod.titre}"
|
||||||
|
return mod
|
||||||
|
|
||||||
elif self.external_data is not None and "module" in self.external_data:
|
elif self.external_data is not None and "module" in self.external_data:
|
||||||
return (
|
return (
|
||||||
|
@ -12,7 +12,7 @@ from sqlalchemy import desc, literal, union, asc
|
|||||||
|
|
||||||
from app import db, g
|
from app import db, g
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.models import Assiduite, Identite, Justificatif
|
from app.models import Assiduite, Identite, Justificatif, Module
|
||||||
from app.scodoc.sco_utils import (
|
from app.scodoc.sco_utils import (
|
||||||
EtatAssiduite,
|
EtatAssiduite,
|
||||||
EtatJustificatif,
|
EtatJustificatif,
|
||||||
@ -534,10 +534,45 @@ class RowAssiJusti(tb.Row):
|
|||||||
if self.table.options.show_module:
|
if self.table.options.show_module:
|
||||||
if self.ligne["type"] == "assiduite":
|
if self.ligne["type"] == "assiduite":
|
||||||
assi: Assiduite = Assiduite.query.get(self.ligne["obj_id"])
|
assi: Assiduite = Assiduite.query.get(self.ligne["obj_id"])
|
||||||
mod: str = assi.get_module(True)
|
if self.table.no_pagination:
|
||||||
self.add_cell("module", "Module", mod, data={"order": mod})
|
mod: Module = assi.get_module(False)
|
||||||
|
code = mod.code if isinstance(mod, Module) else ""
|
||||||
|
titre = ""
|
||||||
|
if isinstance(mod, Module):
|
||||||
|
titre = mod.titre
|
||||||
|
elif isinstance(mod, str):
|
||||||
|
titre = mod
|
||||||
else:
|
else:
|
||||||
|
titre = "Non Spécifié"
|
||||||
|
|
||||||
|
self.add_cell(
|
||||||
|
"code_module", "Code Module", code, data={"order": code}
|
||||||
|
)
|
||||||
|
self.add_cell(
|
||||||
|
"titre_module",
|
||||||
|
"Titre Module",
|
||||||
|
titre,
|
||||||
|
data={"order": titre},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mod: Module = assi.get_module(True)
|
||||||
|
self.add_cell(
|
||||||
|
"module",
|
||||||
|
"Module",
|
||||||
|
mod,
|
||||||
|
data={"order": mod},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if self.table.no_pagination:
|
||||||
self.add_cell("module", "Module", "", data={"order": ""})
|
self.add_cell("module", "Module", "", data={"order": ""})
|
||||||
|
else:
|
||||||
|
self.add_cell("code_module", "Code Module", "", data={"order": ""})
|
||||||
|
self.add_cell(
|
||||||
|
"titre_module",
|
||||||
|
"Titre Module",
|
||||||
|
"",
|
||||||
|
data={"order": ""},
|
||||||
|
)
|
||||||
|
|
||||||
def _utilisateur(self) -> None:
|
def _utilisateur(self) -> None:
|
||||||
utilisateur: User = (
|
utilisateur: User = (
|
||||||
|
@ -20,6 +20,39 @@ le semestre concerné (saisie par jour ou saisie différée).
|
|||||||
<br>
|
<br>
|
||||||
{{billets | safe}}
|
{{billets | safe}}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3>Télécharger l'assiduité</h3>
|
||||||
|
|
||||||
|
<form action="{{url_for('assiduites.recup_assiduites_plage', scodoc_dept=g.scodoc_dept)}}" method="post">
|
||||||
|
<label for="datedeb">
|
||||||
|
Du :
|
||||||
|
<input type="text" class="datepicker" id="datedeb" name="datedeb">
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label for="datefin">
|
||||||
|
Au :
|
||||||
|
<input type="text" class="datepicker" id="datefin" name="datefin">
|
||||||
|
</label>
|
||||||
|
<br>
|
||||||
|
<label for="formsemestre_id">Télécharger l'assiduité de </label>
|
||||||
|
<select name="formsemestre_id" id="formsemestre_id">
|
||||||
|
<option value="">Tout le département</option>
|
||||||
|
{% for id, titre in formsemestres.items() %}
|
||||||
|
{% if formsemestre_id == id %}
|
||||||
|
<option value="{{id}}" selected>{{titre}}</option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{id}}">{{titre}}</option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<input type="submit" value="Télécharger" name="telecharger">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<section class="nonvalide">
|
<section class="nonvalide">
|
||||||
{{tableau | safe }}
|
{{tableau | safe }}
|
||||||
|
@ -17,6 +17,15 @@
|
|||||||
gap: 0.5em;
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#actions {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
#actions label{
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
#fix {
|
#fix {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -49,21 +58,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#tableau-periode {
|
#tableau-periode {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
max-width: var(--sco-content-max-width);
|
max-width: var(--sco-content-max-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
#tableau-periode .pdp {
|
#tableau-periode .pdp {
|
||||||
width: 2em;
|
width: 5em;
|
||||||
height: 2em;
|
border-radius: 8px;
|
||||||
border-radius: 50%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-table {
|
.header {
|
||||||
display: grid;
|
background-color: #f9f9f9;
|
||||||
grid-template-columns: 200px repeat({{ etudiants|length }}, 1fr);
|
padding: 10px;
|
||||||
width: var(--sco-content-max-width);
|
text-align: center;
|
||||||
|
border: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell, .header {
|
.cell, .header {
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
@ -74,19 +86,23 @@
|
|||||||
}
|
}
|
||||||
.header{
|
.header{
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
}
|
}
|
||||||
.cell{
|
|
||||||
|
.cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
width: 256px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell p{
|
.cell p{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
.sticky {
|
.sticky {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: 0;
|
top: 0;
|
||||||
background-color: #f9f9f9;
|
background-color: #f9f9f9;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
@ -95,12 +111,24 @@
|
|||||||
display: block;
|
display: block;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
width: 100% !important;
|
||||||
|
min-width: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assi-btns {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pointer{
|
.pointer{
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ligne{
|
||||||
|
display: flex;
|
||||||
|
gap: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{% endblock styles %}
|
{% endblock styles %}
|
||||||
@ -124,6 +152,10 @@ function afficherPDP(checked) {
|
|||||||
} else {
|
} else {
|
||||||
gtrcontent.removeAttribute("data-pdp");
|
gtrcontent.removeAttribute("data-pdp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On sauvegarde le choix dans le localStorage
|
||||||
|
localStorage.setItem("scodoc-signal_assiduites_diff-pdp", `${checked}`);
|
||||||
|
pdp.checked = checked;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -156,7 +188,7 @@ async function nouvellePeriode(period = null) {
|
|||||||
moduleimpl_id = period.moduleimpl_id;
|
moduleimpl_id = period.moduleimpl_id;
|
||||||
}else{
|
}else{
|
||||||
//Sinon on vérifie qu'on a bien des valeurs
|
//Sinon on vérifie qu'on a bien des valeurs
|
||||||
const text = document.createTextNode("Veuillez remplir tous les champs pour ajouter une période.")
|
const text = document.createTextNode("Veuillez remplir tous les champs pour ajouter une plage.")
|
||||||
if (date == "" || debut == "" || fin == "" || moduleimpl_id == "") {
|
if (date == "" || debut == "" || fin == "" || moduleimpl_id == "") {
|
||||||
openAlertModal(
|
openAlertModal(
|
||||||
"Erreur",
|
"Erreur",
|
||||||
@ -168,10 +200,11 @@ async function nouvellePeriode(period = null) {
|
|||||||
|
|
||||||
// On ajoute la nouvelle période au tableau
|
// On ajoute la nouvelle période au tableau
|
||||||
let periodeDiv = document.createElement("div");
|
let periodeDiv = document.createElement("div");
|
||||||
periodeDiv.classList.add("cell", "sticky");
|
periodeDiv.classList.add("cell", "header");
|
||||||
periodeDiv.id = `periode-${periodId}`;
|
periodeDiv.id = `periode-${periodId}`;
|
||||||
|
|
||||||
const periodP = document.createElement("p");
|
const periodP = document.createElement("p");
|
||||||
periodP.textContent = `Période du ${date} de ${debut} à ${fin}`;
|
periodP.textContent = `Plage du ${date} de ${debut} à ${fin}`;
|
||||||
|
|
||||||
// On ajoute le moduleimpl
|
// On ajoute le moduleimpl
|
||||||
const modP = document.createElement("p");
|
const modP = document.createElement("p");
|
||||||
@ -184,7 +217,7 @@ async function nouvellePeriode(period = null) {
|
|||||||
// On supprime toutes les cases du tableau correspondant à cette période
|
// On supprime toutes les cases du tableau correspondant à cette période
|
||||||
document
|
document
|
||||||
.querySelectorAll(
|
.querySelectorAll(
|
||||||
`.cell[data-periodeid="${periodeDiv.getAttribute("data-periodeid")}"]`
|
`[data-periodeid="${periodeDiv.getAttribute("data-periodeid")}"]`
|
||||||
)
|
)
|
||||||
.forEach((e) => e.remove());
|
.forEach((e) => e.remove());
|
||||||
// On supprime la période de la Map periodes
|
// On supprime la période de la Map periodes
|
||||||
@ -195,11 +228,11 @@ async function nouvellePeriode(period = null) {
|
|||||||
periodeDiv.appendChild(modP);
|
periodeDiv.appendChild(modP);
|
||||||
periodeDiv.appendChild(close);
|
periodeDiv.appendChild(close);
|
||||||
periodeDiv.setAttribute("data-periodeid", periodId);
|
periodeDiv.setAttribute("data-periodeid", periodId);
|
||||||
document.getElementById("tableau-periode").appendChild(periodeDiv);
|
document.getElementById("tete-table").appendChild(periodeDiv);
|
||||||
|
|
||||||
// On récupère les étudiants (etudids)
|
// On récupère les étudiants (etudids)
|
||||||
let etudids = [
|
let etudids = [
|
||||||
...document.querySelectorAll("#tableau-periode .header[data-etudid]"),
|
...document.querySelectorAll(".ligne[data-etudid]"),
|
||||||
].map((e) => e.getAttribute("data-etudid"));
|
].map((e) => e.getAttribute("data-etudid"));
|
||||||
|
|
||||||
// On génère une date de début et de fin de la période
|
// On génère une date de début et de fin de la période
|
||||||
@ -249,7 +282,7 @@ async function nouvellePeriode(period = null) {
|
|||||||
cell.setAttribute("data-etudid", etudid);
|
cell.setAttribute("data-etudid", etudid);
|
||||||
cell.setAttribute("data-periodeid", periodId);
|
cell.setAttribute("data-periodeid", periodId);
|
||||||
cell.id = `cell-${etudid}-${periodId}`;
|
cell.id = `cell-${etudid}-${periodId}`;
|
||||||
document.getElementById("tableau-periode").appendChild(cell);
|
document.querySelector(`.ligne[data-etudid="${etudid}"]`).appendChild(cell);
|
||||||
|
|
||||||
//Vérification inscription au module
|
//Vérification inscription au module
|
||||||
// Si l'étudiant n'est pas inscrit, on le notifie et on passe à l'étudiant suivant
|
// Si l'étudiant n'est pas inscrit, on le notifie et on passe à l'étudiant suivant
|
||||||
@ -265,6 +298,10 @@ async function nouvellePeriode(period = null) {
|
|||||||
const assiduites = data[etudid];
|
const assiduites = data[etudid];
|
||||||
// Si l'étudiant n'a pas d'assiduité, on crée les boutons assiduité
|
// Si l'étudiant n'a pas d'assiduité, on crée les boutons assiduité
|
||||||
if (assiduites.length == 0) {
|
if (assiduites.length == 0) {
|
||||||
|
|
||||||
|
const assi_btns = document.createElement('div');
|
||||||
|
assi_btns.classList.add('assi-btns');
|
||||||
|
|
||||||
["present", "retard", "absent"].forEach((value) => {
|
["present", "retard", "absent"].forEach((value) => {
|
||||||
const cbox = document.createElement("input");
|
const cbox = document.createElement("input");
|
||||||
cbox.type = "checkbox";
|
cbox.type = "checkbox";
|
||||||
@ -284,8 +321,9 @@ async function nouvellePeriode(period = null) {
|
|||||||
// Si une valeur par défaut est donnée alors on l'applique
|
// Si une valeur par défaut est donnée alors on l'applique
|
||||||
cbox.checked = etatDef.value == value;
|
cbox.checked = etatDef.value == value;
|
||||||
|
|
||||||
cell.appendChild(cbox);
|
assi_btns.appendChild(cbox);
|
||||||
});
|
});
|
||||||
|
cell.appendChild(assi_btns);
|
||||||
} else {
|
} else {
|
||||||
// Si une (ou plus) assiduité sont trouvée pour la période
|
// Si une (ou plus) assiduité sont trouvée pour la période
|
||||||
// alors on affiche les informations de la première assiduité
|
// alors on affiche les informations de la première assiduité
|
||||||
@ -297,6 +335,8 @@ async function nouvellePeriode(period = null) {
|
|||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById("tableau-periode").classList.remove("hidden");
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Permet de récupérer la saisie puis créer les assiduités grâce à l'api
|
* Permet de récupérer la saisie puis créer les assiduités grâce à l'api
|
||||||
@ -337,6 +377,7 @@ function sauvegarderAssiduites() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Une fois les assiduités générées, on les envoie à l'api
|
// Une fois les assiduités générées, on les envoie à l'api
|
||||||
async_post(
|
async_post(
|
||||||
"../../api/assiduites/create",
|
"../../api/assiduites/create",
|
||||||
@ -344,7 +385,7 @@ function sauvegarderAssiduites() {
|
|||||||
// Si la requête passe
|
// Si la requête passe
|
||||||
async (data) => {
|
async (data) => {
|
||||||
// On supprime toutes les cases du tableau pour le mettre à jour
|
// On supprime toutes les cases du tableau pour le mettre à jour
|
||||||
document.querySelectorAll(".cell").forEach((e) => e.remove());
|
document.querySelectorAll("[data-periodeid]").forEach((e)=>e.remove())
|
||||||
|
|
||||||
// On recrée les périodes
|
// On recrée les périodes
|
||||||
// (cela permet de redemander les assiduités, donc mettre à jour les cases)
|
// (cela permet de redemander les assiduités, donc mettre à jour les cases)
|
||||||
@ -402,7 +443,7 @@ function sauvegarderAssiduites() {
|
|||||||
const period = periodes.get(periodeId);
|
const period = periodes.get(periodeId);
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
// On affiche la période
|
// On affiche la période
|
||||||
li.textContent = `Période du ${period.date_debut.format(
|
li.textContent = `Plage du ${period.date_debut.format(
|
||||||
"DD/MM/YYYY HH:mm"
|
"DD/MM/YYYY HH:mm"
|
||||||
)} à ${period.date_fin.format("HH:mm")}`;
|
)} à ${period.date_fin.format("HH:mm")}`;
|
||||||
|
|
||||||
@ -474,7 +515,8 @@ 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() {
|
||||||
afficherPDP(pdp.checked);
|
const checked = localStorage.getItem("scodoc-signal_assiduites_diff-pdp") == "true";
|
||||||
|
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é
|
||||||
dateCouranteEstTravaillee();
|
dateCouranteEstTravaillee();
|
||||||
@ -497,8 +539,8 @@ main();
|
|||||||
|
|
||||||
|
|
||||||
<div id="fix">
|
<div id="fix">
|
||||||
<!-- Nouvelle période
|
<!-- Nouvelle Plage
|
||||||
Permet de créer une nouvelle ligne pour une nouvelle période
|
Permet de créer une nouvelle ligne pour une nouvelle Plage
|
||||||
(
|
(
|
||||||
Jour, -> datepicker
|
Jour, -> datepicker
|
||||||
Heure de début, -> timepicker
|
Heure de début, -> timepicker
|
||||||
@ -529,38 +571,34 @@ main();
|
|||||||
{{moduleimpl_select | safe}}
|
{{moduleimpl_select | safe}}
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button id="add_periode" onclick="nouvellePeriode()">Ajouter une période</button>
|
<button id="add_periode" onclick="nouvellePeriode()">Ajouter une plage</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Boutons d'actions
|
||||||
<!-- Boutons d'actions
|
|
||||||
- Sauvegarder
|
- Sauvegarder
|
||||||
- Afficher la photo de profil
|
- Afficher la photo de profil
|
||||||
- Assiduité par défaut (aucune, present, retard, absent)
|
- Assiduité par défaut (aucune, present, retard, absent)
|
||||||
--->
|
--->
|
||||||
|
<br>
|
||||||
<div id="actions" class="box">
|
<div id="actions" class="flex">
|
||||||
|
<button id="save" onclick="sauvegarderAssiduites()">ENREGISTRER</button>
|
||||||
<label for="pdp">
|
<label for="pdp">
|
||||||
Photo de profil :
|
Photo de profil :
|
||||||
<input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)">
|
<input type="checkbox" name="pdp" id="pdp" checked onclick="afficherPDP(this.checked)">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label for="etatDef">
|
<label for="etatDef">
|
||||||
Assiduité par défaut :
|
Intialiser les étudiants comme :
|
||||||
<select name="etatDef" id="etatDef">
|
<select name="etatDef" id="etatDef">
|
||||||
<option value="">Aucune</option>
|
<option value="">-</option>
|
||||||
<option value="present">Présence</option>
|
<option value="present">présents</option>
|
||||||
<option value="retard">Retard</option>
|
<option value="retard">en retard</option>
|
||||||
<option value="absent">Absence</option>
|
<option value="absent">absents</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button id="save" onclick="sauvegarderAssiduites()">Sauvegarder l'assiduité</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<!-- Tableau à double entrée
|
<!-- Tableau à double entrée
|
||||||
Colonne : Etudiants (Header = Nom, Prénom, Photo (si actif))
|
Colonne : Etudiants (Header = Nom, Prénom, Photo (si actif))
|
||||||
Ligne : Période (Header = Jour, Heure de début, Heure de fin, ModuleImplId)
|
Ligne : Période (Header = Jour, Heure de début, Heure de fin, ModuleImplId)
|
||||||
@ -570,17 +608,27 @@ main();
|
|||||||
--->
|
--->
|
||||||
|
|
||||||
<div id="tableau-periode" class="grid-table">
|
<div id="tableau-periode" class="grid-table">
|
||||||
<!-- Header de la première colonne -->
|
<!-- Première ligne : Plages -->
|
||||||
<div class="header sticky">Période</div>
|
<div class="ligne" id="tete-table">
|
||||||
<!-- Headers des autres colonnes (noms des étudiants) -->
|
<div class="cell header sticky">Étudiants</div>
|
||||||
|
{# <div class="cell header" periode-id="X">Plage X</div> #}
|
||||||
|
</div>
|
||||||
|
{# ... #}
|
||||||
|
|
||||||
|
<hr class="hidden" id="separator">
|
||||||
|
|
||||||
{% for etud in etudiants %}
|
{% for etud in etudiants %}
|
||||||
<div class="header etudinfo" data-etudid="{{etud.etudid}}" id="head-{{etud.etudid}}">
|
<div class="ligne" data-etudid="{{etud.etudid}}">
|
||||||
|
<div class="cell etudinfo sticky" id="head-{{etud.etudid}}">
|
||||||
<img src="../../api/etudiant/etudid/{{etud.etudid}}/photo?size=small" alt="{{etud.nomprenom}}" class="pdp">
|
<img src="../../api/etudiant/etudid/{{etud.etudid}}/photo?size=small" alt="{{etud.nomprenom}}" class="pdp">
|
||||||
<span>{{ etud.nomprenom }}</span>
|
<span>{{ etud.nomprenom }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{# <div class="cell" periode-id="X">Assiduité Plage 1</div> #}
|
||||||
|
|
||||||
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<!-- Sera remplis avec les nouvelles périodes -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -186,6 +186,12 @@ def bilan_dept():
|
|||||||
if not table[0]:
|
if not table[0]:
|
||||||
return table[1]
|
return table[1]
|
||||||
|
|
||||||
|
# Récupération des formsemestres (pour le menu déroulant)
|
||||||
|
formsemestres: Query = FormSemestre.get_dept_formsemestres_courants(dept)
|
||||||
|
formsemestres_choices: dict[int, str] = {
|
||||||
|
fs.id: fs.titre_annee() for fs in formsemestres
|
||||||
|
}
|
||||||
|
|
||||||
# Peuplement du template jinja
|
# Peuplement du template jinja
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/pages/bilan_dept.j2",
|
"assiduites/pages/bilan_dept.j2",
|
||||||
@ -193,6 +199,8 @@ def bilan_dept():
|
|||||||
search_etud=sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"),
|
search_etud=sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"),
|
||||||
billets=billets,
|
billets=billets,
|
||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
|
formsemestres=formsemestres_choices,
|
||||||
|
formsemestre_id=None if not formsemestre else formsemestre.id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1565,6 +1573,85 @@ def _prepare_tableau(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/recup_assiduites_plage", methods=["POST"])
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.AbsChange)
|
||||||
|
def recup_assiduites_plage():
|
||||||
|
"""
|
||||||
|
Renvoie un fichier excel contenant toutes les assiduités d'une plage
|
||||||
|
La plage est définie par les valeurs "datedeb" et "datefin" du formulaire
|
||||||
|
Par défaut tous les étudiants du département sont concernés
|
||||||
|
Si le champs "formsemestre_id" est présent dans le formulaire et est non vide,
|
||||||
|
seuls les étudiants inscrits dans ce semestre sont concernés.
|
||||||
|
"""
|
||||||
|
|
||||||
|
date_deb: datetime.datetime = request.form.get("datedeb")
|
||||||
|
date_fin: datetime.datetime = request.form.get("datefin")
|
||||||
|
|
||||||
|
# Vérification des dates
|
||||||
|
try:
|
||||||
|
date_deb = datetime.datetime.strptime(date_deb, "%d/%m/%Y")
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ScoValueError("date_debut invalide", dest_url=request.referrer) from exc
|
||||||
|
try:
|
||||||
|
date_fin = datetime.datetime.strptime(date_fin, "%d/%m/%Y")
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ScoValueError("date_fin invalide", dest_url=request.referrer) from exc
|
||||||
|
|
||||||
|
# Récupération des étudiants
|
||||||
|
etuds: Query = []
|
||||||
|
formsemestre_id: str | None = request.form.get("formsemestre_id")
|
||||||
|
|
||||||
|
name: str = ""
|
||||||
|
|
||||||
|
if formsemestre_id is not None and formsemestre_id != "":
|
||||||
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
|
etuds = formsemestre.etuds
|
||||||
|
name = formsemestre.session_id()
|
||||||
|
else:
|
||||||
|
dept: Departement = Departement.query.get_or_404(g.scodoc_dept_id)
|
||||||
|
etuds = dept.etudiants
|
||||||
|
name = dept.acronym
|
||||||
|
|
||||||
|
# Récupération des assiduités
|
||||||
|
assiduites: Query = Assiduite.query.filter(
|
||||||
|
Assiduite.etudid.in_([etud.id for etud in etuds])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Filtrage des assiduités en fonction des dates données
|
||||||
|
assiduites = scass.filter_by_date(assiduites, Assiduite, date_deb, date_fin)
|
||||||
|
|
||||||
|
table_data: liste_assi.AssiJustifData = liste_assi.AssiJustifData(
|
||||||
|
assiduites_query=assiduites,
|
||||||
|
)
|
||||||
|
|
||||||
|
options: liste_assi.AssiDisplayOptions = liste_assi.AssiDisplayOptions(
|
||||||
|
show_pres=True,
|
||||||
|
show_reta=True,
|
||||||
|
show_module=True,
|
||||||
|
show_etu=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
date_deb_str: str = date_deb.strftime("%d-%m-%Y")
|
||||||
|
date_fin_str: str = date_fin.strftime("%d-%m-%Y")
|
||||||
|
|
||||||
|
filename: str = f"assiduites_{name}_{date_deb_str}_{date_fin_str}"
|
||||||
|
|
||||||
|
tableau: liste_assi.ListeAssiJusti = liste_assi.ListeAssiJusti(
|
||||||
|
table_data,
|
||||||
|
options=options,
|
||||||
|
titre="tableau-dept-" + filename,
|
||||||
|
no_pagination=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return scu.send_file(
|
||||||
|
tableau.excel(),
|
||||||
|
filename=filename,
|
||||||
|
mime=scu.XLSX_MIMETYPE,
|
||||||
|
suffix=scu.XLSX_SUFFIX,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/tableau_assiduite_actions", methods=["GET", "POST"])
|
@bp.route("/tableau_assiduite_actions", methods=["GET", "POST"])
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.AbsChange)
|
@permission_required(Permission.AbsChange)
|
||||||
|
Loading…
Reference in New Issue
Block a user