This commit is contained in:
Emmanuel Viennet 2023-09-13 21:12:37 +02:00
commit 41944d9d65
13 changed files with 497 additions and 391 deletions

View File

@ -19,7 +19,13 @@ from app.api import api_bp as bp
from app.api import api_web_bp from app.api import api_web_bp
from app.api import get_model_api_object, tools from app.api import get_model_api_object, tools
from app.decorators import permission_required, scodoc from app.decorators import permission_required, scodoc
from app.models import Identite, Justificatif, Departement, FormSemestre from app.models import (
Identite,
Justificatif,
Departement,
FormSemestre,
FormSemestreInscription,
)
from app.models.assiduites import ( from app.models.assiduites import (
compute_assiduites_justified, compute_assiduites_justified,
) )
@ -27,6 +33,7 @@ from app.scodoc.sco_archives_justificatifs import JustificatifArchiver
from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.scodoc.sco_groups import get_group_members
# Partie Modèle # Partie Modèle
@ -145,14 +152,40 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False):
if with_query: if with_query:
justificatifs_query = _filter_manager(request, justificatifs_query) justificatifs_query = _filter_manager(request, justificatifs_query)
data_set: list[dict] = [] data_set: list[dict] = []
for just in justificatifs_query.all(): for just in justificatifs_query:
data = just.to_dict(format_api=True) data_set.append(_set_sems_and_groupe(just))
data_set.append(data)
return data_set return data_set
def _set_sems_and_groupe(justi: Justificatif) -> dict:
from app.scodoc.sco_groups import get_etud_groups
data = justi.to_dict(format_api=True)
formsemestre: FormSemestre = (
FormSemestre.query.join(
FormSemestreInscription,
FormSemestre.id == FormSemestreInscription.formsemestre_id,
)
.filter(
justi.date_debut <= FormSemestre.date_fin,
justi.date_fin >= FormSemestre.date_debut,
FormSemestreInscription.etudid == justi.etudid,
)
.first()
)
if formsemestre:
data["formsemestre"] = {
"id": formsemestre.id,
"title": formsemestre.session_id(),
}
return data
@bp.route( @bp.route(
"/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False} "/justificatifs/formsemestre/<int:formsemestre_id>", defaults={"with_query": False}
) )
@ -732,13 +765,16 @@ def _filter_manager(requested, justificatifs_query):
# cas 5 : formsemestre_id # cas 5 : formsemestre_id
formsemestre_id = requested.args.get("formsemestre_id") formsemestre_id = requested.args.get("formsemestre_id")
if formsemestre_id is not None: if formsemestre_id not in [None, "", -1]:
formsemestre: FormSemestre = None formsemestre: FormSemestre = None
try:
formsemestre_id = int(formsemestre_id) formsemestre_id = int(formsemestre_id)
formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first()
justificatifs_query = scass.filter_by_formsemestre( justificatifs_query = scass.filter_by_formsemestre(
justificatifs_query, Justificatif, formsemestre justificatifs_query, Justificatif, formsemestre
) )
except ValueError:
formsemestre = None
order = requested.args.get("order", None) order = requested.args.get("order", None)
if order is not None: if order is not None:
@ -755,4 +791,15 @@ def _filter_manager(requested, justificatifs_query):
Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee), Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee),
) )
group_id = requested.args.get("group_id", None)
if group_id is not None:
try:
group_id = int(group_id)
etudids: list[int] = [etu["etudid"] for etu in get_group_members(group_id)]
justificatifs_query = justificatifs_query.filter(
Justificatif.etudid.in_(etudids)
)
except ValueError:
group_id = None
return justificatifs_query return justificatifs_query

View File

