forked from ScoDoc/ScoDoc
Assiduite: un peu de nettoyage, corrections
This commit is contained in:
parent
2a239ab92f
commit
086b8ee191
@ -5,7 +5,6 @@ from datetime import datetime
|
||||
|
||||
from flask_login import current_user
|
||||
from flask_sqlalchemy.query import Query
|
||||
from psycopg2.errors import InvalidTextRepresentation # c'est ok
|
||||
from sqlalchemy.exc import DataError
|
||||
|
||||
from app import db, log, g, set_sco_dept
|
||||
@ -256,43 +255,19 @@ class Assiduite(ScoDocModel):
|
||||
|
||||
def set_moduleimpl(self, moduleimpl_id: int | str):
|
||||
"""Mise à jour du moduleimpl_id
|
||||
Les valeurs du champs "moduleimpl_id" possibles sont :
|
||||
Les valeurs du champ "moduleimpl_id" possibles sont :
|
||||
- <int> (un id classique)
|
||||
- <str> ("autre" ou "<id>")
|
||||
- None (pas de moduleimpl_id)
|
||||
- "" (pas de moduleimpl_id)
|
||||
Si la valeur est "autre" il faut:
|
||||
- mettre à None assiduité.moduleimpl_id
|
||||
- mettre à jour assiduite.external_data["module"] = "autre"
|
||||
En fonction de la configuration du semestre la valeur `None` peut-être considérée comme invalide.
|
||||
En fonction de la configuration du semestre (option force_module) la valeur "" peut-être
|
||||
considérée comme invalide.
|
||||
- Il faudra donc vérifier que ce n'est pas le cas avant de mettre à jour l'assiduité
|
||||
"""
|
||||
moduleimpl: ModuleImpl = None
|
||||
try:
|
||||
# ne lève une erreur que si moduleimpl_id est une chaine de caractère non parsable (parseInt)
|
||||
moduleimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id)
|
||||
# moduleImpl est soit :
|
||||
# - None si moduleimpl_id==None
|
||||
# - None si moduleimpl_id==<int> non reconnu
|
||||
# - ModuleImpl si <int|str> valide
|
||||
|
||||
# Vérification ModuleImpl not None (raise ScoValueError)
|
||||
if moduleimpl is None and self._check_force_module(moduleimpl):
|
||||
# Ici uniquement si on est autorisé à ne pas avoir de module
|
||||
self.moduleimpl_id = None
|
||||
return
|
||||
|
||||
# Vérification Inscription ModuleImpl (raise ScoValueError)
|
||||
if moduleimpl.est_inscrit(self.etudiant):
|
||||
self.moduleimpl_id = moduleimpl.id
|
||||
else:
|
||||
raise ScoValueError("L'étudiant n'est pas inscrit au module")
|
||||
|
||||
except (DataError, InvalidTextRepresentation) as exc:
|
||||
# On arrive ici si moduleimpl_id == "autre" ou moduleimpl_id == <str> non parsé
|
||||
|
||||
if moduleimpl_id != "autre":
|
||||
raise ScoValueError("Module non reconnu") from exc
|
||||
|
||||
if moduleimpl_id == "autre":
|
||||
# Configuration de external_data pour Module Autre
|
||||
# Si self.external_data None alors on créé un dictionnaire {"module": "autre"}
|
||||
# Sinon on met à jour external_data["module"] à "autre"
|
||||
@ -306,6 +281,29 @@ class Assiduite(ScoDocModel):
|
||||
self.moduleimpl_id = None
|
||||
|
||||
# Ici pas de vérification du force module car on l'a mis dans "external_data"
|
||||
return
|
||||
|
||||
if moduleimpl_id != "":
|
||||
try:
|
||||
moduleimpl_id = int(moduleimpl_id)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("Module non reconnu") from exc
|
||||
moduleimpl: ModuleImpl = ModuleImpl.query.get(moduleimpl_id)
|
||||
|
||||
# ici moduleimpl est None si non spécifié
|
||||
|
||||
# Vérification ModuleImpl not None (raise ScoValueError)
|
||||
if moduleimpl is None:
|
||||
self._check_force_module()
|
||||
# Ici uniquement si on est autorisé à ne pas avoir de module
|
||||
self.moduleimpl_id = None
|
||||
return
|
||||
|
||||
# Vérification Inscription ModuleImpl (raise ScoValueError)
|
||||
if moduleimpl.est_inscrit(self.etudiant):
|
||||
self.moduleimpl_id = moduleimpl.id
|
||||
else:
|
||||
raise ScoValueError("L'étudiant n'est pas inscrit au module")
|
||||
|
||||
def supprime(self):
|
||||
"Supprime l'assiduité. Log et commit."
|
||||
@ -335,7 +333,7 @@ class Assiduite(ScoDocModel):
|
||||
return get_formsemestre_from_data(self.to_dict())
|
||||
|
||||
def get_module(self, traduire: bool = False) -> int | str:
|
||||
"TODO"
|
||||
"TODO documenter"
|
||||
if self.moduleimpl_id is not None:
|
||||
if traduire:
|
||||
modimpl: ModuleImpl = ModuleImpl.query.get(self.moduleimpl_id)
|
||||
@ -364,8 +362,12 @@ class Assiduite(ScoDocModel):
|
||||
|
||||
return f"saisie le {date} {utilisateur}"
|
||||
|
||||
def _check_force_module(self, moduleimpl: ModuleImpl) -> bool:
|
||||
# Vérification si module forcé
|
||||
def _check_force_module(self):
|
||||
"""Vérification si module forcé:
|
||||
Si le module est requis, raise ScoValueError
|
||||
sinon ne fait rien.
|
||||
"""
|
||||
# cherche le formsemestre affecté pour utiliser ses préférences
|
||||
formsemestre: FormSemestre = get_formsemestre_from_data(
|
||||
{
|
||||
"etudid": self.etudid,
|
||||
@ -373,18 +375,15 @@ class Assiduite(ScoDocModel):
|
||||
"date_fin": self.date_fin,
|
||||
}
|
||||
)
|
||||
force: bool
|
||||
|
||||
if formsemestre:
|
||||
force = is_assiduites_module_forced(formsemestre_id=formsemestre.id)
|
||||
else:
|
||||
force = is_assiduites_module_forced(dept_id=self.etudiant.dept_id)
|
||||
|
||||
formsemestre_id = formsemestre.id if formsemestre else None
|
||||
# si pas de formsemestre, utilisera les prefs globales du département
|
||||
dept_id = self.etudiant.dept_id
|
||||
force = is_assiduites_module_forced(
|
||||
formsemestre_id=formsemestre_id, dept_id=dept_id
|
||||
)
|
||||
if force:
|
||||
raise ScoValueError("Module non renseigné")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class Justificatif(ScoDocModel):
|
||||
"""
|
||||
|
@ -419,7 +419,9 @@ def moduleimpl_inscriptions_stats(formsemestre_id):
|
||||
for info in ues_cap_info[ue["ue_id"]]:
|
||||
etud = sco_etud.get_etud_info(etudid=info["etudid"], filled=True)[0]
|
||||
H.append(
|
||||
f"""<li class="etud"><a class="discretelink" href="{
|
||||
f"""<li class="etud"><a class="discretelink etudinfo"
|
||||
id="{info['etudid']}"
|
||||
href="{
|
||||
url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
|
@ -147,6 +147,7 @@ def get_preference(name, formsemestre_id=None, dept_id=None):
|
||||
"""Returns value of named preference.
|
||||
All preferences have a sensible default value, so this
|
||||
function always returns a usable value for all defined preferences names.
|
||||
If dept_id is None, use current dept (g.scodoc_dept_id)
|
||||
"""
|
||||
return get_base_preferences(dept_id=dept_id).get(formsemestre_id, name)
|
||||
|
||||
|
@ -1630,20 +1630,12 @@ def is_entreprises_enabled():
|
||||
def is_assiduites_module_forced(
|
||||
formsemestre_id: int = None, dept_id: int = None
|
||||
) -> bool:
|
||||
"""Vrai si préférence "imposer la saisie du module" sur les assiduités est vraie."""
|
||||
from app.scodoc import sco_preferences
|
||||
|
||||
retour: bool
|
||||
|
||||
if dept_id is None:
|
||||
dept_id = g.scodoc_dept_id
|
||||
|
||||
try:
|
||||
retour = sco_preferences.get_preference(
|
||||
"forcer_module", formsemestre_id=int(formsemestre_id)
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
retour = sco_preferences.get_preference("forcer_module", dept_id=dept_id)
|
||||
return retour
|
||||
return sco_preferences.get_preference(
|
||||
"forcer_module", formsemestre_id=formsemestre_id, dept_id=dept_id
|
||||
)
|
||||
|
||||
|
||||
def get_assiduites_time_config(config_type: str) -> str | int:
|
||||
|
@ -1,235 +0,0 @@
|
||||
{% include "assiduites/widgets/toast.j2" %}
|
||||
{% include "assiduites/widgets/alert.j2" %}
|
||||
|
||||
{% block pageContent %}
|
||||
<div class="pageContent">
|
||||
<h3>Signaler une absence, présence ou retard pour {{etud.html_link_fiche()|safe}}</h3>
|
||||
{% if saisie_eval %}
|
||||
<div id="saisie_eval">
|
||||
<br>
|
||||
<h3>
|
||||
La saisie a été préconfigurée en fonction de l'évaluation. <br>
|
||||
Une fois la saisie terminée, cliquez sur le lien ci-dessous
|
||||
</h3>
|
||||
<a href="{{redirect_url}}">retourner sur la page de l'évaluation</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<section class="assi-form page">
|
||||
<fieldset>
|
||||
<div class="assi-row">
|
||||
<div class="assi-label">
|
||||
<legend for="assi_date_debut" required>Date de début</legend>
|
||||
<input type="text" name="assi_date_debut" id="assi_date_debut" size="10"
|
||||
class="datepicker">
|
||||
<input type="text" name="assi_heure_debut" id="assi_heure_debut" size="5"
|
||||
class="timepicker">
|
||||
<span>Journée entière</span> <input type="checkbox" name="assi_journee" id="assi_journee">
|
||||
</div>
|
||||
<div class="assi-label" id="date_fin">
|
||||
<legend for="assi_date_fin" required>Date de fin</legend>
|
||||
<scodoc-datetime name="assi_date_fin" id="assi_date_fin"></scodoc-datetime>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assi-row">
|
||||
<div class="assi-label">
|
||||
<legend for="assi_etat" required>État de l'assiduité</legend>
|
||||
<select name="assi_etat" id="assi_etat">
|
||||
<option value="absent" selected>Absent</option>
|
||||
<option value="retard">Retard</option>
|
||||
<option value="present">Présent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="assi-row">
|
||||
<div class="assi-label">
|
||||
<legend for="assi_module" required>Module</legend>
|
||||
{% with moduleid="ajout_assiduite_module_impl",label=false %}
|
||||
{% include "assiduites/widgets/moduleimpl_dynamic_selector.j2" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assi-row">
|
||||
<div class="assi-label">
|
||||
<legend for="raison">Raison</legend>
|
||||
<textarea name="raison" id="raison" cols="75" rows="4" maxlength="500"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="assi-row">
|
||||
<button onclick="validerFormulaire(this)">Enregistrer</button>
|
||||
<button onclick="effacerFormulaire()">Remettre à zero</button>
|
||||
</div>
|
||||
|
||||
|
||||
</fieldset>
|
||||
|
||||
</section>
|
||||
<section class="assi-liste">
|
||||
{{tableau | safe }}
|
||||
</section>
|
||||
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.assi-row {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.assi-form fieldset {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.pageContent {
|
||||
max-width: var(--sco-content-max-width);
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.assi-label {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
[required]::after {
|
||||
content: "*";
|
||||
color: var(--color-error);
|
||||
}
|
||||
</style>
|
||||
{% include "sco_timepicker.j2" %}
|
||||
<script>
|
||||
function validateFields() {
|
||||
const field = document.querySelector('.assi-form')
|
||||
const { deb, fin } = getDates()
|
||||
const date_debut = new Date(deb);
|
||||
const date_fin = new Date(fin);
|
||||
|
||||
if (deb == "" || fin == "" || !date_debut.isValid() || !date_fin.isValid()) {
|
||||
openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin valide."), "", color = "crimson");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (date_fin.isBefore(date_debut)) {
|
||||
openAlertModal("Erreur détéctée", document.createTextNode("La date de fin doit se trouver après la date de début."), "", color = "crimson");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function fieldsToAssiduite() {
|
||||
const field = document.querySelector('.assi-form.page')
|
||||
|
||||
const { deb, fin } = getDates()
|
||||
|
||||
const etat = field.querySelector('#assi_etat').value;
|
||||
const raison = field.querySelector('#raison').value;
|
||||
const module = field.querySelector("#ajout_assiduite_module_impl").value;
|
||||
|
||||
return {
|
||||
date_debut: new Date(deb).toFakeIso(),
|
||||
date_fin: new Date(fin).toFakeIso(),
|
||||
etat: etat,
|
||||
description: raison,
|
||||
moduleimpl_id: module,
|
||||
}
|
||||
}
|
||||
|
||||
function validerFormulaire(btn) {
|
||||
if (!validateFields()) return
|
||||
|
||||
const assiduite = fieldsToAssiduite();
|
||||
let assiduite_id = null;
|
||||
|
||||
createAssiduiteComplete(assiduite, etudid);
|
||||
updateTableau();
|
||||
btn.disabled = true;
|
||||
setTimeout(() => {
|
||||
btn.disabled = false;
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
|
||||
function effacerFormulaire() {
|
||||
const field = document.querySelector('.assi-form')
|
||||
|
||||
field.querySelector('#assi_date_debut').value = "";
|
||||
field.querySelector('#assi_date_fin').value = "";
|
||||
field.querySelector('#assi_etat').value = "attente";
|
||||
field.querySelector('#raison').value = "";
|
||||
|
||||
}
|
||||
|
||||
function dayOnly() {
|
||||
const date_deb = document.getElementById("assi_date_debut");
|
||||
const date_fin = document.getElementById("assi_date_fin");
|
||||
|
||||
if (document.getElementById('assi_journee').checked) {
|
||||
date_deb.setAttribute("show", "date")
|
||||
date_fin.setAttribute("show", "date")
|
||||
document.querySelector(`legend[for="assi_date_fin"]`).removeAttribute("required")
|
||||
} else {
|
||||
date_deb.removeAttribute("show")
|
||||
date_fin.removeAttribute("show")
|
||||
document.querySelector(`legend[for="assi_date_fin"]`).setAttribute("required", "")
|
||||
}
|
||||
}
|
||||
|
||||
function getDates() {
|
||||
const date_deb = document.querySelector(".page #assi_date_debut")
|
||||
const date_fin = document.querySelector(".page #assi_date_fin")
|
||||
const journee = document.querySelector('.page #assi_journee').checked
|
||||
const deb = date_deb.valueAsObject.date + "T" + (journee ? assi_morning : date_deb.valueAsObject.time)
|
||||
let fin = "T" + (journee ? assi_evening : date_fin.valueAsObject.time)
|
||||
if (journee) {
|
||||
fin = (date_fin.valueAsObject.date || date_deb.valueAsObject.date) + fin
|
||||
} else {
|
||||
fin = date_fin.valueAsObject.date + fin
|
||||
}
|
||||
|
||||
return {
|
||||
"deb": deb,
|
||||
"fin": fin,
|
||||
}
|
||||
}
|
||||
|
||||
const etudid = {{ sco.etud.id }};
|
||||
|
||||
const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false;
|
||||
const assi_morning = '{{assi_morning}}';
|
||||
const assi_evening = '{{assi_evening}}';
|
||||
|
||||
{% if saisie_eval %}
|
||||
const saisie_eval = true;
|
||||
const date_deb = "{{date_deb}}";
|
||||
const date_fin = "{{date_fin}}";
|
||||
const moduleimpl = {{ moduleimpl_id }};
|
||||
{% else %}
|
||||
const saisie_eval = false;
|
||||
{% endif %}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
document.getElementById('assi_journee').addEventListener('click', () => { dayOnly() });
|
||||
dayOnly()
|
||||
|
||||
if (saisie_eval) {
|
||||
document.getElementById("assi_date_debut").value = Date.removeUTC(date_deb);
|
||||
document.getElementById("assi_date_fin").value = Date.removeUTC(date_fin);
|
||||
} else {
|
||||
const today = (new Date()).format("YYYY-MM-DD");
|
||||
document.getElementById("assi_date_debut").valueAsObject = { date: today, time: assi_morning }
|
||||
document.getElementById("assi_date_fin").valueAsObject = { time: assi_evening }
|
||||
}
|
||||
|
||||
|
||||
document.getElementById("assi_date_debut").addEventListener("blur", (event) => {
|
||||
updateSelect(null, "#ajout_assiduite_module_impl", event.target.valueAsObject.date)
|
||||
})
|
||||
|
||||
updateSelect(saisie_eval ? moduleimpl : "", "#ajout_assiduite_module_impl", document.getElementById("assi_date_debut").valueAsObject.date);
|
||||
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock pageContent %}
|
@ -1,94 +0,0 @@
|
||||
{% block pageContent %}
|
||||
<div class="pageContent">
|
||||
<h3>Assiduites et justificatifs de <span class="rouge">{{sem}}</span> </h3>
|
||||
{% include "assiduites/widgets/tableau_base.j2" %}
|
||||
|
||||
<h4>Assiduité :</h4>
|
||||
<span class="iconline">
|
||||
<a class="icon filter" onclick="filterAssi()"></a>
|
||||
<a class="icon download" onclick="downloadAssi()"></a>
|
||||
</span>
|
||||
{% include "assiduites/widgets/tableau_assi.j2" %}
|
||||
<h4>Justificatifs :</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" %}
|
||||
|
||||
</div>
|
||||
<script>
|
||||
const formsemestre_id = {{ formsemestre_id }};
|
||||
|
||||
function getFormSemestreAssiduites(action) {
|
||||
const path = getUrl() + `/api/assiduites/formsemestre/${formsemestre_id}`
|
||||
async_get(
|
||||
path,
|
||||
(data, status) => {
|
||||
if (action) {
|
||||
action(data)
|
||||
} else {
|
||||
assiduiteCallBack(data);
|
||||
}
|
||||
},
|
||||
(data, status) => {
|
||||
console.error(data, status)
|
||||
errorAlert();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function getFormSemestreJustificatifs(action) {
|
||||
const path = getUrl() + `/api/justificatifs/formsemestre/${formsemestre_id}`
|
||||
async_get(
|
||||
path,
|
||||
(data, status) => {
|
||||
if (action) {
|
||||
action(data)
|
||||
} else {
|
||||
justificatifCallBack(data);
|
||||
}
|
||||
},
|
||||
(data, status) => {
|
||||
console.error(data, status)
|
||||
errorAlert();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function getAssi(action) {
|
||||
try { getFormSemestreAssiduites(action) } catch (_) { }
|
||||
}
|
||||
|
||||
function getJusti(action) {
|
||||
try { getFormSemestreJustificatifs(action) } catch (_) { }
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
|
||||
filterJustificatifs = {
|
||||
"columns": [
|
||||
"etudid",
|
||||
"entry_date",
|
||||
"date_debut",
|
||||
"date_fin",
|
||||
"etat",
|
||||
"raison",
|
||||
"fichier"
|
||||
],
|
||||
"filters": {
|
||||
}
|
||||
}
|
||||
filterAssiduites = {
|
||||
columns: [
|
||||
"etudid", "entry_date", "date_debut", "date_fin", "etat", "moduleimpl_id", "est_just"
|
||||
],
|
||||
"filters": {
|
||||
}
|
||||
}
|
||||
|
||||
loadAll();
|
||||
})
|
||||
|
||||
</script>
|
||||
{% endblock pageContent %}
|
@ -1,160 +0,0 @@
|
||||
{# -*- mode: jinja-html -*- #}
|
||||
{% include "assiduites/widgets/toast.j2" %}
|
||||
{% include "assiduites/widgets/alert.j2" %}
|
||||
{% include "assiduites/widgets/prompt.j2" %}
|
||||
{% include "assiduites/widgets/conflict.j2" %}
|
||||
<div id="page-assiduite-content">
|
||||
{% block content %}
|
||||
<h2>Signalement de l'assiduité de <span class="rouge">{{sco.etud.nomprenom}}</span></h2>
|
||||
|
||||
<div class="infos">
|
||||
Date: <span id="datestr"></span>
|
||||
<input type="text" class="datepicker" name="tl_date" id="tl_date" value="{{ date }}">
|
||||
</div>
|
||||
|
||||
{{timeline|safe}}
|
||||
|
||||
|
||||
<div>
|
||||
{% include "assiduites/widgets/moduleimpl_dynamic_selector.j2" %}
|
||||
<button class="btn" onclick="fastJustify(getCurrentAssiduite(etudid))" id="justif-rapide">Justifier</button>
|
||||
</div>
|
||||
|
||||
<div class="btn_group">
|
||||
<button class="btn" onclick="setTimeLineTimes({{morning}},{{afternoon}})">Journée</button>
|
||||
<button class="btn" onclick="setTimeLineTimes({{morning}},{{lunch}})">Matin</button>
|
||||
<button class="btn" onclick="setTimeLineTimes({{lunch}},{{afternoon}})">Après-midi</button>
|
||||
</div>
|
||||
|
||||
<div class="etud_holder">
|
||||
<div id="etud_row_{{sco.etud.id}}">
|
||||
<div class="index"></div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
{% if saisie_eval %}
|
||||
<div id="saisie_eval">
|
||||
<br>
|
||||
<h3>
|
||||
La saisie de l'assiduité a été préconfigurée en fonction de l'évaluation. <br>
|
||||
Une fois la saisie finie, cliquez sur le lien si dessous pour revenir sur la gestion de l'évaluation
|
||||
</h3>
|
||||
<a href="{{redirect_url}}">retourner sur la page de l'évaluation</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{diff | safe}}
|
||||
|
||||
<div class="legende">
|
||||
<h3>Explication de la timeline</h3>
|
||||
<p>
|
||||
Si la période indiquée par la timeline provoque un conflit d'assiduité pour un étudiant sa ligne deviendra
|
||||
rouge.
|
||||
<br>
|
||||
Dans ce cas il faut résoudre manuellement le conflit : cliquez sur un des boutons d'assiduités pour ouvrir
|
||||
le
|
||||
résolveur de conflit.
|
||||
<br>
|
||||
Correspondance des couleurs :
|
||||
</p>
|
||||
<ul>
|
||||
{% include "assiduites/widgets/legende_couleur.j2" %}
|
||||
</ul>
|
||||
|
||||
<p>Vous pouvez justifier rapidement une assiduité en saisisant l'assiduité puis en appuyant sur "Justifier"</p>
|
||||
|
||||
<h3>Explication de la saisie différée</h3>
|
||||
<p>Si la colonne n'est pas valide elle sera affichée en rouge, passez le curseur sur la colonne pour afficher
|
||||
le message d'erreur</p>
|
||||
<p>Sélectionner la date de début de la colonne mettra automatiquement la date de fin à la durée d'une séance
|
||||
(préférence de département)</p>
|
||||
<p>Modifier le module alors que des informations sont déjà enregistrées pour la période changera leur
|
||||
module.</p>
|
||||
<p>Il y a 4 boutons sur la colonne permettant d'enregistrer l'information pour tous les étudiants</p>
|
||||
<p>Le dernier des boutons retire l'information présente.</p>
|
||||
<p>Vous pouvez ajouter des colonnes en appuyant sur le bouton + </p>
|
||||
<p>Vous pouvez supprimer une colonne en appuyant sur la croix qui se situe dans le coin haut droit de la
|
||||
colonne.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Ajout d'un conteneur pour le loader -->
|
||||
<div class="loader-container" id="loaderContainer">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
const etudid = {{ sco.etud.id }};
|
||||
const nonWorkDays = [{{ nonworkdays| safe }}];
|
||||
|
||||
setupDate(() => {
|
||||
if (updateDate()) {
|
||||
actualizeEtud(etudid);
|
||||
updateSelect();
|
||||
updateSelectedSelect(getCurrentAssiduiteModuleImplId());
|
||||
onlyAbs();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setupTimeLine(() => {
|
||||
if(document.querySelector('.etud_holder .placeholder') != null){
|
||||
generateAllEtudRow();
|
||||
}
|
||||
updateJustifyBtn();
|
||||
});
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
updateDate();
|
||||
getSingleEtud(etudid);
|
||||
actualizeEtud(etudid);
|
||||
updateSelect()
|
||||
updateJustifyBtn();
|
||||
})
|
||||
|
||||
|
||||
function setTimeLineTimes(a, b) {
|
||||
setPeriodValues(a, b);
|
||||
updateJustifyBtn();
|
||||
}
|
||||
|
||||
window.forceModule = "{{ forcer_module }}"
|
||||
window.forceModule = window.forceModule == "True" ? true : false
|
||||
|
||||
const date_deb = "{{date_deb}}";
|
||||
const date_fin = "{{date_fin}}";
|
||||
|
||||
{% if saisie_eval %}
|
||||
createColumn(
|
||||
date_deb,
|
||||
date_fin,
|
||||
{{ moduleimpl_id }}
|
||||
);
|
||||
window.location.href = "#saisie_eval"
|
||||
getAndUpdateCol(1)
|
||||
{% else %}
|
||||
createColumn();
|
||||
{% endif %}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style>
|
||||
.justifie {
|
||||
background-color: rgb(104, 104, 252);
|
||||
color: whitesmoke;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
outline: none;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
@ -1,156 +0,0 @@
|
||||
<div>
|
||||
{% if label != false%}
|
||||
<label for="moduleimpl_select">
|
||||
Module
|
||||
</label>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% if moduleid %}
|
||||
<select id="{{moduleid}}" class="dynaSelect">
|
||||
{% include "assiduites/widgets/simplemoduleimpl_select.j2" %}
|
||||
</select>
|
||||
{% else %}
|
||||
<select id="moduleimpl_select" class="dynaSelect">
|
||||
{% include "assiduites/widgets/simplemoduleimpl_select.j2" %}
|
||||
</select>
|
||||
{% endif %}
|
||||
|
||||
<div id="saved" style="display: none;">
|
||||
{% include "assiduites/widgets/simplemoduleimpl_select.j2" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
function getEtudFormSemestres() {
|
||||
let semestre = {};
|
||||
sync_get(getUrl() + `/api/etudiant/etudid/${etudid}/formsemestres`, (data) => {
|
||||
semestre = data;
|
||||
});
|
||||
return semestre;
|
||||
}
|
||||
|
||||
function filterFormSemestres(semestres, dateIso) {
|
||||
const date = new Date(Date.removeUTC(dateIso));
|
||||
semestres = semestres.filter((fm) => {
|
||||
return date.isBetween(new Date(Date.removeUTC(fm.date_debut_iso)), new Date(Date.removeUTC(fm.date_fin_iso)), '[]');
|
||||
})
|
||||
|
||||
return semestres;
|
||||
}
|
||||
|
||||
function getFormSemestreProgramme(fm_id) {
|
||||
let semestre = {};
|
||||
sync_get(getUrl() + `/api/formsemestre/${fm_id}/programme`, (data) => {
|
||||
semestre = data;
|
||||
});
|
||||
return semestre;
|
||||
}
|
||||
|
||||
function getModulesImplByFormsemestre(semestres) {
|
||||
const map = new Map();
|
||||
|
||||
semestres.forEach((fm) => {
|
||||
const array = [];
|
||||
|
||||
const fm_p = getFormSemestreProgramme(fm.formsemestre_id);
|
||||
["ressources", "saes", "modules"].forEach((r) => {
|
||||
if (r in fm_p) {
|
||||
fm_p[r].forEach((o) => {
|
||||
array.push(getModuleInfos(o))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
map.set(fm.titre_num, array)
|
||||
})
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
function getModuleInfos(obj) {
|
||||
return {
|
||||
moduleimpl_id: obj.moduleimpl_id,
|
||||
titre: obj.module.titre,
|
||||
code: obj.module.code,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function populateSelect(sems, selected, query) {
|
||||
const select = document.querySelector(query);
|
||||
select.innerHTML = document.getElementById('saved').innerHTML
|
||||
sems.forEach((mods, label) => {
|
||||
const optGrp = document.createElement('optgroup');
|
||||
optGrp.label = label
|
||||
|
||||
mods.forEach((obj) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = obj.moduleimpl_id;
|
||||
opt.textContent = `${obj.code} ${obj.titre}`
|
||||
if (obj.moduleimpl_id == selected) {
|
||||
opt.setAttribute('selected', 'true');
|
||||
}
|
||||
|
||||
optGrp.appendChild(opt);
|
||||
})
|
||||
select.appendChild(optGrp);
|
||||
})
|
||||
if (selected === "autre") {
|
||||
select.querySelector('option[value="autre"]').setAttribute('selected', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
function updateSelect(moduleimpl_id, query = "#moduleimpl_select", dateIso = null) {
|
||||
let sem = getEtudFormSemestres()
|
||||
if (!dateIso) {
|
||||
dateIso = getDate().format("YYYY-MM-DD")
|
||||
}
|
||||
|
||||
sem = filterFormSemestres(sem, dateIso)
|
||||
const mod = getModulesImplByFormsemestre(sem)
|
||||
populateSelect(mod, moduleimpl_id, query);
|
||||
}
|
||||
|
||||
function updateSelectedSelect(moduleimpl_id, query = "#moduleimpl_select") {
|
||||
const mod_id = moduleimpl_id != null ? moduleimpl_id : ""
|
||||
document.querySelector(query).value = `${mod_id}`.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
{% if moduleid %}
|
||||
const moduleimpl_dynamic_selector_id = "{{moduleid}}"
|
||||
{% else %}
|
||||
const moduleimpl_dynamic_selector_id = "moduleimpl_select"
|
||||
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
document.getElementById(moduleimpl_dynamic_selector_id).addEventListener('change', (el) => {
|
||||
const assi = getCurrentAssiduite(etudid);
|
||||
if (assi) {
|
||||
editAssiduite(assi.assiduite_id, assi.etat, [assi]);
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
const conflicts = getAssiduitesConflict(etudid);
|
||||
if (conflicts.length > 0) {
|
||||
updateSelectedSelect(getCurrentAssiduiteModuleImplId());
|
||||
}
|
||||
} catch { }
|
||||
}, { once: true });
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#moduleimpl_select {
|
||||
width: 125px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
@ -1,10 +1,7 @@
|
||||
{% if scu.is_assiduites_module_forced(request.args.get('formsemestre_id', None))%}
|
||||
<option value="" disabled> Saisir Module</option>
|
||||
{% if scu.is_assiduites_module_forced(formsemestre_id)%}
|
||||
<option value="" disabled> Saisir Module</option>
|
||||
{% else %}
|
||||
<option value=""> Non spécifié </option>
|
||||
<option value=""> Non spécifié </option>
|
||||
{% endif %}
|
||||
{% if moduleimpl_id == "autre" %}
|
||||
<option value="autre" selected> Tout module </option>
|
||||
{% else %}
|
||||
<option value="autre"> Tout module </option>
|
||||
{% endif %}
|
||||
|
||||
<option value="autre" {{ 'selected' if moduleimpl_id == 'autre' else '' }}>Autre module (pas dans la liste) [{{moduleimpl_id}}]</option>
|
||||
|
@ -1,465 +0,0 @@
|
||||
<table id="assiduiteTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<div>
|
||||
<span>Début</span>
|
||||
<a class="icon order" onclick="order('date_debut', assiduiteCallBack, this)"></a>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div>
|
||||
<span>Fin</span>
|
||||
<a class="icon order" onclick="order('date_fin', assiduiteCallBack, this)"></a>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div>
|
||||
<span>État</span>
|
||||
<a class="icon order" onclick="order('etat', assiduiteCallBack, this)"></a>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div>
|
||||
<span>Module</span>
|
||||
<a class="icon order" onclick="order('moduleimpl_id', assiduiteCallBack, this)"></a>
|
||||
</div>
|
||||
</th>
|
||||
<th>
|
||||
<div>
|
||||
<span>Justifiée</span>
|
||||
<a class="icon order" onclick="order('est_just', assiduiteCallBack, this)"></a>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tableBodyAssiduites">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="paginationContainerAssiduites" class="pagination-container">
|
||||
</div>
|
||||
|
||||
<div style="display: none;" id="cache-module">
|
||||
{% include "assiduites/widgets/moduleimpl_dynamic_selector.j2" %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const paginationContainerAssiduites = document.getElementById("paginationContainerAssiduites");
|
||||
let currentPageAssiduites = 1;
|
||||
let orderAssiduites = true;
|
||||
let filterAssiduites = {
|
||||
columns: [
|
||||
"entry_date", "date_debut", "date_fin", "etat", "moduleimpl_id", "est_just"
|
||||
],
|
||||
filters: {}
|
||||
}
|
||||
const tableBodyAssiduites = document.getElementById("tableBodyAssiduites");
|
||||
|
||||
function assiduiteCallBack(assi) {
|
||||
assi = filterArray(assi, filterAssiduites.filters)
|
||||
renderTableAssiduites(currentPageAssiduites, assi);
|
||||
renderPaginationButtons(assi);
|
||||
|
||||
try { stats() } catch (_) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
function renderTableAssiduites(page, assiduités) {
|
||||
|
||||
generateTableHead(filterAssiduites.columns, true)
|
||||
|
||||
tableBodyAssiduites.innerHTML = "";
|
||||
const start = (page - 1) * itemsPerPage;
|
||||
const end = start + itemsPerPage;
|
||||
|
||||
assiduités.slice(start, end).forEach((assiduite) => {
|
||||
const row = document.createElement("tr");
|
||||
row.setAttribute('type', "assiduite");
|
||||
row.setAttribute('obj_id', assiduite.assiduite_id);
|
||||
|
||||
const etat = assiduite.etat.toLowerCase();
|
||||
row.classList.add(`l-${etat}`);
|
||||
filterAssiduites.columns.forEach((k) => {
|
||||
const td = document.createElement('td');
|
||||
if (k.indexOf('date') != -1) {
|
||||
td.textContent = new Date(Date.removeUTC(assiduite[k])).format(`DD/MM/Y HH:mm`)
|
||||
} else if (k.indexOf("module") != -1) {
|
||||
td.textContent = getModuleImpl(assiduite);
|
||||
} else if (k.indexOf('est_just') != -1) {
|
||||
td.textContent = assiduite[k] ? "Oui" : "Non"
|
||||
if (assiduite[k]) row.classList.add("est_just")
|
||||
} else if (k.indexOf('etudid') != -1) {
|
||||
const e = getEtudiant(assiduite.etudid);
|
||||
|
||||
td.innerHTML = `<a class="etudinfo" id="line-${assiduite.etudid}" href="bilan_etud?etudid=${assiduite.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`;
|
||||
} else {
|
||||
td.textContent = assiduite[k].capitalize()
|
||||
}
|
||||
|
||||
row.appendChild(td)
|
||||
})
|
||||
|
||||
|
||||
row.addEventListener("contextmenu", openContext);
|
||||
|
||||
tableBodyAssiduites.appendChild(row);
|
||||
});
|
||||
updateActivePaginationButton();
|
||||
}
|
||||
|
||||
function detailAssiduites(assiduite_id) {
|
||||
const path = getUrl() + `/api/assiduite/${assiduite_id}`;
|
||||
async_get(
|
||||
path,
|
||||
(data) => {
|
||||
const user = getUser(data);
|
||||
const module = getModuleImpl(data);
|
||||
|
||||
const date_debut = new Date(Date.removeUTC(data.date_debut)).format("DD/MM/YYYY HH:mm");
|
||||
const date_fin = new Date(Date.removeUTC(data.date_fin)).format("DD/MM/YYYY HH:mm");
|
||||
const entry_date = new Date(Date.removeUTC(data.entry_date)).format("DD/MM/YYYY HH:mm");
|
||||
|
||||
const etat = data.etat.capitalize();
|
||||
const desc = data.desc == null ? "" : data.desc;
|
||||
const id = data.assiduite_id;
|
||||
const est_just = data.est_just ? "Oui" : "Non";
|
||||
|
||||
const html = `
|
||||
<div class="obj-detail">
|
||||
<div class="obj-dates">
|
||||
<div id="date_debut" class="obj-part">
|
||||
<span class="obj-title">Date de début</span>
|
||||
<span class="obj-content">${date_debut}</span>
|
||||
</div>
|
||||
<div id="date_fin" class="obj-part">
|
||||
<span class="obj-title">Date de fin</span>
|
||||
<span class="obj-content">${date_fin}</span>
|
||||
</div>
|
||||
<div id="entry_date" class="obj-part">
|
||||
<span class="obj-title">Date de saisie</span>
|
||||
<span class="obj-content">${entry_date}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="obj-mod">
|
||||
<div id="module" class="obj-part">
|
||||
<span class="obj-title">Module</span>
|
||||
<span class="obj-content">${module}</span>
|
||||
</div>
|
||||
<div id="etat" class="obj-part">
|
||||
<span class="obj-title">Etat</span>
|
||||
<span class="obj-content">${etat}</span>
|
||||
</div>
|
||||
<div id="user" class="obj-part">
|
||||
<span class="obj-title">par</span>
|
||||
<span class="obj-content">${user}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="obj-rest">
|
||||
<div id="est_just" class="obj-part">
|
||||
<span class="obj-title">Justifié</span>
|
||||
<span class="obj-content">${est_just}</span>
|
||||
</div>
|
||||
<div id="desc" class="obj-part">
|
||||
<span class="obj-title">Description</span>
|
||||
<p class="obj-content">${desc}</p>
|
||||
</div>
|
||||
<div id="id" class="obj-part" data-assiduite-id="${id}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`
|
||||
|
||||
const el = document.createElement('div');
|
||||
el.innerHTML = html;
|
||||
|
||||
openAlertModal("Détails", el.firstElementChild, null, "var(--color-information)")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function editionAssiduites(assiduite_id) {
|
||||
const path = getUrl() + `/api/assiduite/${assiduite_id}`;
|
||||
async_get(
|
||||
path,
|
||||
(data) => {
|
||||
let module = data.moduleimpl_id;
|
||||
if (
|
||||
module == null && data.hasOwnProperty("external_data") &&
|
||||
data.external_data != null &&
|
||||
data.external_data.hasOwnProperty('module')
|
||||
) {
|
||||
module = data.external_data.module.toLowerCase();
|
||||
}
|
||||
|
||||
const etat = data.etat;
|
||||
let desc = data.desc == null ? "" : data.desc;
|
||||
const html = `
|
||||
<div class="assi-edit">
|
||||
<div class="assi-edit-part">
|
||||
<legend>État de l'assiduité</legend>
|
||||
<select name="etat" id="etat">
|
||||
<option value="present">Présent</option>
|
||||
<option value="retard">En Retard</option>
|
||||
<option value="absent">Absent</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="assi-edit-part">
|
||||
<legend>Module</legend>
|
||||
<select name="module" id="module">
|
||||
</select>
|
||||
</div>
|
||||
<div class="assi-edit-part">
|
||||
<legend>Description</legend>
|
||||
<textarea name="desc" id="desc" cols="50" rows="10" maxlength="500"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
`
|
||||
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = html;
|
||||
const assiEdit = el.firstElementChild;
|
||||
|
||||
assiEdit.querySelector('#etat').value = etat.toLowerCase();
|
||||
assiEdit.querySelector('#desc').value = desc != null ? desc : "";
|
||||
updateSelect(module, '#moduleimpl_select', data.date_debut.split('T')[0])
|
||||
assiEdit.querySelector('#module').replaceWith(document.querySelector('#moduleimpl_select').cloneNode(true));
|
||||
openPromptModal("Modification de l'assiduité", assiEdit, () => {
|
||||
const prompt = document.querySelector('.assi-edit');
|
||||
const etat = prompt.querySelector('#etat').value;
|
||||
const desc = prompt.querySelector('#desc').value;
|
||||
let module = prompt.querySelector('#moduleimpl_select').value;
|
||||
let edit = {
|
||||
"etat": etat,
|
||||
"desc": desc,
|
||||
"external_data": data.external_data
|
||||
}
|
||||
|
||||
edit = setModuleImplId(edit, module);
|
||||
|
||||
fullEditAssiduites(data.assiduite_id, edit, () => {
|
||||
loadAll();
|
||||
})
|
||||
|
||||
|
||||
}, () => { }, "var(--color-information)");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function fullEditAssiduites(assiduite_id, obj, call = () => { }) {
|
||||
const path = getUrl() + `/api/assiduite/${assiduite_id}/edit`;
|
||||
async_post(
|
||||
path,
|
||||
obj,
|
||||
call,
|
||||
(data, status) => {
|
||||
//error
|
||||
console.error(data, status);
|
||||
errorAlert();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function filterAssi() {
|
||||
let html = `
|
||||
<div class="filter-body">
|
||||
<h3>Affichage des colonnes:</h3>
|
||||
<div class="filter-head">
|
||||
<label>
|
||||
Date de saisie
|
||||
<input class="chk" type="checkbox" name="entry_date" id="entry_date">
|
||||
</label>
|
||||
<label>
|
||||
Date de Début
|
||||
<input class="chk" type="checkbox" name="date_debut" id="date_debut" checked>
|
||||
</label>
|
||||
<label>
|
||||
Date de Fin
|
||||
<input class="chk" type="checkbox" name="date_fin" id="date_fin" checked>
|
||||
</label>
|
||||
<label>
|
||||
Etat
|
||||
<input class="chk" type="checkbox" name="etat" id="etat" checked>
|
||||
</label>
|
||||
<label>
|
||||
Module
|
||||
<input class="chk" type="checkbox" name="moduleimpl_id" id="moduleimpl_id" checked>
|
||||
</label>
|
||||
<label>
|
||||
Justifiée
|
||||
<input class="chk" type="checkbox" name="est_just" id="est_just" checked>
|
||||
</label>
|
||||
</div>
|
||||
<hr>
|
||||
<h3>Filtrage des colonnes:</h3>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="entry_date">Date de saisie</span>
|
||||
<select name="entry_date_pref" id="entry_date_pref">
|
||||
<option value="-1">Avant</option>
|
||||
<option value="0">Égal</option>
|
||||
<option value="1">Après</option>
|
||||
</select>
|
||||
<input type="datetime-local" name="entry_date_time" id="entry_date_time">
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="date_debut">Date de début</span>
|
||||
<select name="date_debut_pref" id="date_debut_pref">
|
||||
<option value="-1">Avant</option>
|
||||
<option value="0">Égal</option>
|
||||
<option value="1">Après</option>
|
||||
</select>
|
||||
<input type="datetime-local" name="date_debut_time" id="date_debut_time">
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="date_fin">Date de fin</span>
|
||||
<select name="date_fin_pref" id="date_fin_pref">
|
||||
<option value="-1">Avant</option>
|
||||
<option value="0">Égal</option>
|
||||
<option value="1">Après</option>
|
||||
</select>
|
||||
<input type="datetime-local" name="date_fin_time" id="date_fin_time">
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="etat">Etat</span>
|
||||
<input checked type="checkbox" name="etat_present" id="etat_present" class="rbtn present" value="present">
|
||||
<input checked type="checkbox" name="etat_retard" id="etat_retard" class="rbtn retard" value="retard">
|
||||
<input checked type="checkbox" name="etat_absent" id="etat_absent" class="rbtn absent" value="absent">
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="moduleimpl_id">Module</span>
|
||||
<select id="moduleimpl_id">
|
||||
<option value="">Pas de filtre</option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="est_just">Est Justifiée</span>
|
||||
<select id="est_just">
|
||||
<option value="">Pas de filtre</option>
|
||||
<option value="true">Oui</option>
|
||||
<option value="false">Non</option>
|
||||
</select>
|
||||
</span>
|
||||
<span class="filter-line">
|
||||
<span class="filter-title" for="etud">Rechercher dans les étudiants</span>
|
||||
<input type="text" name="etud" id="etud" placeholder="Anne Onymous" >
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
const span = document.createElement('span');
|
||||
span.innerHTML = html
|
||||
html = span.firstElementChild
|
||||
|
||||
const filterHead = html.querySelector('.filter-head');
|
||||
filterHead.innerHTML = ""
|
||||
let cols = ["etudid", "entry_date", "date_debut", "date_fin", "etat", "moduleimpl_id", "est_just"];
|
||||
|
||||
cols.forEach((k) => {
|
||||
const label = document.createElement('label')
|
||||
label.classList.add('f-label')
|
||||
const s = document.createElement('span');
|
||||
s.textContent = columnTranslator(k);
|
||||
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.classList.add('chk')
|
||||
input.type = "checkbox"
|
||||
input.name = k
|
||||
input.id = k;
|
||||
input.checked = filterAssiduites.columns.includes(k)
|
||||
|
||||
label.appendChild(s)
|
||||
label.appendChild(input)
|
||||
filterHead.appendChild(label)
|
||||
})
|
||||
|
||||
const sl = html.querySelector('.filter-line #moduleimpl_id');
|
||||
let opts = []
|
||||
Object.keys(moduleimpls).forEach((k) => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = k == null ? "null" : k;
|
||||
opt.textContent = moduleimpls[k];
|
||||
opts.push(opt);
|
||||
})
|
||||
|
||||
opts = opts.sort((a, b) => {
|
||||
return a.value < b.value
|
||||
})
|
||||
|
||||
sl.append(...opts);
|
||||
|
||||
// Mise à jour des filtres
|
||||
|
||||
Object.keys(filterAssiduites.filters).forEach((key) => {
|
||||
const l = html.querySelector(`.filter-title[for="${key}"]`).parentElement;
|
||||
if (key.indexOf('date') != -1) {
|
||||
l.querySelector(`#${key}_pref`).value = filterAssiduites.filters[key].pref;
|
||||
l.querySelector(`#${key}_time`).value = filterAssiduites.filters[key].time.format("YYYY-MM-DDTHH:mm");
|
||||
|
||||
} else if (key.indexOf('etat') != -1) {
|
||||
l.querySelectorAll('input').forEach((e) => {
|
||||
e.checked = filterAssiduites.filters[key].includes(e.value)
|
||||
})
|
||||
} else if (key.indexOf("module") != -1) {
|
||||
l.querySelector('#moduleimpl_id').value = filterAssiduites.filters[key];
|
||||
} else if (key.indexOf("est_just") != -1) {
|
||||
l.querySelector('#est_just').value = filterAssiduites.filters[key];
|
||||
} else if (key == "etud") {
|
||||
l.querySelector('#etud').value = filterAssiduites.filters["etud"];
|
||||
}
|
||||
})
|
||||
|
||||
openPromptModal("Filtrage des assiduités", html, () => {
|
||||
|
||||
const columns = [...document.querySelectorAll('.chk')]
|
||||
.map((el) => { if (el.checked) return el.id })
|
||||
.filter((el) => el)
|
||||
|
||||
filterAssiduites.columns = columns
|
||||
filterAssiduites.filters = {}
|
||||
//reste des filtres
|
||||
|
||||
const lines = [...document.querySelectorAll('.filter-line')];
|
||||
|
||||
lines.forEach((l) => {
|
||||
const key = l.querySelector('.filter-title').getAttribute('for');
|
||||
|
||||
if (key.indexOf('date') != -1) {
|
||||
const pref = l.querySelector(`#${key}_pref`).value;
|
||||
const time = l.querySelector(`#${key}_time`).value;
|
||||
if (l.querySelector(`#${key}_time`).value != "") {
|
||||
filterAssiduites.filters[key] = {
|
||||
pref: pref,
|
||||
time: new Date(Date.removeUTC(time))
|
||||
}
|
||||
}
|
||||
} else if (key.indexOf('etat') != -1) {
|
||||
filterAssiduites.filters[key] = [...l.querySelectorAll("input:checked")].map((e) => e.value);
|
||||
} else if (key.indexOf("module") != -1) {
|
||||
filterAssiduites.filters[key] = l.querySelector('#moduleimpl_id').value;
|
||||
} else if (key.indexOf("est_just") != -1) {
|
||||
filterAssiduites.filters[key] = l.querySelector('#est_just').value;
|
||||
} else if (key == "etud") {
|
||||
filterAssiduites.filters["etud"] = l.querySelector('#etud').value;
|
||||
}
|
||||
})
|
||||
|
||||
getAssi(assiduiteCallBack)
|
||||
|
||||
}, () => { }, "var(--color-primary)");
|
||||
|
||||
}
|
||||
|
||||
function downloadAssi() {
|
||||
getAssi((d) => { toCSV(d, filterAssiduites) })
|
||||
}
|
||||
|
||||
function getAssi(action) {
|
||||
try { getAllAssiduitesFromEtud(etudid, action, true, true, assi_limit_annee) } catch (_) { }
|
||||
}
|
||||
|
||||
</script>
|
@ -2101,15 +2101,19 @@ def _module_selector(formsemestre: FormSemestre, moduleimpl_id: int = None) -> s
|
||||
|
||||
return render_template(
|
||||
"assiduites/widgets/moduleimpl_selector.j2",
|
||||
selected=selected,
|
||||
formsemestre_id=formsemestre.id,
|
||||
modules=modules,
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
selected=selected,
|
||||
)
|
||||
|
||||
|
||||
def _module_selector_multiple(
|
||||
etud: Identite, moduleimpl_id: int = None, only_form: FormSemestre = None
|
||||
) -> str:
|
||||
"""menu HTML <select> pour choix moduleimpl
|
||||
Prend les semestres de l'année, sauf si only_form est indiqué.
|
||||
"""
|
||||
modimpls_by_formsemestre = etud.get_modimpls_by_formsemestre(scu.annee_scolaire())
|
||||
choices = {}
|
||||
for formsemestre_id in modimpls_by_formsemestre:
|
||||
@ -2129,22 +2133,13 @@ def _module_selector_multiple(
|
||||
return render_template(
|
||||
"assiduites/widgets/moduleimpl_selector_multiple.j2",
|
||||
choices=choices,
|
||||
formsemestre_id=only_form.id
|
||||
if only_form
|
||||
else list(modimpls_by_formsemestre.keys())[0],
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
)
|
||||
|
||||
|
||||
def _dynamic_module_selector() -> str:
|
||||
"""
|
||||
_dynamic_module_selector retourne l'html/css/javascript du selecteur de module dynamique
|
||||
|
||||
Returns:
|
||||
str: l'html/css/javascript du selecteur de module dynamique
|
||||
"""
|
||||
return render_template(
|
||||
"assiduites/widgets/moduleimpl_dynamic_selector.j2",
|
||||
)
|
||||
|
||||
|
||||
def _timeline(formsemestre_id: int = None, heures=None) -> str:
|
||||
"""
|
||||
_timeline retourne l'html de la timeline
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.6.82"
|
||||
SCOVERSION = "9.6.83"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user