forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -151,7 +151,9 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal
|
|||||||
@as_json
|
@as_json
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
def justificatifs_dept(dept_id: int = None, with_query: bool = False):
|
||||||
"""XXX TODO missing doc"""
|
"""
|
||||||
|
Renvoie tous les justificatifs d'un département (en ajoutant un champs "formsemestre" si possible)
|
||||||
|
"""
|
||||||
|
|
||||||
# Récupération du département et des étudiants du département
|
# Récupération du département et des étudiants du département
|
||||||
dept: Departement = Departement.query.get(dept_id)
|
dept: Departement = Departement.query.get(dept_id)
|
||||||
|
@ -108,7 +108,6 @@ def do_abs_notify(
|
|||||||
return # abort
|
return # abort
|
||||||
|
|
||||||
# Vérification fréquence (pour ne pas envoyer de mails trop souvent)
|
# Vérification fréquence (pour ne pas envoyer de mails trop souvent)
|
||||||
# TODO Mettre la fréquence dans les préférences assiduités
|
|
||||||
abs_notify_max_freq = sco_preferences.get_preference("abs_notify_max_freq")
|
abs_notify_max_freq = sco_preferences.get_preference("abs_notify_max_freq")
|
||||||
destinations_filtered = []
|
destinations_filtered = []
|
||||||
for email_addr in destinations:
|
for email_addr in destinations:
|
||||||
|
@ -763,5 +763,7 @@ def simple_invalidate_cache(obj: dict, etudid: str | int = None):
|
|||||||
|
|
||||||
# Invalide les caches des tableaux de l'étudiant
|
# Invalide les caches des tableaux de l'étudiant
|
||||||
sco_cache.RequeteTableauAssiduiteCache.delete_pattern(
|
sco_cache.RequeteTableauAssiduiteCache.delete_pattern(
|
||||||
pattern=f"tableau-etud-{etudid}:*"
|
pattern=f"tableau-etud-{etudid}*"
|
||||||
)
|
)
|
||||||
|
# Invalide les tableaux "bilan dept"
|
||||||
|
sco_cache.RequeteTableauAssiduiteCache.delete_pattern(pattern=f"tableau-dept*")
|
||||||
|
@ -400,7 +400,7 @@ class ValidationsSemestreCache(ScoDocCache):
|
|||||||
|
|
||||||
class RequeteTableauAssiduiteCache(ScoDocCache):
|
class RequeteTableauAssiduiteCache(ScoDocCache):
|
||||||
"""
|
"""
|
||||||
clé : "<titre_tableau>:<type_obj>:<show_pres>:<show_retard>>:<order_col>:<order>"
|
clé : "<titre_tableau>:<type_obj>:<show_pres>:<show_retard>:<show_desc>:<order_col>:<order>"
|
||||||
Valeur = liste de dicts
|
Valeur = liste de dicts
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -917,7 +917,7 @@ def _make_listes_sem(formsemestre: FormSemestre) -> str:
|
|||||||
<a class="btn" href="{
|
<a class="btn" href="{
|
||||||
url_for("assiduites.bilan_dept",
|
url_for("assiduites.bilan_dept",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre=formsemestre.id,
|
formsemestre_id=formsemestre.id,
|
||||||
group_ids=group.id,
|
group_ids=group.id,
|
||||||
)}">
|
)}">
|
||||||
<button>Justificatifs en attente</button></a>
|
<button>Justificatifs en attente</button></a>
|
||||||
|
@ -541,18 +541,6 @@ class BasePreferences:
|
|||||||
"category": "abs",
|
"category": "abs",
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"abs_notify_max_freq",
|
|
||||||
{
|
|
||||||
"initvalue": 7,
|
|
||||||
"title": "Fréquence maximale de notification",
|
|
||||||
"explanation": "nb de jours minimum entre deux mails envoyés au même destinataire à propos d'un même étudiant ",
|
|
||||||
"size": 4,
|
|
||||||
"type": "int",
|
|
||||||
"convert_numbers": True,
|
|
||||||
"category": "abs",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"abs_notify_abs_threshold",
|
"abs_notify_abs_threshold",
|
||||||
{
|
{
|
||||||
@ -710,11 +698,23 @@ class BasePreferences:
|
|||||||
"size": 10,
|
"size": 10,
|
||||||
"title": "Seuil d'alerte des absences",
|
"title": "Seuil d'alerte des absences",
|
||||||
"type": "int",
|
"type": "int",
|
||||||
"explanation": "Nombres d'absences limite avant alerte dans le bilan (utilisation de l'unité métrique ↑ )",
|
"explanation": "Nombres d'absences limite avant alerte (utilisation de l'unité métrique ↑ )",
|
||||||
"category": "assi",
|
"category": "assi",
|
||||||
"only_global": True,
|
"only_global": True,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"abs_notify_max_freq",
|
||||||
|
{
|
||||||
|
"initvalue": 7,
|
||||||
|
"title": "Fréquence maximale de notification",
|
||||||
|
"explanation": "nb de jours minimum entre deux mails envoyés au même destinataire à propos d'un même étudiant ",
|
||||||
|
"size": 4,
|
||||||
|
"type": "int",
|
||||||
|
"convert_numbers": True,
|
||||||
|
"category": "abs",
|
||||||
|
},
|
||||||
|
),
|
||||||
# portal
|
# portal
|
||||||
(
|
(
|
||||||
"portal_url",
|
"portal_url",
|
||||||
|
@ -298,15 +298,11 @@ def is_iso_formated(date: str, convert=False) -> bool or datetime.datetime or No
|
|||||||
return None if convert else False
|
return None if convert else False
|
||||||
|
|
||||||
|
|
||||||
def localize_datetime(date: datetime.datetime or str) -> datetime.datetime:
|
def localize_datetime(date: datetime.datetime) -> datetime.datetime:
|
||||||
"""Transforme une date sans offset en une date avec offset
|
"""Transforme une date sans offset en une date avec offset
|
||||||
Tente de mettre l'offset de la timezone du serveur (ex : UTC+1)
|
Tente de mettre l'offset de la timezone du serveur (ex : UTC+1)
|
||||||
Si erreur, mettra l'offset UTC
|
Si erreur, mettra l'offset UTC
|
||||||
|
|
||||||
TODO : vérifier puis supprimer l'auto conversion str-> datetime
|
|
||||||
"""
|
"""
|
||||||
if isinstance(date, str):
|
|
||||||
date = is_iso_formated(date, convert=True)
|
|
||||||
|
|
||||||
new_date: datetime.datetime = date
|
new_date: datetime.datetime = date
|
||||||
if new_date.tzinfo is None:
|
if new_date.tzinfo is None:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
// TODO : Supprimer les fonctions non utilisées + optimiser les fonctions utilisées
|
||||||
// <=== CONSTANTS and GLOBALS ===>
|
// <=== CONSTANTS and GLOBALS ===>
|
||||||
|
|
||||||
let url;
|
let url;
|
||||||
|
@ -140,6 +140,7 @@ class ListeAssiJusti(tb.Table):
|
|||||||
type_obj,
|
type_obj,
|
||||||
self.options.show_pres,
|
self.options.show_pres,
|
||||||
self.options.show_reta,
|
self.options.show_reta,
|
||||||
|
self.options.show_desc,
|
||||||
self.options.order[0],
|
self.options.order[0],
|
||||||
self.options.order[1],
|
self.options.order[1],
|
||||||
],
|
],
|
||||||
@ -152,12 +153,18 @@ class ListeAssiJusti(tb.Table):
|
|||||||
assiduites_query_etudiants = self.table_data.assiduites_query
|
assiduites_query_etudiants = self.table_data.assiduites_query
|
||||||
|
|
||||||
# Non affichage des présences
|
# Non affichage des présences
|
||||||
if not self.options.show_pres:
|
if (
|
||||||
|
not self.options.show_pres
|
||||||
|
and assiduites_query_etudiants is not None
|
||||||
|
):
|
||||||
assiduites_query_etudiants = assiduites_query_etudiants.filter(
|
assiduites_query_etudiants = assiduites_query_etudiants.filter(
|
||||||
Assiduite.etat != EtatAssiduite.PRESENT
|
Assiduite.etat != EtatAssiduite.PRESENT
|
||||||
)
|
)
|
||||||
# Non affichage des retards
|
# Non affichage des retards
|
||||||
if not self.options.show_reta:
|
if (
|
||||||
|
not self.options.show_reta
|
||||||
|
and assiduites_query_etudiants is not None
|
||||||
|
):
|
||||||
assiduites_query_etudiants = assiduites_query_etudiants.filter(
|
assiduites_query_etudiants = assiduites_query_etudiants.filter(
|
||||||
Assiduite.etat != EtatAssiduite.RETARD
|
Assiduite.etat != EtatAssiduite.RETARD
|
||||||
)
|
)
|
||||||
@ -266,7 +273,7 @@ class ListeAssiJusti(tb.Table):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if self.options.show_desc:
|
if self.options.show_desc:
|
||||||
assiduites_entities.append(Assiduite.description.label("description"))
|
assiduites_entities.append(Assiduite.description.label("desc"))
|
||||||
|
|
||||||
query_assiduite = query_assiduite.with_entities(*assiduites_entities)
|
query_assiduite = query_assiduite.with_entities(*assiduites_entities)
|
||||||
queries.append(query_assiduite)
|
queries.append(query_assiduite)
|
||||||
@ -288,7 +295,7 @@ class ListeAssiJusti(tb.Table):
|
|||||||
]
|
]
|
||||||
|
|
||||||
if self.options.show_desc:
|
if self.options.show_desc:
|
||||||
justificatifs_entities.append(Justificatif.raison.label("description"))
|
justificatifs_entities.append(Justificatif.raison.label("desc"))
|
||||||
|
|
||||||
query_justificatif = query_justificatif.with_entities(
|
query_justificatif = query_justificatif.with_entities(
|
||||||
*justificatifs_entities
|
*justificatifs_entities
|
||||||
@ -466,7 +473,7 @@ class RowAssiJusti(tb.Row):
|
|||||||
self.add_cell(
|
self.add_cell(
|
||||||
"description",
|
"description",
|
||||||
"Description",
|
"Description",
|
||||||
self.ligne["description"] if self.ligne["description"] else "",
|
self.ligne["desc"] if self.ligne["desc"] else "",
|
||||||
)
|
)
|
||||||
if self.table.options.show_module:
|
if self.table.options.show_module:
|
||||||
if self.ligne["type"] == "assiduite":
|
if self.ligne["type"] == "assiduite":
|
||||||
|
@ -1,187 +1,27 @@
|
|||||||
{% include "assiduites/widgets/tableau_base.j2" %}
|
{% extends "sco_page.j2" %}
|
||||||
<section class="alerte invisible">
|
{% block styles %}
|
||||||
<p>Attention, cet étudiant a trop d'absences</p>
|
{{super()}}
|
||||||
</section>
|
<link rel="stylesheet" href="{{scu.STATIC_DIR}}/css/assiduites.css">
|
||||||
|
{% endblock styles %}
|
||||||
|
{% block scripts %}
|
||||||
|
{{ super() }}
|
||||||
|
<script src="{{scu.STATIC_DIR}}/js/etud_info.js"></script>
|
||||||
|
{% endblock scripts %}
|
||||||
|
{% block app_content %}
|
||||||
|
<h2>Traitement de l'assiduité</h2>
|
||||||
|
<p class="help">
|
||||||
|
Pour saisir l'assiduité ou consulter les états, il est recommandé de passer par
|
||||||
|
le semestre concerné (saisie par jour ou saisie différée).
|
||||||
|
</p>
|
||||||
|
<p class="help">Pour signaler, annuler ou justifier l'assiduité d'un seul étudiant,
|
||||||
|
choisissez d'abord la personne concernée :</p>
|
||||||
|
<br>
|
||||||
|
{{search_etud | safe}}
|
||||||
|
<br>
|
||||||
|
{{billets | safe}}
|
||||||
|
|
||||||
|
<br>
|
||||||
<section class="nonvalide">
|
<section class="nonvalide">
|
||||||
<!-- Tableaux des justificatifs à valider (attente / modifié ) -->
|
{{tableau | safe }}
|
||||||
<h4>Justificatifs en attente (ou modifiés)</h4>
|
|
||||||
<span class="iconline">
|
|
||||||
<a class="icon filter" onclick="filterJusti(true)"></a>
|
|
||||||
<a class="icon download" onclick="downloadJusti()"></a>
|
|
||||||
</span>
|
|
||||||
{% include "assiduites/widgets/tableau_justi.j2" %}
|
|
||||||
</section>
|
</section>
|
||||||
|
{% endblock app_content %}
|
||||||
<div class="annee">
|
|
||||||
<span>Année scolaire 2022-2023 Changer année: </span>
|
|
||||||
<select name="" id="annee" onchange="setterAnnee(this.value)">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="legende">
|
|
||||||
<h3>Gestion des justificatifs</h3>
|
|
||||||
<p>
|
|
||||||
Faites
|
|
||||||
<span style="font-style: italic;">clic droit</span> sur une ligne du tableau pour afficher le menu contextuel :
|
|
||||||
<ul>
|
|
||||||
<li>Détails : Affiche les détails du justificatif sélectionné</li>
|
|
||||||
<li>Editer : Permet de modifier le justificatif (dates, etat, ajouter/supprimer fichier etc)</li>
|
|
||||||
<li>Supprimer : Permet de supprimer le justificatif (Action Irréversible)</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
let formsemestre_id = "{{formsemestre_id}}"
|
|
||||||
let group_id = "{{group_id}}"
|
|
||||||
|
|
||||||
function getDeptJustificatifsFromPeriod(action) {
|
|
||||||
const formsemestre = formsemestre_id ? `&formsemestre_id=${formsemestre_id}` : ""
|
|
||||||
const group = group_id ? `&group_id=${group_id}` : ""
|
|
||||||
const path = getUrl() + `/api/justificatifs/dept/${dept_id}/query?date_debut=${bornes.deb}&date_fin=${bornes.fin}${formsemestre}${group}`
|
|
||||||
async_get(
|
|
||||||
path,
|
|
||||||
(data, status) => {
|
|
||||||
if (action) {
|
|
||||||
action(data)
|
|
||||||
} else {
|
|
||||||
justificatifCallBack(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(data, status) => {
|
|
||||||
console.error(data, status)
|
|
||||||
errorAlert();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function generate(annee) {
|
|
||||||
|
|
||||||
if (annee < 1999 || annee > 2999) {
|
|
||||||
openAlertModal("Année impossible", document.createTextNode("L'année demandé n'existe pas."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bornes = {
|
|
||||||
deb: `${annee}-09-01T00:00`,
|
|
||||||
fin: `${annee + 1}-08-31T23:59`
|
|
||||||
}
|
|
||||||
|
|
||||||
defAnnee = annee;
|
|
||||||
|
|
||||||
loadAll();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function getJusti(action) {
|
|
||||||
try { getDeptJustificatifsFromPeriod(action) } catch (_) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
function setterAnnee(annee) {
|
|
||||||
annee = parseInt(annee);
|
|
||||||
document.querySelector('.annee span').textContent = `Année scolaire ${annee}-${annee + 1} Changer année: `
|
|
||||||
generate(annee)
|
|
||||||
|
|
||||||
}
|
|
||||||
let defAnnee = {{ annee }};
|
|
||||||
let bornes = {
|
|
||||||
deb: `${defAnnee}-09-01T00:00`,
|
|
||||||
fin: `${defAnnee + 1}-08-31T23:59`
|
|
||||||
}
|
|
||||||
const dept_id = {{ dept_id }};
|
|
||||||
|
|
||||||
let annees = {{ annees | safe}}
|
|
||||||
|
|
||||||
annees = annees.filter((x, i) => annees.indexOf(x) === i)
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
|
|
||||||
filterJustificatifs = {
|
|
||||||
"columns": [
|
|
||||||
"formsemestre",
|
|
||||||
"etudid",
|
|
||||||
"entry_date",
|
|
||||||
"date_debut",
|
|
||||||
"date_fin",
|
|
||||||
"etat",
|
|
||||||
"raison",
|
|
||||||
"fichier"
|
|
||||||
],
|
|
||||||
"filters": {
|
|
||||||
"etat": [
|
|
||||||
"attente",
|
|
||||||
"modifie"
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const select = document.querySelector('#annee');
|
|
||||||
|
|
||||||
annees.forEach((a) => {
|
|
||||||
const opt = document.createElement("option");
|
|
||||||
opt.value = a + "",
|
|
||||||
opt.textContent = `${a} - ${a + 1}`;
|
|
||||||
if (a === defAnnee) {
|
|
||||||
opt.selected = true;
|
|
||||||
}
|
|
||||||
select.appendChild(opt)
|
|
||||||
})
|
|
||||||
setterAnnee(defAnnee)
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.stats-values-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-evenly;
|
|
||||||
align-items: center;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats {
|
|
||||||
border: 1px solid #333;
|
|
||||||
padding: 5px 2px;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-values {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-values-item h5 {
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration-line: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-values-part {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerte {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 10px;
|
|
||||||
margin: 5px 0;
|
|
||||||
border-radius: 7px;
|
|
||||||
|
|
||||||
background-color: var(--color-error);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerte.invisible {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.alerte p {
|
|
||||||
font-size: larger;
|
|
||||||
color: whitesmoke;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.suppr {
|
|
||||||
margin: 5px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -25,27 +25,7 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="nonvalide">
|
<section class="nonvalide">
|
||||||
<!-- Tableaux des assiduités (retard/abs) non justifiées -->
|
{{tableau | safe }}
|
||||||
<h4>Absences et retards non justifiés</h4>
|
|
||||||
|
|
||||||
{# TODO Utiliser python tableau plutot que js tableau #}
|
|
||||||
<div class="ue_warning">Attention, cette page utilise des couleurs et conventions différentes
|
|
||||||
de celles des autres pages ScoDoc: elle sera prochainement modifée, merci de votre patience.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span class="iconline">
|
|
||||||
<a class="icon filter" onclick="filterAssi()"></a>
|
|
||||||
<a class="icon download" onclick="downloadAssi()"></a>
|
|
||||||
</span>
|
|
||||||
{% include "assiduites/widgets/tableau_assi.j2" %}
|
|
||||||
<!-- Tableaux des justificatifs à valider (attente / modifié ) -->
|
|
||||||
<h4>Justificatifs en attente (ou modifiés)</h4>
|
|
||||||
<span class="iconline">
|
|
||||||
<a class="icon filter" onclick="filterJusti()"></a>
|
|
||||||
<a class="icon download" onclick="downloadJusti()"></a>
|
|
||||||
</span>
|
|
||||||
{% include "assiduites/widgets/tableau_justi.j2" %}
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="suppr">
|
<section class="suppr">
|
||||||
@ -60,29 +40,6 @@
|
|||||||
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>
|
||||||
<h3>Gestion des justificatifs</h3>
|
|
||||||
<p>
|
|
||||||
Faites
|
|
||||||
<span style="font-style: italic;">clic droit</span> sur une ligne du tableau pour afficher le menu
|
|
||||||
contextuel :
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Détails : affiche les détails du justificatif sélectionné</li>
|
|
||||||
<li>Éditer : modifie le justificatif (dates, état, ajouter/supprimer fichier, etc.)</li>
|
|
||||||
<li>Supprimer : supprime le justificatif (action irréversible)</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h3>Gestion de l'assiduité</h3>
|
|
||||||
<p>
|
|
||||||
Faites
|
|
||||||
<span style="font-style: italic;">clic droit</span> sur une ligne du tableau pour afficher le menu
|
|
||||||
contextuel :
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>Détails : affiche les détails de l'élément sélectionnée</li>
|
|
||||||
<li>Editer : modifie l'élément (module, état)</li>
|
|
||||||
<li>Supprimer : supprime l'élément (action irréversible)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -275,48 +232,8 @@
|
|||||||
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
filterAssiduites = {
|
|
||||||
"columns": [
|
|
||||||
"entry_date",
|
|
||||||
"date_debut",
|
|
||||||
"date_fin",
|
|
||||||
"etat",
|
|
||||||
"moduleimpl_id",
|
|
||||||
"est_just"
|
|
||||||
],
|
|
||||||
"filters": {
|
|
||||||
"etat": [
|
|
||||||
"retard",
|
|
||||||
"absent"
|
|
||||||
],
|
|
||||||
"moduleimpl_id": "",
|
|
||||||
"est_just": "false"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
filterJustificatifs = {
|
|
||||||
"columns": [
|
|
||||||
"entry_date",
|
|
||||||
"date_debut",
|
|
||||||
"date_fin",
|
|
||||||
"etat",
|
|
||||||
"raison",
|
|
||||||
"fichier"
|
|
||||||
],
|
|
||||||
"filters": {
|
|
||||||
"etat": [
|
|
||||||
"attente",
|
|
||||||
"modifie"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('stats_date_fin').value = assi_date_fin;
|
document.getElementById('stats_date_fin').value = assi_date_fin;
|
||||||
document.getElementById('stats_date_debut').value = assi_date_debut;
|
document.getElementById('stats_date_debut').value = assi_date_debut;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loadAll();
|
|
||||||
stats();
|
stats();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
|
{#
|
||||||
|
|
||||||
|
- TODO : revoir le fonctionnement de cette page (trop lente / complexe)
|
||||||
|
- Utiliser majoritairement du python
|
||||||
|
#}
|
||||||
<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 utilise des couleurs et conventions différentes
|
||||||
|
de celles des autres pages ScoDoc: elle sera prochainement modifée, merci de votre patience.
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>{{sem | safe }}</h3>
|
<h3>{{sem | safe }}</h3>
|
||||||
|
|
||||||
{{diff | safe}}
|
{{diff | safe}}
|
||||||
|
@ -3,5 +3,6 @@
|
|||||||
<div class="assiduite-period">{{date_debut}}</div>
|
<div class="assiduite-period">{{date_debut}}</div>
|
||||||
<div class="assiduite-period">{{date_fin}}</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-why">Motif: {{motif}}</div>
|
||||||
<div class="assiduite-user_id">{{saisie}}</div>
|
<div class="assiduite-user_id">{{saisie}}</div>
|
||||||
</div>
|
</div>
|
@ -77,12 +77,6 @@
|
|||||||
const duration = (endTime - startTime) / 1000 / 60;
|
const duration = (endTime - startTime) / 1000 / 60;
|
||||||
|
|
||||||
const percent = (duration / (t_end * 60 - t_start * 60)) * 100
|
const percent = (duration / (t_end * 60 - t_start * 60)) * 100
|
||||||
|
|
||||||
if (percent > 100) {
|
|
||||||
console.log(start, end);
|
|
||||||
console.log(startTime, endTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
return percent + "%";
|
return percent + "%";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,13 +247,11 @@
|
|||||||
*/
|
*/
|
||||||
splitAssiduiteModal() {
|
splitAssiduiteModal() {
|
||||||
//Préparation du prompt
|
//Préparation du prompt
|
||||||
// TODO utiliser timepicker jquery + utiliser les bornes (t_start et t_end)
|
const htmlPrompt = `<legend>Entrez l'heure de séparation</legend>
|
||||||
const htmlPrompt = `<legend>Entrez l'heure de séparation (HH:mm) :</legend>
|
<input type="text" id="promptTime" name="appt"required style="position: relative; z-index: 100000;">`;
|
||||||
<input type="time" id="promptTime" name="appt"
|
|
||||||
min="08:00" max="18:00" required>`;
|
|
||||||
|
|
||||||
const fieldSet = document.createElement("fieldset");
|
const fieldSet = document.createElement("fieldset");
|
||||||
fieldSet.classList.add("fieldsplit");
|
fieldSet.classList.add("fieldsplit", "timepicker");
|
||||||
fieldSet.innerHTML = htmlPrompt;
|
fieldSet.innerHTML = htmlPrompt;
|
||||||
|
|
||||||
//Callback de division
|
//Callback de division
|
||||||
@ -317,11 +309,28 @@
|
|||||||
"L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée."
|
"L'heure de séparation doit être compris dans la période de l'assiduité sélectionnée."
|
||||||
);
|
);
|
||||||
|
|
||||||
openAlertModal("Attention", att, "", "var(--color-warning))");
|
openAlertModal("Attention", att, "", "var(--color-warning)");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
openPromptModal("Séparation de l'assiduité sélectionnée", fieldSet, success, () => { }, "var(--color-present)");
|
openPromptModal("Séparation de l'assiduité sélectionnée", fieldSet, success, () => { }, "var(--color-present)");
|
||||||
|
// Initialisation du timepicker
|
||||||
|
const deb = this.selectedAssiduite.date_debut.substring(11,16);
|
||||||
|
const fin = this.selectedAssiduite.date_fin.substring(11,16);
|
||||||
|
setTimeout(()=>{
|
||||||
|
$('#promptTime').timepicker({
|
||||||
|
timeFormat: 'HH:mm',
|
||||||
|
interval: 60 * tick_delay,
|
||||||
|
minTime: deb,
|
||||||
|
startTime: deb,
|
||||||
|
maxTime: fin,
|
||||||
|
dynamic: false,
|
||||||
|
dropdown: true,
|
||||||
|
scrollbar: false,
|
||||||
|
|
||||||
|
});
|
||||||
|
}, 100
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -467,3 +476,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.ui-timepicker-container {
|
||||||
|
z-index: 100000 !important;
|
||||||
|
}
|
||||||
|
</style>
|
@ -157,6 +157,11 @@
|
|||||||
stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
|
stateDiv.textContent = `État: ${assiduite.etat.capitalize()}`;
|
||||||
bubble.appendChild(stateDiv);
|
bubble.appendChild(stateDiv);
|
||||||
|
|
||||||
|
const motifDiv = document.createElement("div");
|
||||||
|
stateDiv.className = "assiduite-why";
|
||||||
|
stateDiv.textContent = `Motif: ${assiduite.desc?.capitalize()}`;
|
||||||
|
bubble.appendChild(motifDiv);
|
||||||
|
|
||||||
const userIdDiv = document.createElement("div");
|
const userIdDiv = document.createElement("div");
|
||||||
userIdDiv.className = "assiduite-user_id";
|
userIdDiv.className = "assiduite-user_id";
|
||||||
userIdDiv.textContent = `saisie le ${formatDateModal(
|
userIdDiv.textContent = `saisie le ${formatDateModal(
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
<select name="moduleimpl_select" id="moduleimpl_select">
|
||||||
|
|
||||||
|
{% with moduleimpl_id=moduleimpl_id %}
|
||||||
|
{% include "assiduites/widgets/simplemoduleimpl_select.j2" %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% for cat, mods in choices.items() %}
|
||||||
|
<optgroup label="{{cat}}">
|
||||||
|
{% for mod in mods %}
|
||||||
|
{% if mod.moduleimpl_id == moduleimpl_id %}
|
||||||
|
<option value="{{mod.moduleimpl_id}}" selected> {{mod.name}} </option>
|
||||||
|
{% else %}
|
||||||
|
<option value="{{mod.moduleimpl_id}}"> {{mod.name}} </option>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</optgroup>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
</select>
|
@ -178,62 +178,25 @@ class HTMLBuilder:
|
|||||||
def bilan_dept():
|
def bilan_dept():
|
||||||
"""Gestionnaire assiduités, page principale"""
|
"""Gestionnaire assiduités, page principale"""
|
||||||
|
|
||||||
# Préparation de la page
|
|
||||||
H = [
|
|
||||||
html_sco_header.sco_header(
|
|
||||||
page_title="Saisie de l'assiduité",
|
|
||||||
javascripts=[
|
|
||||||
"js/assiduites.js",
|
|
||||||
"js/date_utils.js",
|
|
||||||
],
|
|
||||||
cssstyles=[
|
|
||||||
"css/assiduites.css",
|
|
||||||
],
|
|
||||||
),
|
|
||||||
"""<h2>Traitement de l'assiduité</h2>
|
|
||||||
<p class="help">
|
|
||||||
Pour saisir l'assiduité ou consulter les états, il est recommandé de passer par
|
|
||||||
le semestre concerné (saisie par jour ou saisie différée).
|
|
||||||
</p>
|
|
||||||
""",
|
|
||||||
]
|
|
||||||
H.append(
|
|
||||||
"""<p class="help">Pour signaler, annuler ou justifier l'assiduité d'un seul étudiant,
|
|
||||||
choisissez d'abord la personne concernée :</p>"""
|
|
||||||
)
|
|
||||||
# Ajout de la barre de recherche d'étudiant (redirection vers bilan etud)
|
|
||||||
H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"))
|
|
||||||
|
|
||||||
# Gestion des billets d'absences
|
# Gestion des billets d'absences
|
||||||
if current_user.has_permission(
|
if current_user.has_permission(
|
||||||
Permission.AbsChange
|
Permission.AbsChange
|
||||||
) and sco_preferences.get_preference("handle_billets_abs"):
|
) and sco_preferences.get_preference("handle_billets_abs"):
|
||||||
H.append(
|
billets = f"""
|
||||||
f"""
|
|
||||||
<h2 style="margin-top: 30px;">Billets d'absence</h2>
|
<h2 style="margin-top: 30px;">Billets d'absence</h2>
|
||||||
<ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
|
<ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
|
||||||
}">Traitement des billets d'absence en attente</a>
|
}">Traitement des billets d'absence en attente</a>
|
||||||
</li></ul>
|
</li></ul>
|
||||||
"""
|
"""
|
||||||
)
|
else:
|
||||||
|
billets = ""
|
||||||
# Récupération des années d'étude du département
|
# Récupération du département
|
||||||
# (afin de sélectionner une année)
|
|
||||||
dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
|
dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first()
|
||||||
annees: list[int] = sorted(
|
|
||||||
[f.date_debut.year for f in dept.formsemestres],
|
|
||||||
reverse=True,
|
|
||||||
)
|
|
||||||
annee = scu.annee_scolaire() # Année courante, sera utilisée par défaut
|
|
||||||
# Génération d'une liste "json" d'années
|
|
||||||
annees_str: str = "["
|
|
||||||
for ann in annees:
|
|
||||||
annees_str += f"{ann},"
|
|
||||||
annees_str += "]"
|
|
||||||
|
|
||||||
# Récupération d'un formsemestre
|
# Récupération d'un formsemestre
|
||||||
# (pour n'afficher que les assiduites/justificatifs liés au formsemestre)
|
# (pour n'afficher que les justificatifs liés au formsemestre)
|
||||||
formsemestre_id = request.args.get("formsemestre_id", "")
|
formsemestre_id = request.args.get("formsemestre_id", "")
|
||||||
|
formsemestre = None
|
||||||
if formsemestre_id:
|
if formsemestre_id:
|
||||||
try:
|
try:
|
||||||
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||||
@ -241,19 +204,71 @@ def bilan_dept():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
formsemestre_id = ""
|
formsemestre_id = ""
|
||||||
|
|
||||||
# Peuplement du template jinja
|
# <=> Génération du tableau <=>
|
||||||
H.append(
|
|
||||||
render_template(
|
# Récupération des étudiants du département / groupe
|
||||||
"assiduites/pages/bilan_dept.j2",
|
etudids: list[int] = [etud.id for etud in dept.etudiants] # cas département
|
||||||
dept_id=g.scodoc_dept_id,
|
group_ids = request.args.get("group_ids", "")
|
||||||
annee=annee,
|
if group_ids and formsemestre:
|
||||||
annees=annees_str,
|
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||||
formsemestre_id=formsemestre_id,
|
group_ids.split(","),
|
||||||
group_id=request.args.get("group_id", ""),
|
formsemestre_id=formsemestre.id,
|
||||||
|
select_all_when_unspecified=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if groups_infos.members:
|
||||||
|
etudids = [m["etudid"] for m in groups_infos.members]
|
||||||
|
|
||||||
|
# justificatifs (en attente ou modifiés avec les semestres associés)
|
||||||
|
justificatifs_query: Query = Justificatif.query.filter(
|
||||||
|
Justificatif.etat.in_(
|
||||||
|
[scu.EtatJustificatif.ATTENTE, scu.EtatJustificatif.MODIFIE]
|
||||||
),
|
),
|
||||||
|
Justificatif.etudid.in_(etudids),
|
||||||
|
)
|
||||||
|
# Filtrage par semestre si formsemestre_id != ""
|
||||||
|
if formsemestre:
|
||||||
|
justificatifs_query = justificatifs_query.filter(
|
||||||
|
Justificatif.date_debut >= formsemestre.date_debut,
|
||||||
|
Justificatif.date_debut <= formsemestre.date_fin,
|
||||||
|
)
|
||||||
|
|
||||||
|
data = liste_assi.AssiJustifData(
|
||||||
|
assiduites_query=None,
|
||||||
|
justificatifs_query=justificatifs_query,
|
||||||
|
)
|
||||||
|
|
||||||
|
fname: str = "Bilan Département"
|
||||||
|
cache_key: str = "tableau-dept"
|
||||||
|
titre: str = "Justificatifs en attente ou modifiés"
|
||||||
|
|
||||||
|
if formsemestre:
|
||||||
|
fname += f" {formsemestre.titre_annee()}"
|
||||||
|
cache_key += f"-{formsemestre.id}"
|
||||||
|
titre += f" {formsemestre.titre_annee()}"
|
||||||
|
|
||||||
|
if group_ids:
|
||||||
|
cache_key += f" {group_ids}"
|
||||||
|
|
||||||
|
table = _prepare_tableau(
|
||||||
|
data,
|
||||||
|
afficher_etu=True,
|
||||||
|
filename=fname,
|
||||||
|
titre=titre,
|
||||||
|
cache_key=cache_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not table[0]:
|
||||||
|
return table[1]
|
||||||
|
|
||||||
|
# Peuplement du template jinja
|
||||||
|
return render_template(
|
||||||
|
"assiduites/pages/bilan_dept.j2",
|
||||||
|
tableau=table[1],
|
||||||
|
search_etud=sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud"),
|
||||||
|
billets=billets,
|
||||||
|
sco=ScoData(formsemestre=formsemestre),
|
||||||
)
|
)
|
||||||
H.append(html_sco_header.sco_footer())
|
|
||||||
return "\n".join(H)
|
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/ajout_assiduite_etud", methods=["GET", "POST"])
|
@bp.route("/ajout_assiduite_etud", methods=["GET", "POST"])
|
||||||
@ -601,6 +616,29 @@ 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
|
||||||
|
data = liste_assi.AssiJustifData(
|
||||||
|
etud.assiduites.filter(
|
||||||
|
Assiduite.etat != scu.EtatAssiduite.PRESENT, Assiduite.est_just == False
|
||||||
|
),
|
||||||
|
etud.justificatifs.filter(
|
||||||
|
Justificatif.etat.in_(
|
||||||
|
[scu.EtatJustificatif.ATTENTE, scu.EtatJustificatif.MODIFIE]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
table = _prepare_tableau(
|
||||||
|
data,
|
||||||
|
afficher_etu=False,
|
||||||
|
filename=f"Bilan assiduité {etud.nomprenom}",
|
||||||
|
titre="Bilan de l'assiduité de l'étudiant",
|
||||||
|
cache_key=f"tableau-etud-{etud.id}-bilan",
|
||||||
|
)
|
||||||
|
|
||||||
|
if not table[0]:
|
||||||
|
return table[1]
|
||||||
|
|
||||||
# Génération de la page
|
# Génération de la page
|
||||||
return HTMLBuilder(
|
return HTMLBuilder(
|
||||||
header,
|
header,
|
||||||
@ -615,6 +653,7 @@ def bilan_etud():
|
|||||||
"assi_limit_annee",
|
"assi_limit_annee",
|
||||||
dept_id=g.scodoc_dept_id,
|
dept_id=g.scodoc_dept_id,
|
||||||
),
|
),
|
||||||
|
tableau=table[1],
|
||||||
),
|
),
|
||||||
).build()
|
).build()
|
||||||
|
|
||||||
@ -1599,18 +1638,7 @@ def tableau_assiduite_actions():
|
|||||||
|
|
||||||
if obj_type == "assiduite":
|
if obj_type == "assiduite":
|
||||||
# Construction du menu module
|
# Construction du menu module
|
||||||
# XXX ca ne va pas car cela ne prend qu'un semestre
|
module = _module_selector_multiple(objet.etudiant, objet.moduleimpl_id)
|
||||||
# TODO reprendre le menu de la page ajout_assiduite_etud
|
|
||||||
formsemestre = objet.get_formsemestre()
|
|
||||||
if formsemestre:
|
|
||||||
if objet.moduleimpl_id is not None:
|
|
||||||
module = objet.moduleimpl_id
|
|
||||||
elif objet.external_data is not None:
|
|
||||||
module = objet.external_data.get("module", "")
|
|
||||||
module = module.lower() if isinstance(module, str) else module
|
|
||||||
module = _module_selector(formsemestre, module)
|
|
||||||
else:
|
|
||||||
module = "pas de semestre correspondant"
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/pages/tableau_assiduite_actions.j2",
|
"assiduites/pages/tableau_assiduite_actions.j2",
|
||||||
@ -1818,7 +1846,7 @@ def signal_assiduites_diff():
|
|||||||
)
|
)
|
||||||
date_fin: datetime.date = date_deb + datetime.timedelta(days=6)
|
date_fin: datetime.date = date_deb + datetime.timedelta(days=6)
|
||||||
|
|
||||||
etudiants: list[dict] = []
|
etudiants: list[Identite] = []
|
||||||
|
|
||||||
# --- Vérification de la date ---
|
# --- Vérification de la date ---
|
||||||
real_date = scu.is_iso_formated(date, True).date()
|
real_date = scu.is_iso_formated(date, True).date()
|
||||||
@ -1846,15 +1874,9 @@ def signal_assiduites_diff():
|
|||||||
|
|
||||||
# Récupération des étudiants
|
# Récupération des étudiants
|
||||||
etudiants.extend(
|
etudiants.extend(
|
||||||
[
|
[Identite.get_etud(etudid=m["etudid"]) for m in groups_infos.members]
|
||||||
sco_etud.get_etud_info(etudid=m["etudid"], filled=True)[0]
|
|
||||||
for m in groups_infos.members
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
# XXX utiliser des instances d'Identite et non des dict
|
etudiants = list(sorted(etudiants, key=lambda etud: etud.sort_key))
|
||||||
# puis trier avec etud.sort_key
|
|
||||||
# afin de bien prendre en compte nom usuel etc
|
|
||||||
etudiants = list(sorted(etudiants, key=lambda x: x["nom"]))
|
|
||||||
|
|
||||||
# Génération de l'HTML
|
# Génération de l'HTML
|
||||||
|
|
||||||
@ -1962,9 +1984,7 @@ def signale_evaluation_abs(etudid: int = None, evaluation_id: int = None):
|
|||||||
"assiduites.ajout_assiduite_etud",
|
"assiduites.ajout_assiduite_etud",
|
||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
evaluation_id=evaluation.id,
|
evaluation_id=evaluation.id,
|
||||||
date_deb=evaluation.date_debut.strftime(
|
date_deb=evaluation.date_debut.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||||
"%Y-%m-%dT%H:%M:%S"
|
|
||||||
),
|
|
||||||
date_fin=evaluation.date_fin.strftime("%Y-%m-%dT%H:%M:%S"),
|
date_fin=evaluation.date_fin.strftime("%Y-%m-%dT%H:%M:%S"),
|
||||||
moduleimpl_id=evaluation.moduleimpl.id,
|
moduleimpl_id=evaluation.moduleimpl.id,
|
||||||
saisie_eval="true",
|
saisie_eval="true",
|
||||||
@ -2234,6 +2254,32 @@ def _module_selector(formsemestre: FormSemestre, moduleimpl_id: int = None) -> s
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _module_selector_multiple(
|
||||||
|
etud: Identite, moduleimpl_id: int = None, only_form: FormSemestre = None
|
||||||
|
) -> str:
|
||||||
|
modimpls_by_formsemestre = etud.get_modimpls_by_formsemestre(scu.annee_scolaire())
|
||||||
|
choices = {}
|
||||||
|
for formsemestre_id in modimpls_by_formsemestre:
|
||||||
|
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
|
||||||
|
if only_form is not None and formsemestre != only_form:
|
||||||
|
continue
|
||||||
|
# indique le nom du semestre dans le menu (optgroup)
|
||||||
|
choices[formsemestre.titre_annee()] = [
|
||||||
|
{
|
||||||
|
"moduleimpl_id": m.id,
|
||||||
|
"name": f"{m.module.code} {m.module.abbrev or m.module.titre or ''}",
|
||||||
|
}
|
||||||
|
for m in modimpls_by_formsemestre[formsemestre_id]
|
||||||
|
if m.module.ue.type == UE_STANDARD
|
||||||
|
]
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"assiduites/widgets/moduleimpl_selector_multiple.j2",
|
||||||
|
choices=choices,
|
||||||
|
moduleimpl_id=moduleimpl_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _dynamic_module_selector() -> str:
|
def _dynamic_module_selector() -> str:
|
||||||
"""
|
"""
|
||||||
_dynamic_module_selector retourne l'html/css/javascript du selecteur de module dynamique
|
_dynamic_module_selector retourne l'html/css/javascript du selecteur de module dynamique
|
||||||
@ -2630,6 +2676,8 @@ 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 ""
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"assiduites/widgets/assiduite_bubble.j2",
|
"assiduites/widgets/assiduite_bubble.j2",
|
||||||
moduleimpl=moduleimpl_infos,
|
moduleimpl=moduleimpl_infos,
|
||||||
@ -2637,4 +2685,5 @@ def _generate_assiduite_bubble(assiduite: Assiduite) -> str:
|
|||||||
date_debut=assiduite.date_debut.strftime("%d/%m/%Y %H:%M"),
|
date_debut=assiduite.date_debut.strftime("%d/%m/%Y %H:%M"),
|
||||||
date_fin=assiduite.date_fin.strftime("%d/%m/%Y %H:%M"),
|
date_fin=assiduite.date_fin.strftime("%d/%m/%Y %H:%M"),
|
||||||
saisie=saisie,
|
saisie=saisie,
|
||||||
|
motif=motif,
|
||||||
)
|
)
|
||||||
|
@ -557,50 +557,65 @@ def verifier_filtrage_justificatifs(etud: Identite, justificatifs: list[Justific
|
|||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif).count() == 5
|
scass.filter_by_date(etud.justificatifs, Justificatif).count() == 5
|
||||||
), "Filtrage 'Toute Date' mauvais 1"
|
), "Filtrage 'Toute Date' mauvais 1"
|
||||||
|
date = scu.localize_datetime(
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
scu.is_iso_formated("2022-09-01T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
== 5
|
== 5
|
||||||
), "Filtrage 'Toute Date' mauvais 2"
|
), "Filtrage 'Toute Date' mauvais 2"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T08:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T08:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
== 5
|
== 5
|
||||||
), "Filtrage 'date début' mauvais 3"
|
), "Filtrage 'date début' mauvais 3"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T08:00:01+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T08:00:01+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
== 5
|
== 5
|
||||||
), "Filtrage 'date début' mauvais 4"
|
), "Filtrage 'date début' mauvais 4"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_deb=date).count()
|
||||||
== 4
|
== 4
|
||||||
), "Filtrage 'date début' mauvais 5"
|
), "Filtrage 'date début' mauvais 5"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-01T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 0
|
== 0
|
||||||
), "Filtrage 'date fin' mauvais 6"
|
), "Filtrage 'date fin' mauvais 6"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T08:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T08:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 1
|
== 1
|
||||||
), "Filtrage 'date fin' mauvais 7"
|
), "Filtrage 'date fin' mauvais 7"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T10:00:01+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T10:00:01+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 2
|
== 2
|
||||||
), "Filtrage 'date fin' mauvais 8"
|
), "Filtrage 'date fin' mauvais 8"
|
||||||
|
|
||||||
date = scu.localize_datetime("2023-01-03T12:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2023-01-03T12:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
scass.filter_by_date(etud.justificatifs, Justificatif, date_fin=date).count()
|
||||||
== 5
|
== 5
|
||||||
@ -624,8 +639,12 @@ def editer_supprimer_justificatif(etud: Identite):
|
|||||||
# Modification de l'état
|
# Modification de l'état
|
||||||
justi.etat = scu.EtatJustificatif.MODIFIE
|
justi.etat = scu.EtatJustificatif.MODIFIE
|
||||||
# Modification du moduleimpl
|
# Modification du moduleimpl
|
||||||
justi.date_debut = scu.localize_datetime("2023-02-03T11:00:01+01:00")
|
justi.date_debut = scu.localize_datetime(
|
||||||
justi.date_fin = scu.localize_datetime("2023-02-03T12:00:01+01:00")
|
scu.is_iso_formated("2023-02-03T11:00:01+01:00", convert=True)
|
||||||
|
)
|
||||||
|
justi.date_fin = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2023-02-03T12:00:01+01:00", convert=True)
|
||||||
|
)
|
||||||
|
|
||||||
db.session.add(justi)
|
db.session.add(justi)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -639,7 +658,9 @@ def editer_supprimer_justificatif(etud: Identite):
|
|||||||
scass.filter_by_date(
|
scass.filter_by_date(
|
||||||
etud.justificatifs,
|
etud.justificatifs,
|
||||||
Justificatif,
|
Justificatif,
|
||||||
date_deb=scu.localize_datetime("2023-02-01T11:00:00+01:00"),
|
date_deb=scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2023-02-01T11:00:00+01:00", convert=True)
|
||||||
|
),
|
||||||
).count()
|
).count()
|
||||||
== 1
|
== 1
|
||||||
), "Edition de justificatif mauvais 2"
|
), "Edition de justificatif mauvais 2"
|
||||||
@ -930,44 +951,60 @@ def verifier_comptage_et_filtrage_assiduites(
|
|||||||
scass.filter_by_date(etu2.assiduites, Assiduite).count() == 7
|
scass.filter_by_date(etu2.assiduites, Assiduite).count() == 7
|
||||||
), "Filtrage 'Date début' mauvais 1"
|
), "Filtrage 'Date début' mauvais 1"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-01T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
||||||
), "Filtrage 'Date début' mauvais 2"
|
), "Filtrage 'Date début' mauvais 2"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 7
|
||||||
), "Filtrage 'Date début' mauvais 3"
|
), "Filtrage 'Date début' mauvais 3"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T16:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T16:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 4
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_deb=date).count() == 4
|
||||||
), "Filtrage 'Date début' mauvais 4"
|
), "Filtrage 'Date début' mauvais 4"
|
||||||
|
|
||||||
# Date Fin
|
# Date Fin
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-01T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-01T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 0
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 0
|
||||||
), "Filtrage 'Date fin' mauvais 1"
|
), "Filtrage 'Date fin' mauvais 1"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T10:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T10:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 1
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 1
|
||||||
), "Filtrage 'Date fin' mauvais 2"
|
), "Filtrage 'Date fin' mauvais 2"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T10:00:01+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T10:00:01+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 2
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 2
|
||||||
), "Filtrage 'Date fin' mauvais 3"
|
), "Filtrage 'Date fin' mauvais 3"
|
||||||
|
|
||||||
date = scu.localize_datetime("2022-09-05T16:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2022-09-05T16:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 3
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 3
|
||||||
), "Filtrage 'Date fin' mauvais 4"
|
), "Filtrage 'Date fin' mauvais 4"
|
||||||
|
|
||||||
date = scu.localize_datetime("2023-01-04T16:00+01:00")
|
date = scu.localize_datetime(
|
||||||
|
scu.is_iso_formated("2023-01-04T16:00+01:00", convert=True)
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 7
|
scass.filter_by_date(etu2.assiduites, Assiduite, date_fin=date).count() == 7
|
||||||
), "Filtrage 'Date fin' mauvais 5"
|
), "Filtrage 'Date fin' mauvais 5"
|
||||||
|
Loading…
Reference in New Issue
Block a user