@ -852,6 +852,11 @@ def _make_listes_sem(formsemestre: FormSemestre, with_absences=True):
}?group_ids=%(group_id)s&formsemestre_id={ }?group_ids=%(group_id)s&formsemestre_id={
formsemestre.formsemestre_id formsemestre.formsemestre_id
}"><button>Saisie différée</button></a> }"><button>Saisie différée</button></a>
<a class="btn" href="{
url_for("assiduites.bilan_dept", scodoc_dept=g.scodoc_dept)
}?group_id=%(group_id)s&formsemestre_id={
formsemestre.formsemestre_id
}"><button>Justificatifs en attente</button></a>
</td> </td>
""" """
else: else:

View File

@ -900,7 +900,7 @@ def form_choix_jour_saisie_hebdo(groups_infos, moduleimpl_id=None):
formsemestre_id=groups_infos.formsemestre_id, formsemestre_id=groups_infos.formsemestre_id,
moduleimpl_id="" if moduleimpl_id is None else moduleimpl_id moduleimpl_id="" if moduleimpl_id is None else moduleimpl_id
) )
}';">Saisie du jour</button> }';">Saisie du jour ({datetime.date.today().strftime('%d/%m/%Y')})</button>
""" """

View File

@ -46,6 +46,7 @@ from app.models import (
Module, Module,
ModuleImpl, ModuleImpl,
ScolarNews, ScolarNews,
Assiduite,
) )
from app.models.etudiants import Identite from app.models.etudiants import Identite
@ -75,6 +76,8 @@ import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import json_error from app.scodoc.sco_utils import json_error
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
from flask_sqlalchemy.query import Query
def convert_note_from_string( def convert_note_from_string(
note: str, note: str,
@ -1102,30 +1105,21 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in
# Groupes auxquels appartient cet étudiant: # Groupes auxquels appartient cet étudiant:
e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id) e["groups"] = sco_groups.get_etud_groups(etudid, formsemestre_id)
# Information sur absence (tenant compte de la demi-journée) # Information sur absence
jour_iso = ( warn_abs_lst: str = ""
evaluation.date_debut.date().isoformat() if evaluation.date_debut else "" if evaluation.date_debut is not None and evaluation.date_fin is not None:
assiduites_etud: Query = etud.assiduites.filter(
Assiduite.etat == scu.EtatAssiduite.ABSENT,
Assiduite.date_debut <= evaluation.date_fin,
Assiduite.date_fin >= evaluation.date_debut,
)
premiere_assi: Assiduite = assiduites_etud.first()
if premiere_assi is not None:
warn_abs_lst: str = (
f"absent {'justifié' if premiere_assi.est_just else ''}"
) )
warn_abs_lst = []
# XXX TODO-ASSIDUITE (issue #686)
if evaluation.is_matin():
nbabs = 0 # TODO-ASSIDUITE sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=True)
nbabsjust = 0 # TODO-ASSIDUITE sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=True)
if nbabs:
if nbabsjust:
warn_abs_lst.append("absent justifié le matin !")
else:
warn_abs_lst.append("absent le matin !")
if evaluation.is_apresmidi():
nbabs = 0 # TODO-ASSIDUITE sco_abs.count_abs(etudid, jour_iso, jour_iso, matin=0)
nbabsjust = 0 # TODO-ASSIDUITE sco_abs.count_abs_just(etudid, jour_iso, jour_iso, matin=0)
if nbabs:
if nbabsjust:
warn_abs_lst.append("absent justifié l'après-midi !")
else:
warn_abs_lst.append("absent l'après-midi !")
e["absinfo"] = '<span class="sn_abs">' + " ".join(warn_abs_lst) + "</span> " e["absinfo"] = '<span class="sn_abs">' + warn_abs_lst + "</span> "
# Note actuelle de l'étudiant: # Note actuelle de l'étudiant:
if etudid in notes_db: if etudid in notes_db:

View File

@ -670,8 +670,8 @@ def AbsencesURL():
def AssiduitesURL(): def AssiduitesURL():
"""URL of Assiduités""" """URL of Assiduités"""
return url_for("assiduites.index_html", scodoc_dept=g.scodoc_dept)[ return url_for("assiduites.bilan_dept", scodoc_dept=g.scodoc_dept)[
: -len("/index_html") : -len("/BilanDept")
] ]

View File

@ -577,7 +577,7 @@ function updateDate() {
return true; return true;
} else { } else {
const att = document.createTextNode( const att = document.createTextNode(
"Le jour sélectionné n'est pas un jour travaillé." `Le jour sélectionné (${formatDate(date)}) n'est pas un jour travaillé.`
); );
openAlertModal("Erreur", att, "", "crimson"); openAlertModal("Erreur", att, "", "crimson");
dateInput.value = dateInput.getAttribute("value"); dateInput.value = dateInput.getAttribute("value");

View File

@ -6,6 +6,7 @@
<section class="nonvalide"> <section class="nonvalide">
<!-- Tableaux des justificatifs à valider (attente / modifié ) --> <!-- Tableaux des justificatifs à valider (attente / modifié ) -->
<h4>Justificatifs en attente (ou modifiés)</h4> <h4>Justificatifs en attente (ou modifiés)</h4>
<a class="icon filter" onclick="filterJusti(true)"></a>
{% include "assiduites/widgets/tableau_justi.j2" %} {% include "assiduites/widgets/tableau_justi.j2" %}
</section> </section>
@ -34,14 +35,17 @@
generate(defAnnee) generate(defAnnee)
} }
let formsemestre_id = "{{formsemestre_id}}"
let group_id = "{{group_id}}"
function getDeptJustificatifsFromPeriod(action) { function getDeptJustificatifsFromPeriod(action) {
const path = getUrl() + `/api/justificatifs/dept/${dept_id}/query?date_debut=${bornes.deb}&date_fin=${bornes.fin}&etat=attente,modifie` 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}&etat=attente,modifie${formsemestre}${group}`
async_get( async_get(
path, path,
(data, status) => { (data, status) => {
console.log(data);
justificatifCallBack(data); justificatifCallBack(data);
}, },
(data, status) => { (data, status) => {
console.error(data, status) console.error(data, status)
@ -88,6 +92,7 @@
filterJustificatifs = { filterJustificatifs = {
"columns": [ "columns": [
"formsemestre",
"etudid", "etudid",
"entry_date", "entry_date",
"date_debut", "date_debut",
@ -100,7 +105,7 @@
"etat": [ "etat": [
"attente", "attente",
"modifie" "modifie"
] ],
} }
} }
const select = document.querySelector('#annee'); const select = document.querySelector('#annee');

View File

@ -4,10 +4,10 @@
<h2>Liste de l'assiduité et des justificatifs de <span class="rouge">{{sco.etud.nomprenom}}</span></h2> <h2>Liste de l'assiduité et des justificatifs de <span class="rouge">{{sco.etud.nomprenom}}</span></h2>
{% include "assiduites/widgets/tableau_base.j2" %} {% include "assiduites/widgets/tableau_base.j2" %}
<h3>Assiduités :</h3> <h3>Assiduités :</h3>
<a class="icon filter" onclick="filter()"></a> <a class="icon filter" onclick="filterAssi()"></a>
{% include "assiduites/widgets/tableau_assi.j2" %} {% include "assiduites/widgets/tableau_assi.j2" %}
<h3>Justificatifs :</h3> <h3>Justificatifs :</h3>
<a class="icon filter" onclick="filter(false)"></a> <a class="icon filter" onclick="filterJusti()"></a>
{% include "assiduites/widgets/tableau_justi.j2" %} {% include "assiduites/widgets/tableau_justi.j2" %}
<ul id="contextMenu" class="context-menu"> <ul id="contextMenu" class="context-menu">
<li id="detailOption">Detail</li> <li id="detailOption">Detail</li>

View File

@ -261,4 +261,185 @@
} }
); );
} }
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>
</div>
`;
const span = document.createElement('span');
span.innerHTML = html
html = span.firstElementChild
const filterHead = html.querySelector('.filter-head');
filterHead.innerHTML = ""
let cols = ["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];
}
})
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 moment.tz(time, TIMEZONE)
}
}
} 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;
}
})
getAllAssiduitesFromEtud(etudid, assiduiteCallBack)
}, () => { }, "#7059FF");
}
</script> </script>

