From d55f90469c6d6674ed670bafec28943309361fec Mon Sep 17 00:00:00 2001 From: iziram Date: Sun, 10 Sep 2023 22:11:10 +0200 Subject: [PATCH 01/17] =?UTF-8?q?Assiduit=C3=A9s=20:=20correction=20delete?= =?UTF-8?q?=5Fjustificatif=20sco=5Farchives=5Fjustificatifs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_archives_justificatifs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/scodoc/sco_archives_justificatifs.py b/app/scodoc/sco_archives_justificatifs.py index dc99c3391..1ca486b37 100644 --- a/app/scodoc/sco_archives_justificatifs.py +++ b/app/scodoc/sco_archives_justificatifs.py @@ -144,7 +144,8 @@ class JustificatifArchiver(BaseArchiver): Si trace == True : sauvegarde le nom du/des fichier(s) supprimé(s) dans la trace de l'étudiant """ - if str(etud.id) not in self.list_oids(): + + if str(etud.id) not in self.list_oids(etud.dept_id): raise ValueError(f"Aucune archive pour etudid[{etud.id}]") archive_id = self.get_id_from_name(etud.id, archive_name, dept_id=etud.dept_id) From 864a5a9405ae42424be769546c5565323442e8af Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 08:31:09 +0200 Subject: [PATCH 02/17] Assiduites : fix liste num page #714 --- app/api/assiduites.py | 4 ++++ app/api/justificatifs.py | 6 ++++++ app/scodoc/sco_saisie_notes.py | 1 + app/static/js/assiduites.js | 10 ++++++---- app/templates/assiduites/widgets/tableau_base.j2 | 12 ++++++++---- tests/unit/test_assiduites.py | 1 - 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index bbd6142f9..ab568ae6c 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -1024,6 +1024,10 @@ 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()) + return assiduites_query diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 5f66fa603..3a2977d02 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -696,4 +696,10 @@ 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() + ) + return justificatifs_query diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index a2c951480..058c53efb 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/js/assiduites.js b/app/static/js/assiduites.js index 73d872ec6..a62ebe20a 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1065,8 +1065,9 @@ function actualizeEtudAssiduite(etudid) { }); } -function getAllAssiduitesFromEtud(etudid, action) { - const url_api = getUrl() + `/api/assiduites/${etudid}`; +function getAllAssiduitesFromEtud(etudid, action, order = false) { + const url_api = + getUrl() + `/api/assiduites/${etudid}${order ? "/query?order" : ""}`; $.ajax({ async: true, @@ -1634,8 +1635,9 @@ function createJustificatif(justif, success = () => {}) { }); } -function getAllJustificatifsFromEtud(etudid, action) { - const url_api = getUrl() + `/api/justificatifs/${etudid}`; +function getAllJustificatifsFromEtud(etudid, action, order = false) { + const url_api = + getUrl() + `/api/justificatifs/${etudid}${order ? "/query?order" : ""}`; $.ajax({ async: true, type: "GET", diff --git a/app/templates/assiduites/widgets/tableau_base.j2 b/app/templates/assiduites/widgets/tableau_base.j2 index 5a795af1a..f1180c6ce 100644 --- a/app/templates/assiduites/widgets/tableau_base.j2 +++ b/app/templates/assiduites/widgets/tableau_base.j2 @@ -150,7 +150,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 +159,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 +199,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 +234,8 @@ } function loadAll() { - try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { } - try { getAllJustificatifsFromEtud(etudid, justificatifCallBack) } catch (_) { } + try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true) } catch (_) { } + try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true) } catch (_) { } } function order(keyword, callback = () => { }, el, assi = true) { diff --git a/tests/unit/test_assiduites.py b/tests/unit/test_assiduites.py index 0ed1bb506..46016f267 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) From 6a5c594e683ac84c7a1dd6df74719592c215e346 Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 08:34:56 +0200 Subject: [PATCH 03/17] Assiduites : fast justify plus court que assi fix #716 --- app/static/js/assiduites.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index a62ebe20a..20902c4a9 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1559,12 +1559,8 @@ 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, }; From d0159df66526e0d57e0e069a14fd023f9801c9b9 Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 09:05:29 +0200 Subject: [PATCH 04/17] Assiduites : edit assi fix est_just #717 --- app/api/assiduites.py | 22 +++++++++++++------ app/models/assiduites.py | 20 ++++++++++++----- .../assiduites/widgets/tableau_assi.j2 | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index ab568ae6c..72bbbeda7 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 @@ -851,12 +851,20 @@ def _edit_singular(assiduite_unique, data): assiduite_unique.desc = 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) diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 0f8933e73..994a88e07 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/templates/assiduites/widgets/tableau_assi.j2 b/app/templates/assiduites/widgets/tableau_assi.j2 index 78c34cf00..8ef6848a5 100644 --- a/app/templates/assiduites/widgets/tableau_assi.j2 +++ b/app/templates/assiduites/widgets/tableau_assi.j2 @@ -239,7 +239,7 @@ edit = setModuleImplId(edit, module); fullEditAssiduites(data.assiduite_id, edit, () => { - try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack) } catch (_) { } + loadAll(); }) From c4c7e0ef806e04d6c904dd3a9925ffe0e34cd17f Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 09:16:50 +0200 Subject: [PATCH 05/17] Assiduites : fix description assi #719 --- app/api/assiduites.py | 3 +-- app/templates/assiduites/widgets/tableau_assi.j2 | 2 +- app/templates/assiduites/widgets/tableau_justi.j2 | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index 72bbbeda7..041bb9a0f 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -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,7 +847,7 @@ 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 if assiduite_unique.etat == scu.EtatAssiduite.PRESENT: diff --git a/app/templates/assiduites/widgets/tableau_assi.j2 b/app/templates/assiduites/widgets/tableau_assi.j2 index 8ef6848a5..fd1ee7df7 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}
diff --git a/app/templates/assiduites/widgets/tableau_justi.j2 b/app/templates/assiduites/widgets/tableau_justi.j2 index c4b677733..4b3a50228 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}
From 044926fd62819d818cc0de68bdcaa8db172a36f6 Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 10:45:21 +0200 Subject: [PATCH 06/17] Assiduites : saisie sur mobile #723 --- app/templates/assiduites/widgets/timeline.j2 | 138 +++++++++++-------- 1 file changed, 80 insertions(+), 58 deletions(-) diff --git a/app/templates/assiduites/widgets/timeline.j2 b/app/templates/assiduites/widgets/timeline.j2 index 706c5f504..576e30218 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) { From ba113f9cccd62ed10853776817a0538f2295b309 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 11 Sep 2023 07:11:52 +0200 Subject: [PATCH 07/17] Fix sco_archives_justificatifs --- app/scodoc/sco_archives_justificatifs.py | 1 - sco_version.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scodoc/sco_archives_justificatifs.py b/app/scodoc/sco_archives_justificatifs.py index 1ca486b37..14a779bf8 100644 --- a/app/scodoc/sco_archives_justificatifs.py +++ b/app/scodoc/sco_archives_justificatifs.py @@ -144,7 +144,6 @@ class JustificatifArchiver(BaseArchiver): Si trace == True : sauvegarde le nom du/des fichier(s) supprimé(s) dans la trace de l'étudiant """ - if str(etud.id) not in self.list_oids(etud.dept_id): raise ValueError(f"Aucune archive pour etudid[{etud.id}]") diff --git a/sco_version.py b/sco_version.py index f10bb0ce6..a44e5b0a1 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.6.26" +SCOVERSION = "9.6.27" SCONAME = "ScoDoc" From 740a7defacc14cbd8869c90cf254797a292a245c Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 11:11:00 +0200 Subject: [PATCH 08/17] Assiduites : index find etud renvoi sur bilan etud #709 --- app/views/assiduites.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 654873b0a..3736d45f3 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -162,18 +162,18 @@ 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

