diff --git a/app/api/assiduites.py b/app/api/assiduites.py index bbd6142f91..dd2be3c9ca 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -27,7 +27,7 @@ from app.models import ( Justificatif, ) from flask_sqlalchemy.query import Query -from app.models.assiduites import get_assiduites_justif +from app.models.assiduites import get_assiduites_justif, get_justifs_from_date from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.sco_utils import json_error @@ -728,7 +728,6 @@ def assiduite_edit(assiduite_id: int): assiduite_unique.etudiant.id, msg=f"assiduite: modif {assiduite_unique}", ) - db.session.add(assiduite_unique) db.session.commit() scass.simple_invalidate_cache(assiduite_unique.to_dict()) @@ -848,15 +847,23 @@ def _edit_singular(assiduite_unique, data): # Cas 3 : desc desc = data.get("desc", False) if desc is not False: - assiduite_unique.desc = desc + assiduite_unique.description = desc # Cas 4 : est_just - est_just = data.get("est_just") - if est_just is not None: - if not isinstance(est_just, bool): - errors.append("param 'est_just' : booléen non reconnu") - else: - assiduite_unique.est_just = est_just + if assiduite_unique.etat == scu.EtatAssiduite.PRESENT: + assiduite_unique.est_just = False + else: + assiduite_unique.est_just = ( + len( + get_justifs_from_date( + assiduite_unique.etudiant.id, + assiduite_unique.date_debut, + assiduite_unique.date_fin, + valid=True, + ) + ) + > 0 + ) if errors: err: str = ", ".join(errors) @@ -1024,6 +1031,19 @@ def _filter_manager(requested, assiduites_query: Query) -> Query: if user_id is not False: assiduites_query: Query = scass.filter_by_user_id(assiduites_query, user_id) + order = requested.args.get("order", None) + if order is not None: + assiduites_query: Query = assiduites_query.order_by(Assiduite.date_debut.desc()) + + courant = requested.args.get("courant", None) + if courant is not None: + annee: int = scu.annee_scolaire() + + assiduites_query: Query = assiduites_query.filter( + Assiduite.date_debut >= scu.date_debut_anne_scolaire(annee), + Assiduite.date_fin <= scu.date_fin_anne_scolaire(annee), + ) + return assiduites_query diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 5f66fa6030..e68b397ac3 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -130,6 +130,8 @@ def justificatifs(etudid: int = None, nip=None, ine=None, with_query: bool = Fal @api_web_bp.route( "/justificatifs/dept//query", defaults={"with_query": True} ) +@bp.route("/justificatifs/dept/", defaults={"with_query": False}) +@bp.route("/justificatifs/dept//query", defaults={"with_query": True}) @login_required @scodoc @as_json @@ -151,6 +153,48 @@ def justificatifs_dept(dept_id: int = None, with_query: bool = False): return data_set +@bp.route( + "/justificatifs/formsemestre/", defaults={"with_query": False} +) +@api_web_bp.route( + "/justificatifs/formsemestre/", defaults={"with_query": False} +) +@bp.route( + "/justificatifs/formsemestre//query", + defaults={"with_query": True}, +) +@api_web_bp.route( + "/justificatifs/formsemestre//query", + defaults={"with_query": True}, +) +@login_required +@scodoc +@as_json +@permission_required(Permission.ScoView) +def justificatifs_formsemestre(formsemestre_id: int, with_query: bool = False): + """Retourne tous les justificatifs du formsemestre""" + formsemestre: FormSemestre = None + formsemestre_id = int(formsemestre_id) + formsemestre = FormSemestre.query.filter_by(id=formsemestre_id).first() + + if formsemestre is None: + return json_error(404, "le paramètre 'formsemestre_id' n'existe pas") + + justificatifs_query = scass.filter_by_formsemestre( + Justificatif.query, Justificatif, formsemestre + ) + + if with_query: + justificatifs_query = _filter_manager(request, justificatifs_query) + + data_set: list[dict] = [] + for justi in justificatifs_query.all(): + data = justi.to_dict(format_api=True) + data_set.append(data) + + return data_set + + @bp.route("/justificatif//create", methods=["POST"]) @api_web_bp.route("/justificatif//create", methods=["POST"]) @bp.route("/justificatif/etudid//create", methods=["POST"]) @@ -696,4 +740,19 @@ def _filter_manager(requested, justificatifs_query): justificatifs_query, Justificatif, formsemestre ) + order = requested.args.get("order", None) + if order is not None: + justificatifs_query: Query = justificatifs_query.order_by( + Justificatif.date_debut.desc() + ) + + courant = requested.args.get("courant", None) + if courant is not None: + annee: int = scu.annee_scolaire() + + justificatifs_query: Query = justificatifs_query.filter( + Justificatif.date_debut >= scu.date_debut_anne_scolaire(annee), + Justificatif.date_fin <= scu.date_fin_anne_scolaire(annee), + ) + return justificatifs_query diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 0f8933e734..994a88e07b 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -134,7 +134,10 @@ class Assiduite(db.Model): if not est_just: est_just = ( - len(_get_assiduites_justif(etud.etudid, date_debut, date_fin)) > 0 + len( + get_justifs_from_date(etud.etudid, date_debut, date_fin, valid=True) + ) + > 0 ) if moduleimpl is not None: @@ -375,16 +378,23 @@ def compute_assiduites_justified( def get_assiduites_justif(assiduite_id: int, long: bool): assi: Assiduite = Assiduite.query.get_or_404(assiduite_id) - return _get_assiduites_justif(assi.etudid, assi.date_debut, assi.date_fin, long) + return get_justifs_from_date(assi.etudid, assi.date_debut, assi.date_fin, long) -def _get_assiduites_justif( - etudid: int, date_debut: datetime, date_fin: datetime, long: bool = False +def get_justifs_from_date( + etudid: int, + date_debut: datetime, + date_fin: datetime, + long: bool = False, + valid: bool = False, ): - justifs: Justificatif = Justificatif.query.filter( + justifs: Query = Justificatif.query.filter( Justificatif.etudid == etudid, Justificatif.date_debut <= date_debut, Justificatif.date_fin >= date_fin, ) + if valid: + justifs = justifs.filter(Justificatif.etat == EtatJustificatif.VALIDE) + return [j.justif_id if not long else j.to_dict(True) for j in justifs] diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py index 3990cd82fd..1ad5332d40 100644 --- a/app/scodoc/sco_assiduites.py +++ b/app/scodoc/sco_assiduites.py @@ -311,7 +311,7 @@ def filter_by_date( ) -def filter_justificatifs_by_etat(justificatifs: Justificatif, etat: str) -> Query: +def filter_justificatifs_by_etat(justificatifs: Query, etat: str) -> Query: """ Filtrage d'une collection de justificatifs en fonction de leur état """ diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index e7514ed80d..d3cd1d6bff 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -610,6 +610,17 @@ class BasePreferences: }, ), # Assiduités + ( + "assi_limit_annee", + { + "initvalue": 1, + "title": "Ne lister que les assiduités de l'année", + "explanation": "Limite l'affichage des listes d'assiduités et de justificatifs à l'année en cours", + "input_type": "boolcheckbox", + "labels": ["non", "oui"], + "category": "assi", + }, + ), ( "forcer_module", { diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index a2c951480d..058c53efb6 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -1107,6 +1107,7 @@ def _get_sorted_etuds(evaluation: Evaluation, etudids: list, formsemestre_id: in evaluation.date_debut.date().isoformat() if evaluation.date_debut 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) diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index 10031d7d5c..2d07244633 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -136,6 +136,8 @@ flex-direction: column; align-items: flex-start; margin: 0 5%; + + cursor: pointer; } .etud_row.def .nom::after, @@ -268,6 +270,7 @@ background-size: cover; } + .rbtn.present::before { background-image: url(../icons/present.svg); } @@ -285,8 +288,8 @@ } .rbtn:checked:before { - outline: 3px solid #7059FF; - border-radius: 5px; + outline: 5px solid #7059FF; + border-radius: 50%; } .rbtn:focus { diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index 73d872ec65..019cb894a2 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1065,8 +1065,22 @@ function actualizeEtudAssiduite(etudid) { }); } -function getAllAssiduitesFromEtud(etudid, action) { - const url_api = getUrl() + `/api/assiduites/${etudid}`; +function getAllAssiduitesFromEtud( + etudid, + action, + order = false, + justifs = false, + courant = false +) { + const url_api = + getUrl() + + `/api/assiduites/${etudid}${ + order + ? "/query?order%°" + .replace("%", justifs ? "&with_justifs" : "") + .replace("°", courant ? "&courant" : "") + : "" + }`; $.ajax({ async: true, @@ -1240,12 +1254,10 @@ function generateEtudRow( - +
@@ -1558,20 +1570,18 @@ function fastJustify(assiduite) { //créer justificatif const justif = { - date_debut: new moment.tz(assiduite.date_debut, TIMEZONE) - .add(1, "s") - .format(), - date_fin: new moment.tz(assiduite.date_fin, TIMEZONE) - .subtract(1, "s") - .format(), + date_debut: new moment.tz(assiduite.date_debut, TIMEZONE).format(), + date_fin: new moment.tz(assiduite.date_fin, TIMEZONE).format(), raison: raison, etat: etat, }; createJustificatif(justif); - // justifyAssiduite(assiduite.assiduite_id, true); generateAllEtudRow(); + try { + loadAll(); + } catch {} }; const content = document.createElement("fieldset"); @@ -1634,8 +1644,17 @@ function createJustificatif(justif, success = () => {}) { }); } -function getAllJustificatifsFromEtud(etudid, action) { - const url_api = getUrl() + `/api/justificatifs/${etudid}`; +function getAllJustificatifsFromEtud( + etudid, + action, + order = false, + courant = false +) { + const url_api = + getUrl() + + `/api/justificatifs/${etudid}${ + order ? "/query?order°".replace("°", courant ? "&courant" : "") : "" + }`; $.ajax({ async: true, type: "GET", diff --git a/app/tables/visu_assiduites.py b/app/tables/visu_assiduites.py index 9d1adff300..82ba036813 100644 --- a/app/tables/visu_assiduites.py +++ b/app/tables/visu_assiduites.py @@ -114,9 +114,18 @@ class RowAssi(tb.Row): compte_justificatifs = scass.filter_by_date( etud.justificatifs, Justificatif, self.dates[0], self.dates[1] - ).count() + ) - self.add_cell("justificatifs", "Justificatifs", f"{compte_justificatifs}") + compte_justificatifs_att = compte_justificatifs.filter(Justificatif.etat == 2) + + self.add_cell( + "justificatifs_att", + "Justificatifs en Attente", + f"{compte_justificatifs_att.count()}", + ) + self.add_cell( + "justificatifs", "Justificatifs", f"{compte_justificatifs.count()}" + ) def _get_etud_stats(self, etud: Identite) -> dict[str, list[str, float, float]]: retour: dict[str, tuple[str, float, float]] = { diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index 4cbc8f590a..aef3d5bbdf 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -19,8 +19,9 @@
Date de début + Journée entière
-
+
Date de fin
@@ -110,16 +111,15 @@ function validateFields() { const field = document.querySelector('.justi-form') - const in_date_debut = field.querySelector('#justi_date_debut'); - const in_date_fin = field.querySelector('#justi_date_fin'); + const { deb, fin } = getDates() - if (in_date_debut.value == "" || in_date_fin.value == "") { + if (deb.value == "" || fin.value == "") { openAlertModal("Erreur détéctée", document.createTextNode("Il faut indiquer une date de début et une date de fin."), "", color = "crimson"); return false; } - const date_debut = moment.tz(in_date_debut.value, TIMEZONE); - const date_fin = moment.tz(in_date_fin.value, TIMEZONE); + const date_debut = moment.tz(deb.value, TIMEZONE); + const date_fin = moment.tz(fin.value, TIMEZONE); 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"); @@ -132,14 +132,14 @@ function fieldsToJustificatif() { const field = document.querySelector('.justi-form') - const date_debut = field.querySelector('#justi_date_debut').value; - const date_fin = field.querySelector('#justi_date_fin').value; + const { deb, fin } = getDates() + const etat = field.querySelector('#justi_etat').value; const raison = field.querySelector('#justi_raison').value; return { - date_debut: date_debut, - date_fin: date_fin, + date_debut: deb, + date_fin: fin, etat: etat, raison: raison, } @@ -218,11 +218,43 @@ } + function dayOnly() { + if (document.getElementById('justi_journee').checked) { + document.getElementById("date_fin").style.display = "none"; + document.getElementById("justi_date_debut").type = "date" + } else { + document.getElementById("date_fin").style.display = "block"; + document.getElementById("justi_date_debut").type = "datetime-local" + } + } + + function getDates() { + if (document.getElementById('justi_journee').checked) { + const date_str = document.getElementById("justi_date_debut").value + + return { + "deb": `${date_str}T${assi_morning}`, + "fin": `${date_str}T${assi_evening}`, + } + } + + return { + "deb": document.getElementById("justi_date_debut").value, + "fin": document.getElementById("justi_date_fin").value, + } + } 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}}'; + window.onload = () => { loadAll(); + document.getElementById('justi_journee').addEventListener('click', () => { dayOnly() }); + dayOnly() } {% endblock pageContent %} \ No newline at end of file diff --git a/app/templates/assiduites/pages/bilan_dept.j2 b/app/templates/assiduites/pages/bilan_dept.j2 index aea0bb8b06..2e8ca5be96 100644 --- a/app/templates/assiduites/pages/bilan_dept.j2 +++ b/app/templates/assiduites/pages/bilan_dept.j2 @@ -29,6 +29,7 @@
{% endblock pageContent %} \ No newline at end of file diff --git a/app/templates/assiduites/pages/liste_assiduites.j2 b/app/templates/assiduites/pages/liste_assiduites.j2 index d18021806a..eb1f9ade32 100644 --- a/app/templates/assiduites/pages/liste_assiduites.j2 +++ b/app/templates/assiduites/pages/liste_assiduites.j2 @@ -48,8 +48,43 @@ \ No newline at end of file diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2 index f3febd833b..835cb30842 100644 --- a/app/templates/assiduites/widgets/minitimeline.j2 +++ b/app/templates/assiduites/widgets/minitimeline.j2 @@ -71,6 +71,11 @@ updateSelectedSelect(getCurrentAssiduiteModuleImplId()); updateJustifyBtn(); } + try { + if (isCalendrier()) { + window.location = `ListeAssiduitesEtud?etudid=${etudid}&assiduite_id=${assiduité.assiduite_id}` + } + } catch { } }); //ajouter affichage assiduites on over setupAssiduiteBuble(block, assiduité); diff --git a/app/templates/assiduites/widgets/tableau_assi.j2 b/app/templates/assiduites/widgets/tableau_assi.j2 index 78c34cf007..fd1ee7df7e 100644 --- a/app/templates/assiduites/widgets/tableau_assi.j2 +++ b/app/templates/assiduites/widgets/tableau_assi.j2 @@ -147,7 +147,7 @@ ${etat}
- Créer par + Créée par ${user}
@@ -239,7 +239,7 @@ edit = setModuleImplId(edit, module); fullEditAssiduites(data.assiduite_id, edit, () => { - try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { } + loadAll(); }) diff --git a/app/templates/assiduites/widgets/tableau_base.j2 b/app/templates/assiduites/widgets/tableau_base.j2 index 5a795af1a9..2b468e6a4d 100644 --- a/app/templates/assiduites/widgets/tableau_base.j2 +++ b/app/templates/assiduites/widgets/tableau_base.j2 @@ -18,6 +18,9 @@ document.addEventListener("click", () => { contextMenu.style.display = "none"; + if (contextMenu.childElementCount > 3) { + contextMenu.removeChild(contextMenu.lastElementChild) + } }); editOption.addEventListener("click", () => { @@ -94,6 +97,11 @@ } } + if (k == "obj_id") { + const obj_id = el.assiduite_id || el.justif_id; + return f.obj_id.includes(obj_id) + } + return true; }) @@ -150,7 +158,7 @@ paginationContainerAssiduites.querySelector('.pagination_moins').addEventListener('click', () => { if (currentPageAssiduites > 1) { currentPageAssiduites--; - paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + "" assiduiteCallBack(array); } @@ -159,7 +167,7 @@ paginationContainerAssiduites.querySelector('.pagination_plus').addEventListener('click', () => { if (currentPageAssiduites < totalPages) { currentPageAssiduites++; - paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + paginationContainerAssiduites.querySelector('#paginationAssi').value = currentPageAssiduites + "" assiduiteCallBack(array); } }) @@ -199,8 +207,12 @@ if (assi) { paginationContainerAssiduites.querySelector('#paginationAssi').appendChild(paginationButton) + if (i == currentPageAssiduites) + paginationContainerAssiduites.querySelector('#paginationAssi').value = i + ""; } else { paginationContainerJustificatifs.querySelector('#paginationJusti').appendChild(paginationButton) + if (i == currentPageJustificatifs) + paginationContainerJustificatifs.querySelector('#paginationJusti').value = i + ""; } } updateActivePaginationButton(assi); @@ -230,8 +242,8 @@ } function loadAll() { - try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { } - try { getAllJustificatifsFromEtud(etudid, justificatifCallBack) } catch (_) { } + try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true, true, assi_limit_annee) } catch (_) { } + try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true, assi_limit_annee) } catch (_) { } } function order(keyword, callback = () => { }, el, assi = true) { @@ -641,6 +653,27 @@ contextMenu.style.top = `${e.clientY - contextMenu.offsetHeight}px`; contextMenu.style.left = `${e.clientX}px`; contextMenu.style.display = "block"; + if (contextMenu.childElementCount > 3) { + contextMenu.removeChild(contextMenu.lastElementChild) + } + if (selectedRow.getAttribute('type') == "assiduite") { + + const li = document.createElement('li') + li.textContent = "Justifier" + + li.addEventListener('click', () => { + let obj_id = selectedRow.getAttribute('obj_id'); + assiduite = Object.values(assiduites).flat().filter((a) => { return a.assiduite_id == obj_id }) + console.log(assiduite[0]) + if (assiduite && !assiduite[0].est_just && assiduite[0].etat != "PRESENT") { + fastJustify(assiduite[0]) + } else { + openAlertModal("Erreur", document.createTextNode("L'assiduité est déjà justifiée ou ne peut pas l'être.")) + } + }) + + contextMenu.appendChild(li) + } } diff --git a/app/templates/assiduites/widgets/tableau_justi.j2 b/app/templates/assiduites/widgets/tableau_justi.j2 index c4b677733c..4b3a502289 100644 --- a/app/templates/assiduites/widgets/tableau_justi.j2 +++ b/app/templates/assiduites/widgets/tableau_justi.j2 @@ -169,7 +169,7 @@ ${etat}
- Créer par + Créé par ${user}
diff --git a/app/templates/assiduites/widgets/timeline.j2 b/app/templates/assiduites/widgets/timeline.j2 index 706c5f504b..576e30218b 100644 --- a/app/templates/assiduites/widgets/timeline.j2 +++ b/app/templates/assiduites/widgets/timeline.j2 @@ -18,6 +18,8 @@ const period_default = {{ periode_defaut }}; + let handleMoving = false; + function createTicks() { let i = t_start; @@ -87,72 +89,92 @@ } - function setupTimeLine(callback) { + function timelineMainEvent(event, callback) { const func_call = callback ? callback : () => { }; - timelineContainer.addEventListener("mousedown", (event) => { - const startX = event.clientX; - if (event.target === periodTimeLine) { - const startLeft = parseFloat(periodTimeLine.style.left); + const startX = (event.clientX || event.changedTouches[0].clientX); - const onMouseMove = (moveEvent) => { - const deltaX = moveEvent.clientX - startX; - const containerWidth = timelineContainer.clientWidth; + if (event.target.classList.contains("period-handle")) { + const startWidth = parseFloat(periodTimeLine.style.width); + const startLeft = parseFloat(periodTimeLine.style.left); + const isLeftHandle = event.target.classList.contains("left"); + handleMoving = true + const onMouseMove = (moveEvent) => { + + if (!handleMoving) return; + + const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX; + const containerWidth = timelineContainer.clientWidth; + const newWidth = + startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100; + + if (isLeftHandle) { const newLeft = startLeft + (deltaX / containerWidth) * 100; + adjustPeriodPosition(newLeft, newWidth); + } else { + adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth); + } - adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width)); + updatePeriodTimeLabel(); + }; + const mouseUp = () => { + snapHandlesToQuarters(); + generateAllEtudRow(); - updatePeriodTimeLabel(); - }; + timelineContainer.removeEventListener("mousemove", onMouseMove); + handleMoving = false; + func_call(); - document.addEventListener("mousemove", onMouseMove); - document.addEventListener( - "mouseup", - () => { - generateAllEtudRow(); - snapHandlesToQuarters(); - document.removeEventListener("mousemove", onMouseMove); - func_call(); - }, - { once: true } - ); - } else if (event.target.classList.contains("period-handle")) { - const startWidth = parseFloat(periodTimeLine.style.width); - const startLeft = parseFloat(periodTimeLine.style.left); - const isLeftHandle = event.target.classList.contains("left"); - - const onMouseMove = (moveEvent) => { - const deltaX = moveEvent.clientX - startX; - const containerWidth = timelineContainer.clientWidth; - const newWidth = - startWidth + ((isLeftHandle ? -deltaX : deltaX) / containerWidth) * 100; - - if (isLeftHandle) { - const newLeft = startLeft + (deltaX / containerWidth) * 100; - adjustPeriodPosition(newLeft, newWidth); - } else { - adjustPeriodPosition(parseFloat(periodTimeLine.style.left), newWidth); - } - - updatePeriodTimeLabel(); - }; - - document.addEventListener("mousemove", onMouseMove); - document.addEventListener( - "mouseup", - () => { - snapHandlesToQuarters(); - generateAllEtudRow(); - - document.removeEventListener("mousemove", onMouseMove); - - func_call(); - - }, - { once: true } - ); } - }); + timelineContainer.addEventListener("mousemove", onMouseMove); + timelineContainer.addEventListener("touchmove", onMouseMove); + document.addEventListener( + "mouseup", + mouseUp, + ); + document.addEventListener( + "touchend", + mouseUp, + ); + } else if (event.target === periodTimeLine) { + + const startLeft = parseFloat(periodTimeLine.style.left); + + const onMouseMove = (moveEvent) => { + console.warn("move Period") + if (handleMoving) return; + const deltaX = (moveEvent.clientX || moveEvent.changedTouches[0].clientX) - startX; + const containerWidth = timelineContainer.clientWidth; + const newLeft = startLeft + (deltaX / containerWidth) * 100; + + adjustPeriodPosition(newLeft, parseFloat(periodTimeLine.style.width)); + + updatePeriodTimeLabel(); + }; + const mouseUp = () => { + generateAllEtudRow(); + snapHandlesToQuarters(); + timelineContainer.removeEventListener("mousemove", onMouseMove); + func_call(); + } + timelineContainer.addEventListener("mousemove", onMouseMove); + timelineContainer.addEventListener("touchmove", onMouseMove); + document.addEventListener( + "mouseup", + mouseUp, + { once: true } + ); + document.addEventListener( + "touchend", + mouseUp, + { once: true } + ); + } + } + + function setupTimeLine(callback) { + timelineContainer.addEventListener("mousedown", (e) => { timelineMainEvent(e, callback) }); + timelineContainer.addEventListener("touchstart", (e) => { timelineMainEvent(e, callback) }); } function adjustPeriodPosition(newLeft, newWidth) { diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 654873b0a7..c40345c9bb 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -162,24 +162,35 @@ def index_html(): """

Pour signaler, annuler ou justifier une assiduité pour un seul étudiant, choisissez d'abord le concerné:

""" ) - H.append(sco_find_etud.form_search_etud()) - # if current_user.has_permission( - # Permission.ScoAbsChange - # ) and sco_preferences.get_preference("handle_billets_abs"): - # H.append( - # f""" - #

