forked from ScoDoc/ScoDoc
Merge branch 'iziram-modif_assi'
This commit is contained in:
commit
5c6935337e
@ -353,12 +353,12 @@ class Assiduite(ScoDocModel):
|
|||||||
|
|
||||||
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 (
|
||||||
"Tout module"
|
"Autre module (pas dans la liste)"
|
||||||
if self.external_data["module"] == "Autre"
|
if self.external_data["module"] == "Autre"
|
||||||
else self.external_data["module"]
|
else self.external_data["module"]
|
||||||
)
|
)
|
||||||
|
|
||||||
return "Non spécifié" if traduire else None
|
return "Module non spécifié" if traduire else None
|
||||||
|
|
||||||
def get_saisie(self) -> str:
|
def get_saisie(self) -> str:
|
||||||
"""
|
"""
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
#
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""HTML Header/Footer for ScoDoc pages
|
"""HTML Header/Footer for ScoDoc pages"""
|
||||||
"""
|
|
||||||
|
|
||||||
import html
|
import html
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ _HTML_BEGIN = f"""<!DOCTYPE html>
|
|||||||
<script src="{scu.STATIC_DIR}/libjs/menu.js"></script>
|
<script src="{scu.STATIC_DIR}/libjs/menu.js"></script>
|
||||||
<script src="{scu.STATIC_DIR}/libjs/bubble.js"></script>
|
<script src="{scu.STATIC_DIR}/libjs/bubble.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload=function(){{enableTooltips("gtrcontent")}};
|
window.onload=function(){{enableTooltips("gtrcontent"); enableTooltips("sidebar");}};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="{scu.STATIC_DIR}/jQuery/jquery.js"></script>
|
<script src="{scu.STATIC_DIR}/jQuery/jquery.js"></script>
|
||||||
@ -218,7 +217,7 @@ def sco_header(
|
|||||||
<script src="{scu.STATIC_DIR}/libjs/menu.js"></script>
|
<script src="{scu.STATIC_DIR}/libjs/menu.js"></script>
|
||||||
<script src="{scu.STATIC_DIR}/libjs/bubble.js"></script>
|
<script src="{scu.STATIC_DIR}/libjs/bubble.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload=function(){{enableTooltips("gtrcontent")}};
|
window.onload=function(){{enableTooltips("gtrcontent"); enableTooltips("sidebar");}};
|
||||||
|
|
||||||
const SCO_URL="{url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)}";
|
const SCO_URL="{url_for("scolar.index_html", scodoc_dept=g.scodoc_dept)}";
|
||||||
const SCO_TIMEZONE="{scu.TIME_ZONE}";
|
const SCO_TIMEZONE="{scu.TIME_ZONE}";
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
"""
|
"""
|
||||||
Génération de la "sidebar" (marge gauche des pages HTML)
|
Génération de la "sidebar" (marge gauche des pages HTML)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import render_template, url_for
|
from flask import render_template, url_for
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
@ -151,7 +152,7 @@ def sidebar(etudid: int = None):
|
|||||||
H = [
|
H = [
|
||||||
f"""
|
f"""
|
||||||
<!-- sidebar py -->
|
<!-- sidebar py -->
|
||||||
<div class="sidebar">
|
<div class="sidebar" id="sidebar">
|
||||||
{ sidebar_common() }
|
{ sidebar_common() }
|
||||||
<div class="box-chercheetud">Chercher étudiant:<br>
|
<div class="box-chercheetud">Chercher étudiant:<br>
|
||||||
<form method="get" id="form-chercheetud"
|
<form method="get" id="form-chercheetud"
|
||||||
@ -193,7 +194,7 @@ def sidebar(etudid: int = None):
|
|||||||
formsemestre.date_debut.strftime(scu.DATE_FMT)
|
formsemestre.date_debut.strftime(scu.DATE_FMT)
|
||||||
} au {
|
} au {
|
||||||
formsemestre.date_fin.strftime(scu.DATE_FMT)
|
formsemestre.date_fin.strftime(scu.DATE_FMT)
|
||||||
}">({
|
}" data-tooltip>({
|
||||||
sco_preferences.get_preference("assi_metrique", None)})
|
sco_preferences.get_preference("assi_metrique", None)})
|
||||||
<br>{nbabsjust:1g} J., {nbabsnj:1g} N.J.</span>"""
|
<br>{nbabsjust:1g} J., {nbabsnj:1g} N.J.</span>"""
|
||||||
)
|
)
|
||||||
@ -227,12 +228,9 @@ def sidebar(etudid: int = None):
|
|||||||
<li><a href="{ url_for('assiduites.calendrier_assi_etud',
|
<li><a href="{ url_for('assiduites.calendrier_assi_etud',
|
||||||
scodoc_dept=g.scodoc_dept, etudid=etudid)
|
scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
}">Calendrier</a></li>
|
}">Calendrier</a></li>
|
||||||
<li><a href="{ url_for('assiduites.liste_assiduites_etud',
|
|
||||||
scodoc_dept=g.scodoc_dept, etudid=etudid)
|
|
||||||
}">Liste</a></li>
|
|
||||||
<li><a href="{ url_for('assiduites.bilan_etud',
|
<li><a href="{ url_for('assiduites.bilan_etud',
|
||||||
scodoc_dept=g.scodoc_dept, etudid=etudid)
|
scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
}">Bilan</a></li>
|
}" title="Les pages bilan et liste ont été fusionnées">Liste/Bilan</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
@ -822,6 +822,35 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sem-groups-assi">
|
<div class="sem-groups-assi">
|
||||||
|
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
if can_edit_abs:
|
||||||
|
H.append(
|
||||||
|
f"""
|
||||||
|
<div>
|
||||||
|
<a class="stdlink" href="{
|
||||||
|
url_for("assiduites.signal_assiduites_group",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
jour=datetime.date.today().isoformat(),
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
group_ids=group.id,
|
||||||
|
)}">
|
||||||
|
Saisir l'assiduité</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a class="stdlink" href="{
|
||||||
|
url_for("assiduites.bilan_dept",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
group_ids=group.id,
|
||||||
|
)}">
|
||||||
|
Justificatifs en attente</a>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
H.append(
|
||||||
|
f"""
|
||||||
<div>
|
<div>
|
||||||
<a class="stdlink" href="{
|
<a class="stdlink" href="{
|
||||||
url_for("assiduites.visu_assi_group",
|
url_for("assiduites.visu_assi_group",
|
||||||
@ -834,48 +863,20 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
|
|||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
if can_edit_abs:
|
if can_edit_abs:
|
||||||
H.append(
|
H.append(
|
||||||
f"""
|
f"""
|
||||||
<div>
|
<div>
|
||||||
<a class="stdlink" href="{
|
|
||||||
url_for("assiduites.visu_assiduites_group",
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
formsemestre_id=formsemestre.id,
|
|
||||||
jour = datetime.date.today().isoformat(),
|
|
||||||
group_ids=group.id,
|
|
||||||
)}">
|
|
||||||
Visualiser</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a class="stdlink" href="{
|
|
||||||
url_for("assiduites.signal_assiduites_group",
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
jour=datetime.date.today().isoformat(),
|
|
||||||
formsemestre_id=formsemestre.id,
|
|
||||||
group_ids=group.id,
|
|
||||||
)}">
|
|
||||||
Saisie journalière</a>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a class="stdlink" href="{
|
<a class="stdlink" href="{
|
||||||
url_for("assiduites.signal_assiduites_diff",
|
url_for("assiduites.signal_assiduites_diff",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
group_ids=group.id,
|
group_ids=group.id,
|
||||||
)}">
|
)}" title="Page en cours de fusion et sera prochainement supprimée. Veuillez utiliser la page `Saisir l'assiduité`">
|
||||||
Saisie différée</a>
|
(Saisie différée)</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
"""
|
||||||
<a class="stdlink" href="{
|
|
||||||
url_for("assiduites.bilan_dept",
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
formsemestre_id=formsemestre.id,
|
|
||||||
group_ids=group.id,
|
|
||||||
)}">
|
|
||||||
Justificatifs en attente</a>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
H.append("</div>") # /sem-groups-assi
|
H.append("</div>") # /sem-groups-assi
|
||||||
|
@ -110,6 +110,7 @@ get_base_preferences(formsemestre_id)
|
|||||||
Return base preferences for current scodoc_dept (instance BasePreferences)
|
Return base preferences for current scodoc_dept (instance BasePreferences)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
from flask import current_app, flash, g, request, url_for
|
from flask import current_app, flash, g, request, url_for
|
||||||
|
|
||||||
@ -622,18 +623,6 @@ class BasePreferences:
|
|||||||
"explanation": "Désactive la saisie et l'affichage des présences",
|
"explanation": "Désactive la saisie et l'affichage des présences",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"periode_defaut",
|
|
||||||
{
|
|
||||||
"initvalue": 2.0,
|
|
||||||
"size": 10,
|
|
||||||
"title": "Durée par défaut d'un créneau",
|
|
||||||
"type": "float",
|
|
||||||
"category": "assi",
|
|
||||||
"only_global": True,
|
|
||||||
"explanation": "Durée d'un créneau en heure. Utilisé dans les pages de saisie",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"nb_heures_par_jour",
|
"nb_heures_par_jour",
|
||||||
{
|
{
|
||||||
@ -2302,9 +2291,7 @@ class BasePreferences:
|
|||||||
if "explanation" in descr:
|
if "explanation" in descr:
|
||||||
del descr["explanation"]
|
del descr["explanation"]
|
||||||
if formsemestre_id:
|
if formsemestre_id:
|
||||||
descr[
|
descr["explanation"] = f"""ou <span class="spanlink"
|
||||||
"explanation"
|
|
||||||
] = f"""ou <span class="spanlink"
|
|
||||||
onclick="set_global_pref(this, '{pref_name}');"
|
onclick="set_global_pref(this, '{pref_name}');"
|
||||||
>utiliser paramètre global</span>"""
|
>utiliser paramètre global</span>"""
|
||||||
if formsemestre_id and self.is_global(formsemestre_id, pref_name):
|
if formsemestre_id and self.is_global(formsemestre_id, pref_name):
|
||||||
|
@ -302,7 +302,6 @@
|
|||||||
.rbtn {
|
.rbtn {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,9 +326,12 @@
|
|||||||
background-image: url(../icons/absent.svg);
|
background-image: url(../icons/absent.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.rbtn.aucun::before {
|
.rbtn.aucun {
|
||||||
background-image: url(../icons/aucun.svg);
|
background-image: url("../icons/delete.svg");
|
||||||
background-color: var(--color-defaut-dark);
|
background-size: calc(100% - 8px) calc(100% - 8px);
|
||||||
|
/* Adjust size to create "margin" */
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rbtn.retard::before {
|
.rbtn.retard::before {
|
||||||
|
1
app/static/icons/delete.svg
Normal file
1
app/static/icons/delete.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" height="512" viewBox="0 0 512 512" width="512" xmlns="http://www.w3.org/2000/svg" data-name="Layer 1"><path d="m170.8 14.221a14.21 14.21 0 0 1 14.2-14.207l141.991-.008a14.233 14.233 0 0 1 14.2 14.223v35.117h-170.391zm233.461 477.443a21.75 21.75 0 0 1 -21.856 20.33h-254.451a21.968 21.968 0 0 1 -21.854-20.416l-21.774-318.518h343.174l-23.234 318.6zm56.568-347.452h-409.658v-33a33.035 33.035 0 0 1 33.005-33.012l343.644-.011a33.051 33.051 0 0 1 33 33.02v33zm-270.79 291.851a14.422 14.422 0 1 0 28.844 0v-202.247a14.42 14.42 0 0 0 -28.839-.01v202.257zm102.9 0a14.424 14.424 0 1 0 28.848 0v-202.247a14.422 14.422 0 0 0 -28.843-.01z" fill="#fc3333" fill-rule="evenodd"/></svg>
|
After Width: | Height: | Size: 689 B |
@ -53,7 +53,7 @@ async function async_get(path, success, errors) {
|
|||||||
* @param {CallableFunction} errors fonction à effectuer en cas d'échec
|
* @param {CallableFunction} errors fonction à effectuer en cas d'échec
|
||||||
*/
|
*/
|
||||||
async function async_post(path, data, success, errors) {
|
async function async_post(path, data, success, errors) {
|
||||||
console.log("async_post " + path);
|
// console.log("async_post " + path);
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await fetch(path, {
|
response = await fetch(path, {
|
||||||
@ -401,7 +401,7 @@ async function creerTousLesEtudiants(etuds) {
|
|||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
async function getModuleImpl(assiduite) {
|
async function getModuleImpl(assiduite) {
|
||||||
if (assiduite == null) return "Pas de module";
|
if (assiduite == null) return "Module non spécifié";
|
||||||
const id = assiduite.moduleimpl_id;
|
const id = assiduite.moduleimpl_id;
|
||||||
|
|
||||||
if (id == null || id == undefined) {
|
if (id == null || id == undefined) {
|
||||||
@ -414,7 +414,7 @@ async function getModuleImpl(assiduite) {
|
|||||||
? "Autre module (pas dans la liste)"
|
? "Autre module (pas dans la liste)"
|
||||||
: assiduite.external_data.module;
|
: assiduite.external_data.module;
|
||||||
} else {
|
} else {
|
||||||
return "Pas de module";
|
return "Module non spécifié";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -643,6 +643,9 @@ function mettreToutLeMonde(etat, el = null) {
|
|||||||
|
|
||||||
// Suppression des assiduités
|
// Suppression des assiduités
|
||||||
if (etat == "vide") {
|
if (etat == "vide") {
|
||||||
|
if (!confirm("Effacer tout les évènements correspondant à cette plage ?")) {
|
||||||
|
return; // annulation
|
||||||
|
}
|
||||||
const assiduites_id = lignesEtuds
|
const assiduites_id = lignesEtuds
|
||||||
.filter((e) => e.getAttribute("type") == "edition")
|
.filter((e) => e.getAttribute("type") == "edition")
|
||||||
.map((e) => Number(e.getAttribute("assiduite_id")));
|
.map((e) => Number(e.getAttribute("assiduite_id")));
|
||||||
@ -759,6 +762,7 @@ function envoiToastEtudiant(etat, etud) {
|
|||||||
pushToast(generateToast(span, getToastColorFromEtat(etat.toUpperCase()), 5));
|
pushToast(generateToast(span, getToastColorFromEtat(etat.toUpperCase()), 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO commenter toutes les fonctions js
|
||||||
function envoiToastTous(etat, count) {
|
function envoiToastTous(etat, count) {
|
||||||
const span = document.createElement("span");
|
const span = document.createElement("span");
|
||||||
let etatAffiche = etat;
|
let etatAffiche = etat;
|
||||||
@ -798,13 +802,16 @@ function estJourTravail(jour, nonWorkdays) {
|
|||||||
return !nonWorkdays.includes(d);
|
return !nonWorkdays.includes(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
function retourJourTravail(date) {
|
function retourJourTravail(date, anti = true) {
|
||||||
const jourMiliSecondes = 86400000; // 24 * 3600 * 1000 | H * s * ms
|
const jourMiliSecondes = 86400000; // 24 * 3600 * 1000 | H * s * ms
|
||||||
let jour = date;
|
let jour = date;
|
||||||
let compte = 0;
|
let compte = 0;
|
||||||
|
|
||||||
while (!estJourTravail(jour, nonWorkDays) && compte++ < 7) {
|
while (!estJourTravail(jour, nonWorkDays) && compte++ < 7) {
|
||||||
jour = new Date(jour - jourMiliSecondes);
|
let temps = anti
|
||||||
|
? jour - jourMiliSecondes
|
||||||
|
: jour.valueOf() + jourMiliSecondes;
|
||||||
|
jour = new Date(temps);
|
||||||
}
|
}
|
||||||
return jour;
|
return jour;
|
||||||
}
|
}
|
||||||
@ -814,9 +821,12 @@ function dateCouranteEstTravaillee() {
|
|||||||
if (!estJourTravail(date, nonWorkDays)) {
|
if (!estJourTravail(date, nonWorkDays)) {
|
||||||
const nouvelleDate = retourJourTravail(date);
|
const nouvelleDate = retourJourTravail(date);
|
||||||
$("#date").datepicker("setDate", nouvelleDate);
|
$("#date").datepicker("setDate", nouvelleDate);
|
||||||
|
let msg = "Le jour sélectionné";
|
||||||
|
if ((new Date()).format("YYYY-MM-DD") == date.format("YYYY-MM-DD")) {
|
||||||
|
msg = "Aujourd'hui";
|
||||||
|
}
|
||||||
const att = document.createTextNode(
|
const att = document.createTextNode(
|
||||||
`Le jour sélectionné (${Date.toFRA(
|
`${msg} (${Date.toFRA(
|
||||||
date.format("YYYY-MM-DD")
|
date.format("YYYY-MM-DD")
|
||||||
)}) n'est pas un jour travaillé.`
|
)}) n'est pas un jour travaillé.`
|
||||||
);
|
);
|
||||||
@ -837,6 +847,17 @@ function dateCouranteEstTravaillee() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function jourSuivant(anti = false) {
|
||||||
|
let date = $("#date").datepicker("getDate");
|
||||||
|
|
||||||
|
date = anti ? date.add(-1, "days") : date.add(1, "days");
|
||||||
|
|
||||||
|
const nouvelleDate = retourJourTravail(date, anti);
|
||||||
|
|
||||||
|
$("#date").datepicker("setDate", nouvelleDate);
|
||||||
|
creerTousLesEtudiants(etuds);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ajout de la visualisation des assiduités de la mini timeline
|
* Ajout de la visualisation des assiduités de la mini timeline
|
||||||
* @param {HTMLElement} el l'élément survollé
|
* @param {HTMLElement} el l'élément survollé
|
||||||
@ -876,6 +897,11 @@ function setupAssiduiteBubble(el, assiduite) {
|
|||||||
actionsDiv.appendChild(infos);
|
actionsDiv.appendChild(infos);
|
||||||
bubble.appendChild(actionsDiv);
|
bubble.appendChild(actionsDiv);
|
||||||
|
|
||||||
|
const stateDiv = document.createElement("div");
|
||||||
|
stateDiv.className = "assiduite-state";
|
||||||
|
stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
|
||||||
|
bubble.appendChild(stateDiv);
|
||||||
|
|
||||||
const idDiv = document.createElement("div");
|
const idDiv = document.createElement("div");
|
||||||
idDiv.className = "assiduite-id";
|
idDiv.className = "assiduite-id";
|
||||||
getModuleImpl(assiduite).then((modImpl) => {
|
getModuleImpl(assiduite).then((modImpl) => {
|
||||||
@ -883,26 +909,32 @@ function setupAssiduiteBubble(el, assiduite) {
|
|||||||
});
|
});
|
||||||
bubble.appendChild(idDiv);
|
bubble.appendChild(idDiv);
|
||||||
|
|
||||||
const periodDivDeb = document.createElement("div");
|
// Affichage des dates
|
||||||
periodDivDeb.className = "assiduite-period";
|
// si les jours sont les mêmes, on affiche "jour hh:mm - hh:mm"
|
||||||
periodDivDeb.textContent = `${formatDateModal(assiduite.date_debut)}`;
|
// sinon on affiche "jour hh:mm - jour hh:mm"
|
||||||
bubble.appendChild(periodDivDeb);
|
const periodDiv = document.createElement("div");
|
||||||
const periodDivFin = document.createElement("div");
|
periodDiv.className = "assiduite-period";
|
||||||
periodDivFin.className = "assiduite-period";
|
const dateDeb = new Date(Date.removeUTC(assiduite.date_debut));
|
||||||
periodDivFin.textContent = `${formatDateModal(assiduite.date_fin)}`;
|
const dateFin = new Date(Date.removeUTC(assiduite.date_fin));
|
||||||
bubble.appendChild(periodDivFin);
|
if (dateDeb.isSame(dateFin, "day")) {
|
||||||
|
const jour = dateDeb.format("DD/MM/YYYY");
|
||||||
|
const deb = dateDeb.format("HH:mm");
|
||||||
|
const fin = dateFin.format("HH:mm");
|
||||||
|
periodDiv.textContent = `${jour} de ${deb} à ${fin}`;
|
||||||
|
} else {
|
||||||
|
const jourDeb = dateDeb.format("DD/MM/YYYY");
|
||||||
|
const jourFin = dateFin.format("DD/MM/YYYY");
|
||||||
|
periodDiv.textContent = `du ${jourDeb} au ${jourFin}`;
|
||||||
|
}
|
||||||
|
|
||||||
const stateDiv = document.createElement("div");
|
bubble.appendChild(periodDiv);
|
||||||
stateDiv.className = "assiduite-state";
|
|
||||||
stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
|
|
||||||
bubble.appendChild(stateDiv);
|
|
||||||
|
|
||||||
const motifDiv = document.createElement("div");
|
const motifDiv = document.createElement("div");
|
||||||
stateDiv.className = "assiduite-why";
|
motifDiv.className = "assiduite-why";
|
||||||
const motif = ["", null, undefined].includes(assiduite.desc)
|
const motif = ["", null, undefined].includes(assiduite.desc)
|
||||||
? "Pas de motif"
|
? "Non spécifié"
|
||||||
: assiduite.desc.capitalize();
|
: assiduite.desc.capitalize();
|
||||||
stateDiv.textContent = `Motif: ${motif}`;
|
motifDiv.textContent = `Motif: ${motif}`;
|
||||||
bubble.appendChild(motifDiv);
|
bubble.appendChild(motifDiv);
|
||||||
|
|
||||||
const userIdDiv = document.createElement("div");
|
const userIdDiv = document.createElement("div");
|
||||||
|
@ -11,8 +11,8 @@ h.id="btc";
|
|||||||
h.setAttribute("id","btc");
|
h.setAttribute("id","btc");
|
||||||
h.style.position="absolute";
|
h.style.position="absolute";
|
||||||
document.getElementsByTagName("body")[0].appendChild(h);
|
document.getElementsByTagName("body")[0].appendChild(h);
|
||||||
if(id==null) links=document.getElementsByTagName("a");
|
if(id==null) links=document.querySelectorAll("a, [data-tooltip]"); // was document.getElementsByTagName("a")
|
||||||
else links=document.getElementById(id).getElementsByTagName("a");
|
else links=document.getElementById(id).querySelectorAll("a, [data-tooltip]");// was document.getElementById(id).getElementsByTagName("a")
|
||||||
for(i=0;i<links.length;i++){
|
for(i=0;i<links.length;i++){
|
||||||
Prepare(links[i]);
|
Prepare(links[i]);
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
<h1>Traitement de l'assiduité</h1>
|
<h1>Traitement de l'assiduité</h1>
|
||||||
<p class="help">
|
<p class="help">
|
||||||
Pour saisir l'assiduité ou consulter les états, il est recommandé de passer par
|
Pour saisir l'assiduité ou consulter les états, passer par
|
||||||
le semestre concerné (saisie par jour ou saisie différée).
|
le semestre concerné (saisie par jour ou saisie différée).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -86,9 +86,6 @@ Bilan assiduité de {{sco.etud.nomprenom}}
|
|||||||
|
|
||||||
<div class="scobox">
|
<div class="scobox">
|
||||||
<section class="nonvalide">
|
<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 }}
|
{{tableau | safe }}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@ -99,6 +96,9 @@ Bilan assiduité de {{sco.etud.nomprenom}}
|
|||||||
département)</p>
|
département)</p>
|
||||||
<p>Les statistiques sont calculées entre les deux dates sélectionnées. Après modification des dates,
|
<p>Les statistiques sont calculées entre les deux dates sélectionnées. Après modification des dates,
|
||||||
appuyer sur le bouton "Actualiser"</p>
|
appuyer sur le bouton "Actualiser"</p>
|
||||||
|
|
||||||
|
{% include "assiduites/explication_etats_justifs.j2" %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
{% extends "sco_page.j2" %}
|
|
||||||
|
|
||||||
{% block title %}
|
|
||||||
Assiduité de {{etud.nomprenom}}
|
|
||||||
{% endblock title %}
|
|
||||||
|
|
||||||
{% block styles %}
|
|
||||||
{{ super() }}
|
|
||||||
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
|
||||||
{% endblock styles %}
|
|
||||||
|
|
||||||
{% block scripts %}
|
|
||||||
{{ super() }}
|
|
||||||
<script src="{{scu.STATIC_DIR}}/js/date_utils.js"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block app_content %}
|
|
||||||
<div class="pageContent">
|
|
||||||
|
|
||||||
<h2>Liste de l'assiduité et des justificatifs de {{sco.etud.html_link_fiche()|safe}}</h2>
|
|
||||||
{{tableau | safe }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% include "assiduites/explication_etats_justifs.j2" %}
|
|
||||||
|
|
||||||
{% endblock app_content %}
|
|
@ -570,6 +570,13 @@ window.addEventListener("load", main);
|
|||||||
|
|
||||||
<h2>Signalement différé de l'assiduité {{gr |safe}}</h2>
|
<h2>Signalement différé de l'assiduité {{gr |safe}}</h2>
|
||||||
|
|
||||||
|
<div class="ue_warning">
|
||||||
|
Attention, cette page est en cours de fusion avec la page de Saisie journalière.
|
||||||
|
<br>
|
||||||
|
Vous pouvez dès à présent cliquer sur <a href="{{url_for('assiduites.signal_assiduites_group', scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, group_ids=group_ids)}}" target="_blank">ce lien</a> pour accéder à la nouvelle version.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="fix">
|
<div id="fix">
|
||||||
<!-- Nouvelle Plage
|
<!-- Nouvelle Plage
|
||||||
|
@ -105,6 +105,24 @@
|
|||||||
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.css">
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/libjs/bootstrap-multiselect-1.1.2/bootstrap-multiselect.min.css">
|
||||||
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
||||||
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/minitimeline.css">
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/minitimeline.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#retour-haut{
|
||||||
|
position: fixed;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 3em;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
html{
|
||||||
|
scroll-behavior: smooth !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
{% endblock styles %}
|
{% endblock styles %}
|
||||||
|
|
||||||
|
|
||||||
@ -113,6 +131,10 @@
|
|||||||
{{ minitimeline|safe }}
|
{{ minitimeline|safe }}
|
||||||
<section id="content">
|
<section id="content">
|
||||||
|
|
||||||
|
<a id="retour-haut" href="#gtrcontent">
|
||||||
|
⬆️
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="no-display">
|
<div class="no-display">
|
||||||
<span class="formsemestre_id">{{formsemestre_id}}</span>
|
<span class="formsemestre_id">{{formsemestre_id}}</span>
|
||||||
<span id="formsemestre_date_debut">{{formsemestre_date_debut}}</span>
|
<span id="formsemestre_date_debut">{{formsemestre_date_debut}}</span>
|
||||||
@ -131,12 +153,22 @@
|
|||||||
<div class="infos">
|
<div class="infos">
|
||||||
<div class="infos-button">Groupes : {{grp|safe}}</div>
|
<div class="infos-button">Groupes : {{grp|safe}}</div>
|
||||||
<div>
|
<div>
|
||||||
|
<button class="btn_date" onclick="jourSuivant(true)">
|
||||||
|
⇤
|
||||||
|
</button>
|
||||||
<input type="text" name="date" id="date" class="datepicker" value="{{date}}">
|
<input type="text" name="date" id="date" class="datepicker" value="{{date}}">
|
||||||
</div>
|
</div>
|
||||||
|
<button class="btn_date" onclick="jourSuivant(false)">
|
||||||
|
⇥
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div style="display: {{'none' if readonly == 'true' else 'block'}};">
|
<div style="display: {{'none' if readonly == 'true' else 'block'}};">
|
||||||
{{timeline|safe}}
|
{{timeline|safe}}
|
||||||
|
<div>
|
||||||
|
<button onclick="setPeriodValues(t_start, t_mid)">Matin</button>
|
||||||
|
<button onclick="setPeriodValues(t_mid, t_end)">Après-Midi</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if readonly == "false" %}
|
{% if readonly == "false" %}
|
||||||
@ -162,14 +194,14 @@
|
|||||||
<fieldset class="btns_field mass">
|
<fieldset class="btns_field mass">
|
||||||
{% if not non_present %}
|
{% if not non_present %}
|
||||||
<input type="checkbox" value="present" name="mass_btn_assiduites" id="mass_rbtn_present"
|
<input type="checkbox" value="present" name="mass_btn_assiduites" id="mass_rbtn_present"
|
||||||
class="rbtn present" onclick="mettreToutLeMonde('present', this)" title="Present">
|
class="rbtn present" onclick="mettreToutLeMonde('present', this)" title="Indique l'état Présent pour tous les étudiants" data-tooltip>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<input type="checkbox" value="retard" name="mass_btn_assiduites" id="mass_rbtn_retard"
|
<input type="checkbox" value="retard" name="mass_btn_assiduites" id="mass_rbtn_retard"
|
||||||
class="rbtn retard" onclick="mettreToutLeMonde('retard', this)" title="Retard">
|
class="rbtn retard" onclick="mettreToutLeMonde('retard', this)" title="Indique l'état Retard pour tous les étudiants" data-tooltip>
|
||||||
<input type="checkbox" value="absent" name="mass_btn_assiduites" id="mass_rbtn_absent"
|
<input type="checkbox" value="absent" name="mass_btn_assiduites" id="mass_rbtn_absent"
|
||||||
class="rbtn absent" onclick="mettreToutLeMonde('absent', this)" title="Absent">
|
class="rbtn absent" onclick="mettreToutLeMonde('absent', this)" title="Indique l'état Absent pour tous les étudiants" data-tooltip>
|
||||||
<input type="checkbox" value="remove" name="mass_btn_assiduites" id="mass_rbtn_aucun"
|
<input type="checkbox" value="remove" name="mass_btn_assiduites" id="mass_rbtn_aucun"
|
||||||
class="rbtn aucun" onclick="mettreToutLeMonde('vide', this)" title="Supprimer">
|
class="rbtn aucun" onclick="mettreToutLeMonde('vide', this)" title="Retire l'état pour tous les étudiants" data-tooltip>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<em>Les saisies ci-dessous sont enregistrées au fur et à mesure.</em>
|
<em>Les saisies ci-dessous sont enregistrées au fur et à mesure.</em>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<div class="assiduite-bubble {{etat}}">
|
<div class="assiduite-bubble {{etat}}">
|
||||||
<div class="assiduite-id">{{moduleimpl}}</div>
|
|
||||||
<div class="assiduite-period">{{date_debut}}</div>
|
|
||||||
<div class="assiduite-period">{{date_fin}}</div>
|
|
||||||
<div class="assiduite-state">État: {{etat}}</div>
|
<div class="assiduite-state">État: {{etat}}</div>
|
||||||
|
<div class="assiduite-id">{{moduleimpl}}</div>
|
||||||
|
<div class="assiduite-period">{{date}}</div>
|
||||||
<div class="assiduite-why">Motif: {{motif}}</div>
|
<div class="assiduite-why">Motif: {{motif}}</div>
|
||||||
<div class="assiduite-user_id">{{saisie}}</div>
|
<div class="assiduite-user_id">{{saisie}}</div>
|
||||||
</div>
|
</div>
|
@ -17,12 +17,13 @@
|
|||||||
const timelineContainer = document.querySelector(".timeline-container");
|
const timelineContainer = document.querySelector(".timeline-container");
|
||||||
const periodTimeLine = document.querySelector(".period");
|
const periodTimeLine = document.querySelector(".period");
|
||||||
const t_start = {{ t_start }};
|
const t_start = {{ t_start }};
|
||||||
|
const t_mid = {{ t_mid }};
|
||||||
const t_end = {{ t_end }};
|
const t_end = {{ t_end }};
|
||||||
|
|
||||||
const tick_time = 60 / {{ tick_time }};
|
const tick_time = 60 / {{ tick_time }};
|
||||||
const tick_delay = 1 / tick_time;
|
const tick_delay = 1 / tick_time;
|
||||||
|
|
||||||
const period_default = {{ periode_defaut }};
|
const period_default = 2;
|
||||||
|
|
||||||
let handleMoving = false;
|
let handleMoving = false;
|
||||||
|
|
||||||
@ -133,6 +134,7 @@
|
|||||||
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
||||||
handleMoving = false;
|
handleMoving = false;
|
||||||
func_call();
|
func_call();
|
||||||
|
savePeriodInLocalStorage();
|
||||||
|
|
||||||
}
|
}
|
||||||
timelineContainer.addEventListener("mousemove", onMouseMove);
|
timelineContainer.addEventListener("mousemove", onMouseMove);
|
||||||
@ -166,6 +168,7 @@
|
|||||||
snapHandlesToQuarters();
|
snapHandlesToQuarters();
|
||||||
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
timelineContainer.removeEventListener("mousemove", onMouseMove);
|
||||||
func_call();
|
func_call();
|
||||||
|
savePeriodInLocalStorage();
|
||||||
}
|
}
|
||||||
timelineContainer.addEventListener("mousemove", onMouseMove);
|
timelineContainer.addEventListener("mousemove", onMouseMove);
|
||||||
timelineContainer.addEventListener("touchmove", onMouseMove);
|
timelineContainer.addEventListener("touchmove", onMouseMove);
|
||||||
@ -264,6 +267,7 @@
|
|||||||
snapHandlesToQuarters();
|
snapHandlesToQuarters();
|
||||||
updatePeriodTimeLabel()
|
updatePeriodTimeLabel()
|
||||||
func_call();
|
func_call();
|
||||||
|
savePeriodInLocalStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
function snapHandlesToQuarters() {
|
function snapHandlesToQuarters() {
|
||||||
@ -309,9 +313,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function savePeriodInLocalStorage(){
|
||||||
|
const dates = getPeriodValues();
|
||||||
|
localStorage.setItem("sco-timeline-values", JSON.stringify(dates));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadPeriodFromLocalStorage(){
|
||||||
|
const dates = JSON.parse(localStorage.getItem("sco-timeline-values"));
|
||||||
|
if(dates){
|
||||||
|
setPeriodValues(...dates);
|
||||||
|
}else{
|
||||||
|
setPeriodValues(t_start, t_start + period_default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createTicks();
|
createTicks();
|
||||||
|
|
||||||
setPeriodValues(t_start, t_start + period_default);
|
loadPeriodFromLocalStorage();
|
||||||
|
|
||||||
{% if heures %}
|
{% if heures %}
|
||||||
let [heure_deb, heure_fin] = [{{ heures | safe }}]
|
let [heure_deb, heure_fin] = [{{ heures | safe }}]
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
<script src="{{scu.STATIC_DIR}}/js/scodoc.js"></script>
|
<script src="{{scu.STATIC_DIR}}/js/scodoc.js"></script>
|
||||||
<script src="{{scu.STATIC_DIR}}/DataTables/datatables.min.js"></script>
|
<script src="{{scu.STATIC_DIR}}/DataTables/datatables.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function () { enableTooltips("gtrcontent") };
|
window.onload = function () { enableTooltips("gtrcontent"); enableTooltips("sidebar"); };
|
||||||
|
|
||||||
const SCO_URL = "{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}";
|
const SCO_URL = "{{ url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}";
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{# Barre marge gauche ScoDoc #}
|
{# Barre marge gauche ScoDoc #}
|
||||||
{# -*- mode: jinja-html -*- #}
|
{# -*- mode: jinja-html -*- #}
|
||||||
<!-- sidebar -->
|
<!-- sidebar -->
|
||||||
<div class="sidebar">
|
<div class="sidebar" id="sidebar">
|
||||||
{# sidebar_common #}
|
{# sidebar_common #}
|
||||||
<a class="scodoc_title" href="{{
|
<a class="scodoc_title" href="{{
|
||||||
url_for('scodoc.index', scodoc_dept=g.scodoc_dept) }}">ScoDoc {{ sco.SCOVERSION }}</a>
|
url_for('scodoc.index', scodoc_dept=g.scodoc_dept) }}">ScoDoc {{ sco.SCOVERSION }}</a>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
<b>Absences</b>
|
<b>Absences</b>
|
||||||
{% if sco.etud_cur_sem %}
|
{% if sco.etud_cur_sem %}
|
||||||
<span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }}
|
<span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }}
|
||||||
au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}">({{sco.prefs["assi_metrique"]}})
|
au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}" data-tooltip>({{sco.prefs["assi_metrique"]}})
|
||||||
<br />{{'%1g'|format(sco.nb_abs_just)}} J., {{'%1g'|format(sco.nb_abs_nj)}} N.J.</span>
|
<br />{{'%1g'|format(sco.nb_abs_just)}} J., {{'%1g'|format(sco.nb_abs_nj)}} N.J.</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul>
|
<ul>
|
||||||
@ -73,10 +73,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="{{ url_for('assiduites.calendrier_assi_etud', scodoc_dept=g.scodoc_dept,
|
<li><a href="{{ url_for('assiduites.calendrier_assi_etud', scodoc_dept=g.scodoc_dept,
|
||||||
etudid=sco.etud.id) }}">Calendrier</a></li>
|
etudid=sco.etud.id) }}">Calendrier</a></li>
|
||||||
<li><a href="{{ url_for('assiduites.liste_assiduites_etud', scodoc_dept=g.scodoc_dept,
|
|
||||||
etudid=sco.etud.id) }}">Liste</a></li>
|
|
||||||
<li><a href="{{ url_for('assiduites.bilan_etud', scodoc_dept=g.scodoc_dept,
|
<li><a href="{{ url_for('assiduites.bilan_etud', scodoc_dept=g.scodoc_dept,
|
||||||
etudid=sco.etud.id) }}">Bilan</a></li>
|
etudid=sco.etud.id) }}" title="Les pages bilan et liste ont été fusionnées">Liste/Bilan</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div> {# /etud-insidebar #}
|
</div> {# /etud-insidebar #}
|
||||||
|
@ -511,51 +511,6 @@ def _record_assiduite_etud(
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/liste_assiduites_etud")
|
|
||||||
@scodoc
|
|
||||||
@permission_required(Permission.ScoView)
|
|
||||||
def liste_assiduites_etud():
|
|
||||||
"""
|
|
||||||
liste_assiduites_etud Affichage de toutes les assiduites et justificatifs d'un etudiant
|
|
||||||
Args:
|
|
||||||
etudid (int): l'identifiant de l'étudiant
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: l'html généré
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Récupération de l'étudiant concerné
|
|
||||||
etudid = request.args.get("etudid", -1)
|
|
||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
|
||||||
if etud.dept_id != g.scodoc_dept_id:
|
|
||||||
abort(404, "étudiant inexistant dans ce département")
|
|
||||||
|
|
||||||
# Gestion d'une assiduité unique (redirigé depuis le calendrier) TODO-Assiduites
|
|
||||||
assiduite_id: int = request.args.get("assiduite_id", -1)
|
|
||||||
|
|
||||||
# Préparation de la page
|
|
||||||
tableau = _prepare_tableau(
|
|
||||||
liste_assi.AssiJustifData.from_etudiants(
|
|
||||||
etud,
|
|
||||||
),
|
|
||||||
filename=f"assiduites-justificatifs-{etud.id}",
|
|
||||||
afficher_etu=False,
|
|
||||||
filtre=liste_assi.AssiFiltre(type_obj=0),
|
|
||||||
options=liste_assi.AssiDisplayOptions(show_module=True),
|
|
||||||
cache_key=f"tableau-etud-{etud.id}",
|
|
||||||
)
|
|
||||||
if not tableau[0]:
|
|
||||||
return tableau[1]
|
|
||||||
# Page HTML:
|
|
||||||
return render_template(
|
|
||||||
"assiduites/pages/liste_assiduites.j2",
|
|
||||||
assi_id=assiduite_id,
|
|
||||||
etud=etud,
|
|
||||||
tableau=tableau[1],
|
|
||||||
sco=ScoData(etud),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/bilan_etud")
|
@bp.route("/bilan_etud")
|
||||||
@scodoc
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@ -583,28 +538,19 @@ def bilan_etud():
|
|||||||
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
sco_preferences.get_preference("assi_metrique", dept_id=g.scodoc_dept_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Récupération des assiduités et justificatifs de l'étudiant
|
# Préparation de la page
|
||||||
data = liste_assi.AssiJustifData(
|
tableau = _prepare_tableau(
|
||||||
etud.assiduites.filter(
|
liste_assi.AssiJustifData.from_etudiants(
|
||||||
Assiduite.etat != scu.EtatAssiduite.PRESENT, Assiduite.est_just == False
|
etud,
|
||||||
),
|
),
|
||||||
etud.justificatifs.filter(
|
filename=f"assiduites-justificatifs-{etud.id}",
|
||||||
Justificatif.etat.in_(
|
|
||||||
[scu.EtatJustificatif.ATTENTE, scu.EtatJustificatif.MODIFIE]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
table = _prepare_tableau(
|
|
||||||
data,
|
|
||||||
afficher_etu=False,
|
afficher_etu=False,
|
||||||
filename=f"Bilan assiduité {etud.nomprenom}",
|
filtre=liste_assi.AssiFiltre(type_obj=0),
|
||||||
titre="Bilan de l'assiduité de l'étudiant",
|
options=liste_assi.AssiDisplayOptions(show_module=True),
|
||||||
cache_key=f"tableau-etud-{etud.id}-bilan",
|
cache_key=f"tableau-etud-{etud.id}",
|
||||||
)
|
)
|
||||||
|
if not tableau[0]:
|
||||||
if not table[0]:
|
return tableau[1]
|
||||||
return table[1]
|
|
||||||
|
|
||||||
# Génération de la page HTML
|
# Génération de la page HTML
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -614,7 +560,7 @@ def bilan_etud():
|
|||||||
date_debut=date_debut,
|
date_debut=date_debut,
|
||||||
date_fin=date_fin,
|
date_fin=date_fin,
|
||||||
sco=ScoData(etud),
|
sco=ScoData(etud),
|
||||||
tableau=table[1],
|
tableau=tableau[1],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -654,7 +600,7 @@ def edit_justificatif_etud(justif_id: int):
|
|||||||
back_url = request.args.get("back_url", None)
|
back_url = request.args.get("back_url", None)
|
||||||
|
|
||||||
redirect_url = back_url or url_for(
|
redirect_url = back_url or url_for(
|
||||||
"assiduites.liste_assiduites_etud",
|
"assiduites.bilan_etud",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
etudid=justif.etudiant.id,
|
etudid=justif.etudiant.id,
|
||||||
)
|
)
|
||||||
@ -958,8 +904,6 @@ def choix_date() -> str:
|
|||||||
Route utilisée uniquement si la date courante n'est pas dans le semestre
|
Route utilisée uniquement si la date courante n'est pas dans le semestre
|
||||||
concerné par la requête vers une des pages suivantes :
|
concerné par la requête vers une des pages suivantes :
|
||||||
- saisie_assiduites_group
|
- saisie_assiduites_group
|
||||||
- visu_assiduites_group
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
formsemestre_id = request.args.get("formsemestre_id")
|
formsemestre_id = request.args.get("formsemestre_id")
|
||||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
@ -989,11 +933,7 @@ def choix_date() -> str:
|
|||||||
if ok:
|
if ok:
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
(
|
"assiduites.signal_assiduites_group",
|
||||||
"assiduites.signal_assiduites_group"
|
|
||||||
if request.args.get("readonly") is None
|
|
||||||
else "assiduites.visu_assiduites_group"
|
|
||||||
),
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre_id,
|
||||||
group_ids=group_ids,
|
group_ids=group_ids,
|
||||||
@ -1065,7 +1005,7 @@ def signal_assiduites_group():
|
|||||||
)
|
)
|
||||||
if not groups_infos.members:
|
if not groups_infos.members:
|
||||||
return (
|
return (
|
||||||
html_sco_header.sco_header(page_title="Saisie journalière de l'assiduité")
|
html_sco_header.sco_header(page_title="Saisie de l'assiduité")
|
||||||
+ "<h3>Aucun étudiant ! </h3>"
|
+ "<h3>Aucun étudiant ! </h3>"
|
||||||
+ html_sco_header.sco_footer()
|
+ html_sco_header.sco_footer()
|
||||||
)
|
)
|
||||||
@ -1156,137 +1096,7 @@ def signal_assiduites_group():
|
|||||||
sco=ScoData(formsemestre=formsemestre),
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
sem=sem["titre_num"],
|
sem=sem["titre_num"],
|
||||||
timeline=_timeline(heures=",".join([f"'{s}'" for s in heures])),
|
timeline=_timeline(heures=",".join([f"'{s}'" for s in heures])),
|
||||||
title="Saisie journalière des assiduités",
|
title="Saisie de l'assiduité",
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/visu_assiduites_group")
|
|
||||||
@scodoc
|
|
||||||
@permission_required(Permission.ScoView)
|
|
||||||
def visu_assiduites_group():
|
|
||||||
"""
|
|
||||||
Visualisation des assiduités des groupes pour le jour donné
|
|
||||||
dans le formsemestre_id et le moduleimpl_id
|
|
||||||
Returns:
|
|
||||||
str: l'html généré
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Récupération des paramètres de la requête
|
|
||||||
formsemestre_id: int = request.args.get("formsemestre_id", -1)
|
|
||||||
moduleimpl_id: int = request.args.get("moduleimpl_id")
|
|
||||||
date: str = request.args.get("jour", datetime.date.today().isoformat())
|
|
||||||
group_ids: list[int] = request.args.get("group_ids", None)
|
|
||||||
if group_ids is None:
|
|
||||||
group_ids = []
|
|
||||||
else:
|
|
||||||
group_ids = group_ids.split(",")
|
|
||||||
map(str, group_ids)
|
|
||||||
|
|
||||||
# Vérification du moduleimpl_id
|
|
||||||
if moduleimpl_id is not None:
|
|
||||||
try:
|
|
||||||
moduleimpl_id = int(moduleimpl_id)
|
|
||||||
except (TypeError, ValueError) as exc:
|
|
||||||
raise ScoValueError("identifiant de moduleimpl invalide") from exc
|
|
||||||
# Vérification du formsemestre_id
|
|
||||||
if formsemestre_id is not None:
|
|
||||||
try:
|
|
||||||
formsemestre_id = int(formsemestre_id)
|
|
||||||
except (TypeError, ValueError) as exc:
|
|
||||||
raise ScoValueError("identifiant de formsemestre invalide") from exc
|
|
||||||
|
|
||||||
# Récupérations des/du groupe(s)
|
|
||||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
|
||||||
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
|
||||||
)
|
|
||||||
if not groups_infos.members:
|
|
||||||
return (
|
|
||||||
html_sco_header.sco_header(page_title="Saisie journalière de l'assiduité")
|
|
||||||
+ "<h3>Aucun étudiant ! </h3>"
|
|
||||||
+ html_sco_header.sco_footer()
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Filtrage par formsemestre ---
|
|
||||||
formsemestre_id = groups_infos.formsemestre_id
|
|
||||||
|
|
||||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
|
||||||
if formsemestre.dept_id != g.scodoc_dept_id:
|
|
||||||
abort(404, "groupes inexistants dans ce département")
|
|
||||||
|
|
||||||
# Récupération des étudiants du/des groupe(s)
|
|
||||||
etuds = [
|
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
|
||||||
for m in groups_infos.members
|
|
||||||
]
|
|
||||||
|
|
||||||
# --- Vérification de la date ---
|
|
||||||
real_date = scu.is_iso_formated(date, True).date()
|
|
||||||
if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin:
|
|
||||||
# Si le jour est hors semestre, renvoyer vers choix date
|
|
||||||
return redirect(
|
|
||||||
url_for(
|
|
||||||
"assiduites.choix_date",
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
group_ids=group_ids,
|
|
||||||
moduleimpl_id=moduleimpl_id,
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
readonly="true",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Restriction en fonction du moduleimpl_id ---
|
|
||||||
if moduleimpl_id:
|
|
||||||
mod_inscrits = {
|
|
||||||
x["etudid"]
|
|
||||||
for x in sco_moduleimpl.do_moduleimpl_inscription_list(
|
|
||||||
moduleimpl_id=moduleimpl_id
|
|
||||||
)
|
|
||||||
}
|
|
||||||
etuds_inscrits_module = [e for e in etuds if e["etudid"] in mod_inscrits]
|
|
||||||
if etuds_inscrits_module:
|
|
||||||
etuds = etuds_inscrits_module
|
|
||||||
else:
|
|
||||||
# Si aucun etudiant n'est inscrit au module choisi...
|
|
||||||
moduleimpl_id = None
|
|
||||||
|
|
||||||
# --- Génération du HTML ---
|
|
||||||
|
|
||||||
if groups_infos.tous_les_etuds_du_sem:
|
|
||||||
gr_tit = "en"
|
|
||||||
else:
|
|
||||||
if len(groups_infos.group_ids) > 1:
|
|
||||||
grp = "des groupes"
|
|
||||||
else:
|
|
||||||
grp = "du groupe"
|
|
||||||
gr_tit = (
|
|
||||||
grp + ' <span class="fontred">' + groups_infos.groups_titles + "</span>"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Récupération du semestre en dictionnaire
|
|
||||||
sem = formsemestre.to_dict()
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"assiduites/pages/signal_assiduites_group.j2",
|
|
||||||
date=_dateiso_to_datefr(date),
|
|
||||||
defdem=_get_etuds_dem_def(formsemestre),
|
|
||||||
forcer_module=sco_preferences.get_preference(
|
|
||||||
"forcer_module",
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
dept_id=g.scodoc_dept_id,
|
|
||||||
),
|
|
||||||
formsemestre_date_debut=str(formsemestre.date_debut),
|
|
||||||
formsemestre_date_fin=str(formsemestre.date_fin),
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
gr_tit=gr_tit,
|
|
||||||
grp=sco_groups_view.menu_groups_choice(groups_infos),
|
|
||||||
minitimeline=_mini_timeline(),
|
|
||||||
moduleimpl_select=_module_selector(formsemestre, moduleimpl_id),
|
|
||||||
nonworkdays=_non_work_days(),
|
|
||||||
sem=sem["titre_num"],
|
|
||||||
timeline=_timeline(),
|
|
||||||
readonly="true",
|
|
||||||
sco=ScoData(formsemestre=formsemestre),
|
|
||||||
title="Saisie journalière de l'assiduité",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -2028,6 +1838,8 @@ def signal_assiduites_diff():
|
|||||||
dept_id=g.scodoc_dept_id,
|
dept_id=g.scodoc_dept_id,
|
||||||
),
|
),
|
||||||
nouv_plage=nouv_plage,
|
nouv_plage=nouv_plage,
|
||||||
|
formsemestre_id=formsemestre_id,
|
||||||
|
group_ids=group_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -2298,89 +2110,6 @@ def _get_date_str(deb: datetime.datetime, fin: datetime.datetime) -> str:
|
|||||||
return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}'
|
return f'du {deb.strftime("%d/%m/%Y %H:%M")} au {fin.strftime("%d/%m/%Y %H:%M")}'
|
||||||
|
|
||||||
|
|
||||||
def _get_days_between_dates(deb: str, fin: str) -> str:
|
|
||||||
"""
|
|
||||||
_get_days_between_dates récupère tous les jours entre deux dates
|
|
||||||
|
|
||||||
Args:
|
|
||||||
deb (str): date de début
|
|
||||||
fin (str): date de fin
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: une chaine json représentant une liste des jours
|
|
||||||
['date_iso','date_iso2', ...]
|
|
||||||
"""
|
|
||||||
if deb is None or fin is None:
|
|
||||||
return "null"
|
|
||||||
try:
|
|
||||||
if isinstance(deb, str) and isinstance(fin, str):
|
|
||||||
date_deb: datetime.date = datetime.date.fromisoformat(deb)
|
|
||||||
date_fin: datetime.date = datetime.date.fromisoformat(fin)
|
|
||||||
else:
|
|
||||||
date_deb, date_fin = deb.date(), fin.date()
|
|
||||||
except ValueError:
|
|
||||||
return "null"
|
|
||||||
dates: list[str] = []
|
|
||||||
while date_deb <= date_fin:
|
|
||||||
dates.append(f'"{date_deb.isoformat()}"')
|
|
||||||
date_deb = date_deb + datetime.timedelta(days=1)
|
|
||||||
|
|
||||||
return f"[{','.join(dates)}]"
|
|
||||||
|
|
||||||
|
|
||||||
def _differee(
|
|
||||||
etudiants: list[dict],
|
|
||||||
moduleimpl_select: str,
|
|
||||||
date: str = None,
|
|
||||||
periode: dict[str, str] = None,
|
|
||||||
formsemestre_id: int = None,
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
_differee Génère un tableau de saisie différé
|
|
||||||
|
|
||||||
Args:
|
|
||||||
etudiants (list[dict]): la liste des étudiants (représentés par des dictionnaires)
|
|
||||||
moduleimpl_select (str): l'html représentant le selecteur de module
|
|
||||||
date (str, optional): la première date à afficher. Defaults to None.
|
|
||||||
periode (dict[str, str], optional):La période par défaut de la première colonne.
|
|
||||||
formsemestre_id (int, optional): l'id du semestre pour le selecteur de module.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: le widget (html/css/js)
|
|
||||||
"""
|
|
||||||
if date is None:
|
|
||||||
date = datetime.date.today().isoformat()
|
|
||||||
|
|
||||||
forcer_module = sco_preferences.get_preference(
|
|
||||||
"forcer_module",
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
dept_id=g.scodoc_dept_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
assi_etat_defaut = sco_preferences.get_preference(
|
|
||||||
"assi_etat_defaut",
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
dept_id=g.scodoc_dept_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
periode_defaut = sco_preferences.get_preference(
|
|
||||||
"periode_defaut",
|
|
||||||
formsemestre_id=formsemestre_id,
|
|
||||||
dept_id=g.scodoc_dept_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
return render_template(
|
|
||||||
"assiduites/widgets/differee.j2",
|
|
||||||
etudiants=etudiants,
|
|
||||||
assi_etat_defaut=assi_etat_defaut,
|
|
||||||
periode_defaut=periode_defaut,
|
|
||||||
forcer_module=forcer_module,
|
|
||||||
moduleimpl_select=moduleimpl_select,
|
|
||||||
date=date,
|
|
||||||
periode=periode,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _module_selector(formsemestre: FormSemestre, moduleimpl_id: int = None) -> str:
|
def _module_selector(formsemestre: FormSemestre, moduleimpl_id: int = None) -> str:
|
||||||
"""
|
"""
|
||||||
_module_selector Génère un HTMLSelectElement à partir des moduleimpl du formsemestre
|
_module_selector Génère un HTMLSelectElement à partir des moduleimpl du formsemestre
|
||||||
@ -2450,7 +2179,7 @@ def _module_selector_multiple(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _timeline(formsemestre_id: int = None, heures=None) -> str:
|
def _timeline(heures=None) -> str:
|
||||||
"""
|
"""
|
||||||
_timeline retourne l'html de la timeline
|
_timeline retourne l'html de la timeline
|
||||||
|
|
||||||
@ -2465,11 +2194,9 @@ def _timeline(formsemestre_id: int = None, heures=None) -> str:
|
|||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/timeline.j2",
|
"assiduites/widgets/timeline.j2",
|
||||||
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
t_start=ScoDocSiteConfig.assi_get_rounded_time("assi_morning_time", "08:00:00"),
|
||||||
|
t_mid=ScoDocSiteConfig.assi_get_rounded_time("assi_lunch_time", "13:00:00"),
|
||||||
t_end=ScoDocSiteConfig.assi_get_rounded_time("assi_afternoon_time", "18:00:00"),
|
t_end=ScoDocSiteConfig.assi_get_rounded_time("assi_afternoon_time", "18:00:00"),
|
||||||
tick_time=ScoDocSiteConfig.get("assi_tick_time", 15),
|
tick_time=ScoDocSiteConfig.get("assi_tick_time", 15),
|
||||||
periode_defaut=sco_preferences.get_preference(
|
|
||||||
"periode_defaut", formsemestre_id
|
|
||||||
),
|
|
||||||
heures=heures,
|
heures=heures,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2854,14 +2581,26 @@ def _generate_assiduite_bubble(assiduite: Assiduite) -> str:
|
|||||||
# Récupérer informations saisie
|
# Récupérer informations saisie
|
||||||
saisie: str = assiduite.get_saisie()
|
saisie: str = assiduite.get_saisie()
|
||||||
|
|
||||||
motif: str = assiduite.description if assiduite.description else ""
|
motif: str = assiduite.description or "Non spécifié"
|
||||||
|
|
||||||
|
# Récupérer date
|
||||||
|
|
||||||
|
if assiduite.date_debut.date() == assiduite.date_fin.date():
|
||||||
|
jour = assiduite.date_debut.strftime("%d/%m/%Y")
|
||||||
|
heure_deb: str = assiduite.date_debut.strftime("%H:%M")
|
||||||
|
heure_fin: str = assiduite.date_fin.strftime("%H:%M")
|
||||||
|
date: str = f"{jour} de {heure_deb} à {heure_fin}"
|
||||||
|
else:
|
||||||
|
date: str = (
|
||||||
|
f"du {assiduite.date_debut.strftime('%d/%m/%Y')} "
|
||||||
|
+ f"au {assiduite.date_fin.strftime('%d/%m/%Y')}"
|
||||||
|
)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/assiduite_bubble.j2",
|
"assiduites/widgets/assiduite_bubble.j2",
|
||||||
moduleimpl=moduleimpl_infos,
|
moduleimpl=moduleimpl_infos,
|
||||||
etat=scu.EtatAssiduite(assiduite.etat).name.lower(),
|
etat=scu.EtatAssiduite(assiduite.etat).name.lower(),
|
||||||
date_debut=assiduite.date_debut.strftime("%d/%m/%Y %H:%M"),
|
date=date,
|
||||||
date_fin=assiduite.date_fin.strftime("%d/%m/%Y %H:%M"),
|
|
||||||
saisie=saisie,
|
saisie=saisie,
|
||||||
motif=motif,
|
motif=motif,
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user