View File

@ -102,6 +102,10 @@
return f.obj_id.includes(obj_id) return f.obj_id.includes(obj_id)
} }
if (k == "formsemestre") {
return f.formsemestre === "" || (el.hasOwnProperty("formsemestre") && el.formsemestre.title.replaceAll('-', ' ').indexOf(f.formsemestre) != -1);
}
return true; return true;
}) })
@ -286,343 +290,6 @@
} }
function filter(assi = true) {
if (assi) {
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>
</div>
`;
const span = document.createElement('span');
span.innerHTML = html
html = span.firstElementChild
const filterHead = html.querySelector('.filter-head');
filterHead.innerHTML = ""
let cols = ["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];
}
})
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 moment.tz(time, TIMEZONE)
}
}
} 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;
}
})
getAllAssiduitesFromEtud(etudid, assiduiteCallBack)
}, () => { }, "#7059FF");
} else {
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>
Raison
<input class="chk" type="checkbox" name="raison" id="raison" checked>
</label>
<label>
Fichier
<input class="chk" type="checkbox" name="fichier" id="fichier" 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>
<label>
Valide
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="valide">
</label>
<label>
Non Valide
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="non_valide">
</label>
<label>
En Attente
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="attente">
</label>
<label>
Modifié
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="modifie">
</label>
</span>
</div>
`;
const span = document.createElement('span');
span.innerHTML = html
html = span.firstElementChild
const filterHead = html.querySelector('.filter-head');
filterHead.innerHTML = ""
let cols = ["entry_date", "date_debut", "date_fin", "etat", "raison", "fichier"];
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 = filterJustificatifs.columns.includes(k)
label.appendChild(s)
label.appendChild(input)
filterHead.appendChild(label)
})
// Mise à jour des filtres
Object.keys(filterJustificatifs.filters).forEach((key) => {
const l = html.querySelector(`.filter-title[for="${key}"]`).parentElement;
if (key.indexOf('date') != -1) {
l.querySelector(`#${key}_pref`).value = filterJustificatifs.filters[key].pref;
l.querySelector(`#${key}_time`).value = filterJustificatifs.filters[key].time.format("YYYY-MM-DDTHH:mm");
} else if (key.indexOf('etat') != -1) {
l.querySelectorAll('input').forEach((e) => {
e.checked = filterJustificatifs.filters[key].includes(e.value)
})
}
})
openPromptModal("Filtrage des Justificatifs", html, () => {
const columns = [...document.querySelectorAll('.chk')]
.map((el) => { if (el.checked) return el.id })
.filter((el) => el)
filterJustificatifs.columns = columns
filterJustificatifs.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 != "") {
filterJustificatifs.filters[key] = {
pref: pref,
time: new moment.tz(time, TIMEZONE)
}
}
} else if (key.indexOf('etat') != -1) {
filterJustificatifs.filters[key] = [...l.querySelectorAll("input:checked")].map((e) => e.value);
}
})
getAllJustificatifsFromEtud(etudid, justificatifCallBack)
}, () => { }, "#7059FF");
}
}
function columnTranslator(colName) { function columnTranslator(colName) {
switch (colName) { switch (colName) {
@ -644,6 +311,8 @@
return "Fichier"; return "Fichier";
case "etudid": case "etudid":
return "Etudiant"; return "Etudiant";
case "formsemestre":
return "Semestre";
} }
} }