Billets d'absence

- # - # """ - # ) + H.append(sco_find_etud.form_search_etud(dest_url="assiduites.bilan_etud")) + if current_user.has_permission( + Permission.ScoAbsChange + ) and sco_preferences.get_preference("handle_billets_abs"): + H.append( + f""" +

Billets d'absence

+ + """ + ) + 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, + ) + + annees_str: str = "[" + for ann in annees: + annees_str += f"{ann}," + annees_str += "]" H.append( render_template( "assiduites/pages/bilan_dept.j2", dept_id=g.scodoc_dept_id, annee=scu.annee_scolaire(), + annees=annees_str, ), ) H.append(html_sco_header.sco_footer()) @@ -299,6 +310,8 @@ def liste_assiduites_etud(): if etud.dept_id != g.scodoc_dept_id: abort(404, "étudiant inexistant dans ce département") + assiduite_id: int = request.args.get("assiduite_id", -1) + header: str = html_sco_header.sco_header( page_title="Liste des assiduités", init_qtip=True, @@ -319,6 +332,11 @@ def liste_assiduites_etud(): "assiduites/pages/liste_assiduites.j2", sco=ScoData(etud), date=datetime.date.today().isoformat(), + assi_id=assiduite_id, + assi_limit_annee=sco_preferences.get_preference( + "assi_limit_annee", + dept_id=g.scodoc_dept_id, + ), ), ).build() @@ -371,6 +389,10 @@ def bilan_etud(): date_fin=date_fin, assi_metric=assi_metric, assi_seuil=_get_seuil(), + assi_limit_annee=sco_preferences.get_preference( + "assi_limit_annee", + dept_id=g.scodoc_dept_id, + ), ), ).build() @@ -412,6 +434,12 @@ def ajout_justificatif_etud(): render_template( "assiduites/pages/ajout_justificatif.j2", sco=ScoData(etud), + assi_limit_annee=sco_preferences.get_preference( + "assi_limit_annee", + dept_id=g.scodoc_dept_id, + ), + assi_morning=ScoDocSiteConfig.get("assi_morning_time", "08:00"), + assi_evening=ScoDocSiteConfig.get("assi_evening_time", "18:00"), ), ).build() diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py index 0ed1bb506c..46016f267a 100644 --- a/tests/unit/test_assiduites.py +++ b/tests/unit/test_assiduites.py @@ -157,7 +157,6 @@ def test_general(test_client): editer_supprimer_justificatif(etuds[0]) -# XXX TODO-ASSIDUITE (issue #696) def verif_migration_abs_assiduites(): """Vérification que le script de migration fonctionne correctement""" downgrade_module(assiduites=True, justificatifs=True)