+ + """ + ) H.append( render_template( From b27cf5198f3864abd01ff76c265e02bbd2e96bed Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 11:30:43 +0200 Subject: [PATCH 09/17] =?UTF-8?q?Assiduites=20:=20Am=C3=A9lioration=20cont?= =?UTF-8?q?raste=20bouton=20=C3=A9tat=20#708?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/static/css/assiduites.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index 10031d7d5..206cdc56e 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -268,6 +268,7 @@ background-size: cover; } + .rbtn.present::before { background-image: url(../icons/present.svg); } @@ -285,8 +286,8 @@ } .rbtn:checked:before { - outline: 3px solid #7059FF; - border-radius: 5px; + outline: 5px solid #7059FF; + border-radius: 50%; } .rbtn:focus { From 673179c8f7cc8879ec6a36e1e93567e13e0ed0cf Mon Sep 17 00:00:00 2001 From: iziram Date: Mon, 11 Sep 2023 15:55:18 +0200 Subject: [PATCH 10/17] =?UTF-8?q?Assiduit=C3=A9s=20:=20issue=20712=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_assiduites.py | 2 +- app/static/css/assiduites.css | 2 ++ app/static/js/assiduites.js | 22 +++++++++---- app/templates/assiduites/pages/calendrier.j2 | 2 ++ .../assiduites/pages/liste_assiduites.j2 | 32 ++++++++++++++++++ .../assiduites/widgets/minitimeline.j2 | 5 +++ .../assiduites/widgets/tableau_base.j2 | 33 +++++++++++++++++-- app/views/assiduites.py | 3 ++ 8 files changed, 91 insertions(+), 10 deletions(-) diff --git a/app/scodoc/sco_assiduites.py b/app/scodoc/sco_assiduites.py index 3990cd82f..1ad5332d4 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/static/css/assiduites.css b/app/static/css/assiduites.css index 206cdc56e..2d0724463 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, diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index 20902c4a9..1456f6714 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1065,9 +1065,17 @@ function actualizeEtudAssiduite(etudid) { }); } -function getAllAssiduitesFromEtud(etudid, action, order = false) { +function getAllAssiduitesFromEtud( + etudid, + action, + order = false, + justifs = false +) { const url_api = - getUrl() + `/api/assiduites/${etudid}${order ? "/query?order" : ""}`; + getUrl() + + `/api/assiduites/${etudid}${ + order ? "/query?order%".replace("%", justifs ? "&with_justifs" : "") : "" + }`; $.ajax({ async: true, @@ -1241,12 +1249,10 @@ function generateEtudRow( - +
@@ -1567,8 +1573,10 @@ function fastJustify(assiduite) { createJustificatif(justif); - // justifyAssiduite(assiduite.assiduite_id, true); generateAllEtudRow(); + try { + loadAll(); + } catch {} }; const content = document.createElement("fieldset"); diff --git a/app/templates/assiduites/pages/calendrier.j2 b/app/templates/assiduites/pages/calendrier.j2 index ce8e9e0f1..3f8099342 100644 --- a/app/templates/assiduites/pages/calendrier.j2 +++ b/app/templates/assiduites/pages/calendrier.j2 @@ -354,5 +354,7 @@ setterAnnee(defAnnee) }; + + function isCalendrier() { return true } {% 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 d18021806..80e14a6d6 100644 --- a/app/templates/assiduites/pages/liste_assiduites.j2 +++ b/app/templates/assiduites/pages/liste_assiduites.j2 @@ -48,8 +48,40 @@ \ No newline at end of file diff --git a/app/templates/assiduites/widgets/minitimeline.j2 b/app/templates/assiduites/widgets/minitimeline.j2 index f3febd833..835cb3084 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_base.j2 b/app/templates/assiduites/widgets/tableau_base.j2 index f1180c6ce..2dec4a2ce 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; }) @@ -234,8 +242,8 @@ } function loadAll() { - try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true) } catch (_) { } - try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true) } catch (_) { } + try { getAllAssiduitesFromEtud(etudid, assiduiteCallBack, true, true) } catch (_) { } + try { getAllJustificatifsFromEtud(etudid, justificatifCallBack, true, true) } catch (_) { } } function order(keyword, callback = () => { }, el, assi = true) { @@ -645,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/views/assiduites.py b/app/views/assiduites.py index 3736d45f3..b11970541 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -299,6 +299,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 +321,7 @@ def liste_assiduites_etud(): "assiduites/pages/liste_assiduites.j2", sco=ScoData(etud), date=datetime.date.today().isoformat(), + assi_id=assiduite_id, ), ).build() From 306edaf63d1181cfac555149d40b38de4f09aacd Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 11 Sep 2023 17:58:12 +0200 Subject: [PATCH 11/17] Tri responsable formsemestre par nom. --- app/models/formsemestre.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index e29ffbfc3..8edc88a32 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -18,6 +18,7 @@ from flask_login import current_user from flask import flash, g, url_for from sqlalchemy.sql import text +from sqlalchemy import func import app.scodoc.sco_utils as scu from app import db, log @@ -138,6 +139,7 @@ class FormSemestre(db.Model): secondary="notes_formsemestre_responsables", lazy=True, backref=db.backref("formsemestres", lazy=True), + order_by=func.upper(User.nom), ) partitions = db.relationship( "Partition", From 1ec07a9329966bdbeb12070fe03932743c4e3b28 Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 12 Sep 2023 09:37:03 +0200 Subject: [PATCH 12/17] Assiduites : fin issue #712 --- app/api/assiduites.py | 9 ++++++++ app/api/justificatifs.py | 9 ++++++++ app/scodoc/sco_preferences.py | 11 +++++++++ app/static/js/assiduites.js | 21 +++++++++++++---- .../assiduites/pages/ajout_justificatif.j2 | 3 +++ app/templates/assiduites/pages/bilan_dept.j2 | 20 ++++++++++------ app/templates/assiduites/pages/bilan_etud.j2 | 3 +++ .../assiduites/pages/liste_assiduites.j2 | 3 +++ .../assiduites/widgets/tableau_base.j2 | 4 ++-- app/views/assiduites.py | 23 +++++++++++++++++++ 10 files changed, 93 insertions(+), 13 deletions(-) diff --git a/app/api/assiduites.py b/app/api/assiduites.py index 041bb9a0f..dd2be3c9c 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -1035,6 +1035,15 @@ def _filter_manager(requested, assiduites_query: Query) -> Query: 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 3a2977d02..9c1fd0010 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -702,4 +702,13 @@ def _filter_manager(requested, justificatifs_query): 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/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index e7514ed80..d3cd1d6bf 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/static/js/assiduites.js b/app/static/js/assiduites.js index 1456f6714..019cb894a 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1069,12 +1069,17 @@ function getAllAssiduitesFromEtud( etudid, action, order = false, - justifs = false + justifs = false, + courant = false ) { const url_api = getUrl() + `/api/assiduites/${etudid}${ - order ? "/query?order%".replace("%", justifs ? "&with_justifs" : "") : "" + order + ? "/query?order%°" + .replace("%", justifs ? "&with_justifs" : "") + .replace("°", courant ? "&courant" : "") + : "" }`; $.ajax({ @@ -1639,9 +1644,17 @@ function createJustificatif(justif, success = () => {}) { }); } -function getAllJustificatifsFromEtud(etudid, action, order = false) { +function getAllJustificatifsFromEtud( + etudid, + action, + order = false, + courant = false +) { const url_api = - getUrl() + `/api/justificatifs/${etudid}${order ? "/query?order" : ""}`; + getUrl() + + `/api/justificatifs/${etudid}${ + order ? "/query?order°".replace("°", courant ? "&courant" : "") : "" + }`; $.ajax({ async: true, type: "GET", diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index 4cbc8f590..878d2fa06 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -221,6 +221,9 @@ const etudid = {{ sco.etud.id }}; + + const assi_limit_annee = "{{ assi_limit_annee }}" == "True" ? true : false; + window.onload = () => { loadAll(); } diff --git a/app/templates/assiduites/pages/bilan_dept.j2 b/app/templates/assiduites/pages/bilan_dept.j2 index aea0bb8b0..2e8ca5be9 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/views/assiduites.py b/app/views/assiduites.py index 55b96e470..c40345c9b 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -174,7 +174,7 @@ def index_html(): """ ) - dept : Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first() + dept: Departement = Departement.query.filter_by(id=g.scodoc_dept_id).first() annees: list[int] = sorted( [f.date_debut.year for f in dept.formsemestres], reverse=True, @@ -438,6 +438,8 @@ def ajout_justificatif_etud(): "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() From 602b5084679c02fbf2df0aea49d1a8edb92e0e26 Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 12 Sep 2023 13:40:03 +0200 Subject: [PATCH 14/17] Assiduites : route formsemestre justificatifs --- app/api/justificatifs.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 9c1fd0010..099d9e6c5 100644 --- a/app/api/justificatifs.py +++ b/app/api/justificatifs.py @@ -151,6 +151,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"]) From 996e83f3b3ed55028c86b2df3db928b8a607e344 Mon Sep 17 00:00:00 2001 From: iziram Date: Tue, 12 Sep 2023 14:58:01 +0200 Subject: [PATCH 15/17] =?UTF-8?q?Assiduit=C3=A9s=20:=20publication=20api?= =?UTF-8?q?=20justificatif=20route=20dept?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/justificatifs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/api/justificatifs.py b/app/api/justificatifs.py index 099d9e6c5..e68b397ac 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 From 23e189c21a8db0f2811b85cd9d62844ae12c114a Mon Sep 17 00:00:00 2001 From: iziram Date: Sun, 10 Sep 2023 22:11:10 +0200 Subject: [PATCH 16/17] =?UTF-8?q?Assiduit=C3=A9s=20:=20correction=20delete?= =?UTF-8?q?=5Fjustificatif=20sco=5Farchives=5Fjustificatifs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_archives_justificatifs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scodoc/sco_archives_justificatifs.py b/app/scodoc/sco_archives_justificatifs.py index 14a779bf8..1ca486b37 100644 --- a/app/scodoc/sco_archives_justificatifs.py +++ b/app/scodoc/sco_archives_justificatifs.py @@ -144,6 +144,7 @@ class JustificatifArchiver(BaseArchiver): Si trace == True : sauvegarde le nom du/des fichier(s) supprimé(s) dans la trace de l'étudiant """ + if str(etud.id) not in self.list_oids(etud.dept_id): raise ValueError(f"Aucune archive pour etudid[{etud.id}]") From e4cf023e464579fc783d39b342c0c083ace70dfd Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 11 Sep 2023 07:11:52 +0200 Subject: [PATCH 17/17] Fix sco_archives_justificatifs --- app/scodoc/sco_archives_justificatifs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/scodoc/sco_archives_justificatifs.py b/app/scodoc/sco_archives_justificatifs.py index 1ca486b37..14a779bf8 100644 --- a/app/scodoc/sco_archives_justificatifs.py +++ b/app/scodoc/sco_archives_justificatifs.py @@ -144,7 +144,6 @@ class JustificatifArchiver(BaseArchiver): Si trace == True : sauvegarde le nom du/des fichier(s) supprimé(s) dans la trace de l'étudiant """ - if str(etud.id) not in self.list_oids(etud.dept_id): raise ValueError(f"Aucune archive pour etudid[{etud.id}]")