View File

@ -99,7 +99,13 @@
} else if (k.indexOf('etudid') != -1) { } else if (k.indexOf('etudid') != -1) {
const e = getEtudiant(justificatif.etudid); const e = getEtudiant(justificatif.etudid);
td.textContent = `${e.prenom.capitalize()} ${e.nom.toUpperCase()}`; td.innerHTML = `<a class="etudinfo" id="line-${justificatif.etudid}" href="BilanEtud?etudid=${justificatif.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`;
} else if (k == "formsemestre") {
if (justificatif.hasOwnProperty("formsemestre")) {
td.textContent = justificatif.formsemestre.title.replaceAll('-', ' ');
} else {
td.textContent = `Pas de Semestre`;
}
} }
else { else {
if (justificatif[k] != null) { if (justificatif[k] != null) {
@ -458,7 +464,191 @@
}) })
} }
function filterJusti(dept = false) {
let dept_html_head = `
<label>
Semestre
<input class="chk" type="checkbox" name="formsemestre" id="formsemestre" checked>
</label>
<label>
Etudiant
<input class="chk" type="checkbox" name="etudid" id="etudid" checked>
</label>
`
let dept_html_body = `
<span class="filter-line">
<span class="filter-title" for="formsemestre">Recherche dans les semestre</span>
<input type="text" name="formsemestre" id="formsemestre" placeholder="S1 2023" >
</span>
`
let html = `
<div class="filter-body">
<h3>Affichage des colonnes:</h3>
<div class="filter-head">
${dept ? dept_html_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>
Raison
<input class="chk" type="checkbox" name="raison" id="raison" checked>
</label>
<label>
Fichier
<input class="chk" type="checkbox" name="fichier" id="fichier" 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>
<label>
Valide
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="valide">
</label>
<label>
Non Valide
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="non_valide">
</label>
<label>
En Attente
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="attente">
</label>
<label>
Modifié
<input checked type="checkbox" name="etat_valide" id="etat_valide" class="" value="modifie">
</label>
</span>
${dept ? dept_html_body : ""}
</div>
`;
const span = document.createElement('span');
span.innerHTML = html
html = span.firstElementChild
const filterHead = html.querySelector('.filter-head');
filterHead.innerHTML = ""
let cols = ["formsemestre", "etudid", "entry_date", "date_debut", "date_fin", "etat", "raison", "fichier"];
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 = filterJustificatifs.columns.includes(k)
label.appendChild(s)
label.appendChild(input)
filterHead.appendChild(label)
})
// Mise à jour des filtres
Object.keys(filterJustificatifs.filters).forEach((key) => {
const l = html.querySelector(`.filter-title[for="${key}"]`).parentElement;
if (key.indexOf('date') != -1) {
l.querySelector(`#${key}_pref`).value = filterJustificatifs.filters[key].pref;
l.querySelector(`#${key}_time`).value = filterJustificatifs.filters[key].time.format("YYYY-MM-DDTHH:mm");
} else if (key.indexOf('etat') != -1) {
l.querySelectorAll('input').forEach((e) => {
e.checked = filterJustificatifs.filters[key].includes(e.value)
})
} else if (key == "formsemestre") {
l.querySelector('#formsemestre').value = filterJustificatifs.filters["formsemestre"];
}
})
openPromptModal("Filtrage des Justificatifs", html, () => {
const columns = [...document.querySelectorAll('.chk')]
.map((el) => { if (el.checked) return el.id })
.filter((el) => el)
filterJustificatifs.columns = columns
filterJustificatifs.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 != "") {
filterJustificatifs.filters[key] = {
pref: pref,
time: new moment.tz(time, TIMEZONE)
}
}
} else if (key.indexOf('etat') != -1) {
filterJustificatifs.filters[key] = [...l.querySelectorAll("input:checked")].map((e) => e.value);
} else if (key == "formsemestre") {
filterJustificatifs.filters["formsemestre"] = l.querySelector('#formsemestre').value;
}
})
if (dept) {
loadAll();
} else {
getAllJustificatifsFromEtud(etudid, justificatifCallBack)
}
}, () => { }, "#7059FF");
}
</script> </script>
<style> <style>
.fich-file { .fich-file {

View File

@ -26,7 +26,7 @@
<a href="{{url_for('notes.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Programmes</a> <br> <a href="{{url_for('notes.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Programmes</a> <br>
{% if current_user.has_permission(sco.Permission.ScoAbsChange)%} {% if current_user.has_permission(sco.Permission.ScoAbsChange)%}
<a href="{{url_for('assiduites.index_html', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Assiduités</a> <br> <a href="{{url_for('assiduites.bilan_dept', scodoc_dept=g.scodoc_dept)}}" class="sidebar">Assiduités</a> <br>
{% endif %} {% endif %}
{% if current_user.has_permission(sco.Permission.ScoUsersAdmin) {% if current_user.has_permission(sco.Permission.ScoUsersAdmin)
or current_user.has_permission(sco.Permission.ScoUsersView) or current_user.has_permission(sco.Permission.ScoUsersView)
@ -86,8 +86,7 @@
<div class="sidebar-bottom"><a href="{{ url_for( 'scodoc.about', <div class="sidebar-bottom"><a href="{{ url_for( 'scodoc.about',
scodoc_dept=g.scodoc_dept ) }}" class="sidebar">À propos</a> scodoc_dept=g.scodoc_dept ) }}" class="sidebar">À propos</a>
<br /> <br />
<a href="{{ scu.SCO_USER_MANUAL }}" <a href="{{ scu.SCO_USER_MANUAL }}" target="_blank" rel="noopener" class="sidebar">Aide</a>
target="_blank" rel="noopener" class="sidebar">Aide</a>
</div> </div>
</div> </div>
<div class="logo-logo"> <div class="logo-logo">

View File

@ -134,10 +134,10 @@ class HTMLBuilder:
@bp.route("/") @bp.route("/")
@bp.route("/index_html") @bp.route("/BilanDept")
@scodoc @scodoc
@permission_required(Permission.ScoAbsChange) @permission_required(Permission.ScoAbsChange)
def index_html(): def bilan_dept():
"""Gestionnaire assiduités, page principale""" """Gestionnaire assiduités, page principale"""
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
@ -180,17 +180,29 @@ def index_html():
reverse=True, reverse=True,
) )
annee = scu.annee_scolaire()
annees_str: str = "[" annees_str: str = "["
for ann in annees: for ann in annees:
annees_str += f"{ann}," annees_str += f"{ann},"
annees_str += "]" annees_str += "]"
formsemestre_id = request.args.get("formsemestre_id", "")
if formsemestre_id:
try:
formsemestre: FormSemestre = FormSemestre.get_formsemestre(formsemestre_id)
annee = formsemestre.annee_scolaire()
except AttributeError:
formsemestre_id = ""
H.append( H.append(
render_template( render_template(
"assiduites/pages/bilan_dept.j2", "assiduites/pages/bilan_dept.j2",
dept_id=g.scodoc_dept_id, dept_id=g.scodoc_dept_id,
annee=scu.annee_scolaire(), annee=annee,
annees=annees_str, annees=annees_str,
formsemestre_id=formsemestre_id,
group_id=request.args.get("group_id", ""),
), ),
) )
H.append(html_sco_header.sco_footer()) H.append(html_sco_header.sco_footer())
@ -565,10 +577,14 @@ def signal_assiduites_group():
real_date = scu.is_iso_formated(date, True).date() real_date = scu.is_iso_formated(date, True).date()
if real_date < formsemestre.date_debut: if real_date < formsemestre.date_debut or real_date > formsemestre.date_fin:
date = formsemestre.date_debut.isoformat() real_str = real_date.strftime("%d/%m/%Y")
elif real_date > formsemestre.date_fin: form_deb = formsemestre.date_debut.strftime("%d/%m/%Y")
date = formsemestre.date_fin.isoformat() form_fin = formsemestre.date_fin.strftime("%d/%m/%Y")
raise ScoValueError(
f"Impossible de saisir les assiduités pour le {real_str}"
+ f" : Jour en dehors du semestre ( {form_deb}{form_fin}) "
)
# --- Restriction en fonction du moduleimpl_id --- # --- Restriction en fonction du moduleimpl_id ---
if moduleimpl_id: if moduleimpl_id: