diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index 35fbbeea16..17a03b955a 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -896,14 +896,21 @@ def is_valid_filename(filename): BOOL_STR = { + 0: False, + 1: True, "": False, "0": False, "1": True, "f": False, "false": False, + "o": True, + "on": True, "n": False, "t": True, "true": True, + True: True, + "v": True, + "vrai": True, "y": True, } diff --git a/app/static/css/assiduites.css b/app/static/css/assiduites.css index ed5daa259f..941a655792 100644 --- a/app/static/css/assiduites.css +++ b/app/static/css/assiduites.css @@ -1,646 +1,651 @@ :root { - --color-present: #6bdb83; - --color-absent: #e62a11; - --color-absent-clair: #F25D4A; - --color-retard: #f0c865; - --color-justi: #7059FF; - --color-justi-clair: #6885E3; - --color-justi-invalide: #a84476; - --color-nonwork: #badfff; + --color-present: #6bdb83; + --color-absent: #e62a11; + --color-absent-clair: #f25d4a; + --color-retard: #f0c865; + --color-justi: #7059ff; + --color-justi-clair: #6885e3; + --color-justi-invalide: #a84476; + --color-nonwork: #badfff; - --color-absent-justi: #e65ab7; - --color-retard-justi: #ffef7a; + --color-absent-justi: #e65ab7; + --color-retard-justi: #ffef7a; - --color-error: #e62a11; - --color-warning: #eec660; - --color-information: #658ef0; + --color-error: #e62a11; + --color-warning: #eec660; + --color-information: #658ef0; - --color-def: #d61616; - --color-conflit: #ff00009c; - --color-bg-def: #c8c8c8; - --color-primary: #7059FF; - --color-secondary: #6f9fff; + --color-def: #d61616; + --color-conflit: #ff00009c; + --color-bg-def: #c8c8c8; + --color-primary: #7059ff; + --color-secondary: #6f9fff; - --color-defaut: #FFF; - --color-defaut-dark: #444; - --color-default-text: #1F1F1F; + --color-defaut: #fff; + --color-defaut-dark: #444; + --color-default-text: #1f1f1f; - - --motif-justi: repeating-linear-gradient(135deg, transparent, transparent 4px, var(--color-justi) 4px, var(--color-justi) 8px); - --motif-justi-invalide: repeating-linear-gradient(-135deg, transparent, transparent 4px, var(--color-justi-invalide) 4px, var(--color-justi-invalide) 8px); + --motif-justi: repeating-linear-gradient( + 135deg, + transparent, + transparent 4px, + var(--color-justi) 4px, + var(--color-justi) 8px + ); + --motif-justi-invalide: repeating-linear-gradient( + -135deg, + transparent, + transparent 4px, + var(--color-justi-invalide) 4px, + var(--color-justi-invalide) 8px + ); } * { - box-sizing: border-box; + box-sizing: border-box; } -.selectors>* { - margin: 10px 0; +.selectors > * { + margin: 10px 0; } .selectors:disabled { - opacity: 0.5; + opacity: 0.5; } #validate_selectors { - margin: 15px 0; + margin: 15px 0; } .no-display { - display: none !important; + display: none !important; } /* === Gestion de la timeline === */ #tl_date { - visibility: hidden; - width: 0px; - height: 0px; - position: absolute; - left: 15%; + visibility: hidden; + width: 0px; + height: 0px; + position: absolute; + left: 15%; } .infos-button { - position: relative; - display: inline-block; - vertical-align: middle; + position: relative; + display: inline-block; + vertical-align: middle; } .infos { - position: relative; - display: flex; - justify-content: start; - align-content: center; - gap: 10px; + position: relative; + display: flex; + justify-content: start; + align-content: center; + gap: 10px; } #datestr { - cursor: pointer; - background-color: white; - border: 1px #444 solid; - border-radius: 5px; - padding: 5px; - min-width: 250px; - display: inline-block; - min-height: 20px; + cursor: pointer; + background-color: white; + border: 1px #444 solid; + border-radius: 5px; + padding: 5px; + min-width: 250px; + display: inline-block; + min-height: 20px; } #tl_slider { - width: 90%; - cursor: grab; + width: 90%; + cursor: grab; - /* visibility: hidden; */ + /* visibility: hidden; */ } #datestr, #time { - width: fit-content; + width: fit-content; } .ui-slider-handle.tl_handle { - background: none; - width: 25px; - height: 25px; - visibility: visible; - background-position: top; - background-size: cover; - border: none; - top: -180%; - cursor: grab; - + background: none; + width: 25px; + height: 25px; + visibility: visible; + background-position: top; + background-size: cover; + border: none; + top: -180%; + cursor: grab; } #l_handle { - background-image: url(../icons/l_handle.svg); + background-image: url(../icons/l_handle.svg); } #r_handle { - background-image: url(../icons/r_handle.svg); + background-image: url(../icons/r_handle.svg); } .ui-slider-range.ui-widget-header.ui-corner-all { - background-color: var(--color-warning); - background-image: none; - opacity: 0.50; - visibility: visible; + background-color: var(--color-warning); + background-image: none; + opacity: 0.5; + visibility: visible; } - /* === Gestion des etuds row === */ - .etud_row { - display: grid; - grid-template-columns: 2% 20% 55% auto; - gap: 16px; - background-color: white; - border-radius: 15px; - padding: 4px 16px; - margin: 0.5% 0; - box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); - -webkit-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); - -moz-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); - max-width: 800px; + display: grid; + grid-template-columns: 2% 20% 55% auto; + gap: 16px; + background-color: white; + border-radius: 15px; + padding: 4px 16px; + margin: 0.5% 0; + box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); + -webkit-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); + -moz-box-shadow: -1px 12px 5px -8px rgba(68, 68, 68, 0.61); + max-width: 800px; } .etud_row * { - display: flex; - justify-content: center; - align-items: center; - - height: 50px; + display: flex; + justify-content: center; + align-items: center; + height: 50px; } .etud_row.def, .etud_row.dem { - background-color: var(--color-bg-def); + background-color: var(--color-bg-def); } /* --- Index --- */ .etud_row .index_field { - grid-column: 1; + grid-column: 1; } /* --- Nom étud --- */ .etud_row .name_field { - grid-column: 2; - height: 100%; - justify-content: start; + grid-column: 2; + height: 100%; + justify-content: start; } .etud_row .name_field .name_set { - flex-direction: column; - align-items: flex-start; - margin: 0 5%; + flex-direction: column; + align-items: flex-start; + margin: 0 5%; - cursor: pointer; + cursor: pointer; } .etud_row.def .nom::after, .tr.def .td.sticky span::after { - display: block; - content: " (Déf.)"; - color: var(--color-def); - margin-left: 2px; + display: block; + content: " (Déf.)"; + color: var(--color-def); + margin-left: 2px; } .etud_row.dem .nom::after, .tr.dem .td.sticky span::after { - display: block; - content: " (Dém.)"; - color: var(--color-def); - margin-left: 2px; + display: block; + content: " (Dém.)"; + color: var(--color-def); + margin-left: 2px; } .etud_row .name_field .name_set * { - padding: 0; - margin: 0; + padding: 0; + margin: 0; } .etud_row .name_field .name_set h4 { - font-size: small; - font-weight: 600; + font-size: small; + font-weight: 600; } .etud_row .name_field .name_set h5 { - font-size: x-small; + font-size: x-small; } .etud_row .pdp { - border-radius: 15px; + border-radius: 15px; } /* --- Barre assiduités --- */ .etud_row .assiduites_bar { - display: grid; - grid-template-columns: 7px 1fr; - gap: 13px; - grid-column: 3; - position: relative; + display: grid; + grid-template-columns: 7px 1fr; + gap: 13px; + grid-column: 3; + position: relative; } - - .etud_row .assiduites_bar .filler { - height: 5px; - width: 90%; + height: 5px; + width: 90%; - background-color: white; - border: 1px solid #444; + background-color: white; + border: 1px solid #444; } .etud_row .assiduites_bar #prevDateAssi { - height: 7px; - width: 7px; + height: 7px; + width: 7px; - background-color: white; - border: 1px solid #444; - margin: 0px 8px; + background-color: white; + border: 1px solid #444; + margin: 0px 8px; } .etud_row .assiduites_bar #prevDateAssi.single { - height: 9px; - width: 9px; + height: 9px; + width: 9px; } .etud_row.conflit { - background-color: var(--color-conflit); + background-color: var(--color-conflit); } .etud_row .assiduites_bar .absent, .demo.absent { - background-color: var(--color-absent) !important; + background-color: var(--color-absent) !important; } .etud_row .assiduites_bar .present, .demo.present { - background-color: var(--color-present) !important; + background-color: var(--color-present) !important; } .etud_row .assiduites_bar .retard, .demo.retard { - background-color: var(--color-retard) !important; + background-color: var(--color-retard) !important; } .demo.nonwork { - background-color: var(--color-nonwork) !important; + background-color: var(--color-nonwork) !important; } .etud_row .assiduites_bar .justified, .demo.justified { - background-image: var(--motif-justi); + background-image: var(--motif-justi); } .etud_row .assiduites_bar .invalid_justified, .demo.invalid_justified { - background-image: var(--motif-justi-invalide); + background-image: var(--motif-justi-invalide); } - /* --- Boutons assiduités --- */ .etud_row .btns_field { - grid-column: 4; + grid-column: 4; } .btns_field:disabled { - opacity: 0.7; + opacity: 0.7; } .etud_row .btns_field * { - margin: 0 5%; - cursor: pointer; - width: 35px; - height: 35px; + margin: 0 5%; + cursor: pointer; + width: 35px; + height: 35px; } .rbtn { - -webkit-appearance: none; - appearance: none; - - cursor: pointer; + -webkit-appearance: none; + appearance: none; + cursor: pointer; } .rbtn::before { - content: ""; - display: inline-block; - width: 35px; - height: 35px; - background-position: center; - background-size: cover; - border-radius: 5px; - border: 1px solid var(--color-defaut-dark); + content: ""; + display: inline-block; + width: 35px; + height: 35px; + background-position: center; + background-size: cover; + border-radius: 5px; + border: 1px solid var(--color-defaut-dark); } - .rbtn.present::before { - background-image: url(../icons/present.svg); - background-color: var(--color-present); - + background-image: url(../icons/present.svg); + background-color: var(--color-present); } .rbtn.absent::before { - background-color: var(--color-absent); - background-image: url(../icons/absent.svg); + background-color: var(--color-absent); + background-image: url(../icons/absent.svg); } .rbtn.aucun::before { - background-image: url(../icons/aucun.svg); - background-color: var(--color-defaut-dark); - + background-image: url(../icons/aucun.svg); + background-color: var(--color-defaut-dark); } .rbtn.retard::before { - background-color: var(--color-retard); - background-image: url(../icons/retard.svg); + background-color: var(--color-retard); + background-image: url(../icons/retard.svg); } .rbtn:checked:before { - outline: 5px solid var(--color-primary); - border-radius: 50%; + outline: 5px solid var(--color-primary); + border-radius: 50%; } .rbtn:focus { - outline: none !important; + outline: none !important; } /*<== Modal conflit ==>*/ .modal { - display: block; - position: fixed; - z-index: 500; - left: 0; - top: 0; - width: 100%; - height: 100%; - overflow: auto; - background-color: rgba(0, 0, 0, 0.4); + display: block; + position: fixed; + z-index: 500; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0, 0, 0, 0.4); } .modal-content { - background-color: #fefefe; - margin: 5% auto; - padding: 20px; - border: 1px solid #888; - width: 80%; - height: 320px; - position: relative; - border-radius: 10px; - + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: 80%; + height: 320px; + position: relative; + border-radius: 10px; } - .close { - color: #111; - position: absolute; - right: 5px; - top: 0px; - font-size: 28px; - font-weight: bold; - cursor: pointer; + color: #111; + position: absolute; + right: 5px; + top: 0px; + font-size: 28px; + font-weight: bold; + cursor: pointer; } /* Ajout de styles pour la frise chronologique */ .modal-timeline { - display: flex; - flex-direction: column; - align-items: stretch; - margin-bottom: 20px; + display: flex; + flex-direction: column; + align-items: stretch; + margin-bottom: 20px; } .time-labels, .assiduites-container { - display: flex; - justify-content: space-between; - position: relative; + display: flex; + justify-content: space-between; + position: relative; } .time-label { - font-size: 14px; - margin-bottom: 4px; + font-size: 14px; + margin-bottom: 4px; } .assiduite { - position: absolute; - top: 20px; - cursor: pointer; - border-radius: 4px; - z-index: 10; - height: 100px; - padding: 4px; + position: absolute; + top: 20px; + cursor: pointer; + border-radius: 4px; + z-index: 10; + height: 100px; + padding: 4px; } - .assiduite-info { - display: flex; - flex-direction: column; - height: 100%; - justify-content: space-between; + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-between; } .assiduite-id, .assiduite-period, .assiduite-state, .assiduite-user_id { - font-size: 12px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - text-align: center; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; } .assiduites-container { - min-height: 20px; - height: calc(50% - 60px); - /* Augmentation de la hauteur du conteneur des assiduités */ - position: relative; - margin-bottom: 10px; + min-height: 20px; + height: calc(50% - 60px); + /* Augmentation de la hauteur du conteneur des assiduités */ + position: relative; + margin-bottom: 10px; } - .action-buttons { - position: absolute; - text-align: center; - display: flex; - justify-content: space-evenly; - align-items: center; - height: 60px; - width: 100%; - bottom: 5%; + position: absolute; + text-align: center; + display: flex; + justify-content: space-evenly; + align-items: center; + height: 60px; + width: 100%; + bottom: 5%; } - /* Ajout de la classe CSS pour la bordure en pointillés */ .assiduite.selected { - border: 2px dashed black; + border: 2px dashed black; } .assiduite-special { - height: 120px; - position: absolute; - z-index: 5; - border: 2px solid #000; - background-color: rgba(36, 36, 36, 0.25); - background-image: repeating-linear-gradient(135deg, transparent, transparent 5px, rgba(81, 81, 81, 0.61) 5px, rgba(81, 81, 81, 0.61) 10px); - border-radius: 5px; + height: 120px; + position: absolute; + z-index: 5; + border: 2px solid #000; + background-color: rgba(36, 36, 36, 0.25); + background-image: repeating-linear-gradient( + 135deg, + transparent, + transparent 5px, + rgba(81, 81, 81, 0.61) 5px, + rgba(81, 81, 81, 0.61) 10px + ); + border-radius: 5px; } - /*<== Info sur l'assiduité sélectionnée ==>*/ .modal-assiduite-content { - background-color: #fefefe; - margin: 5% auto; - padding: 20px; - border: 1px solid #888; - width: max-content; - position: relative; - border-radius: 10px; - display: none; + background-color: #fefefe; + margin: 5% auto; + padding: 20px; + border: 1px solid #888; + width: max-content; + position: relative; + border-radius: 10px; + display: none; } - .modal-assiduite-content.show { - display: block; + display: block; } .modal-assiduite-content .infos { - display: flex; - flex-direction: column; - justify-content: space-evenly; - align-items: flex-start; + display: flex; + flex-direction: column; + justify-content: space-evenly; + align-items: flex-start; } - /*<=== Mass Action ==>*/ .mass-selection { - display: flex; - justify-content: flex-start; - align-items: center; - width: 100%; - margin: 2% 0; + display: flex; + justify-content: flex-start; + align-items: center; + width: 100%; + margin: 2% 0; } .mass-selection span { - margin: 0 1%; + margin: 0 1%; } .mass-selection .rbtn { - background-color: transparent; - cursor: pointer; + background-color: transparent; + cursor: pointer; } /*<== Loader ==> */ .loader-container { - display: none; - /* Cacher le loader par défaut */ - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - /* Fond semi-transparent pour bloquer les clics */ - z-index: 9999; - /* Placer le loader au-dessus de tout le contenu */ + display: none; + /* Cacher le loader par défaut */ + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + /* Fond semi-transparent pour bloquer les clics */ + z-index: 9999; + /* Placer le loader au-dessus de tout le contenu */ } .loader { - border: 6px solid #f3f3f3; - border-radius: 50%; - border-top: 6px solid var(--color-primary); - width: 60px; - height: 60px; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - animation: spin 2s linear infinite; + border: 6px solid #f3f3f3; + border-radius: 50%; + border-top: 6px solid var(--color-primary); + width: 60px; + height: 60px; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: spin 2s linear infinite; } @keyframes spin { - 0% { - transform: translate(-50%, -50%) rotate(0deg); - } + 0% { + transform: translate(-50%, -50%) rotate(0deg); + } - 100% { - transform: translate(-50%, -50%) rotate(360deg); - } + 100% { + transform: translate(-50%, -50%) rotate(360deg); + } } .fieldsplit { - display: flex; - justify-content: flex-start; - align-items: center; - flex-direction: column; + display: flex; + justify-content: flex-start; + align-items: center; + flex-direction: column; } .fieldsplit legend { - margin: 0; + margin: 0; } - - - #page-assiduite-content { - display: flex; - flex-wrap: wrap; - gap: 5%; - flex-direction: column; + display: flex; + flex-wrap: wrap; + gap: 5%; + flex-direction: column; } -#page-assiduite-content>* { - margin: 1.5% 0; +#page-assiduite-content > * { + margin: 1.5% 0; } .rouge { - color: var(--color-error); + color: var(--color-error); } .legende { - border: 1px dashed #333; - width: 75%; - padding: 20px; + border: 1px dashed #333; + width: 75%; + padding: 20px; } .order { - background-image: url(../icons/sort.svg); + background-image: url(../icons/sort.svg); } .filter { - background-image: url(../icons/filter.svg); + background-image: url(../icons/filter.svg); } .download { - background-image: url(../icons/download.svg); + background-image: url(../icons/download.svg); } .iconline { - display: flex; - justify-content: flex-start; - gap: min(2%, 15px); - align-items: center; + display: flex; + justify-content: flex-start; + gap: min(2%, 15px); + align-items: center; } -[name='destroyFile'] { - -webkit-appearance: none; - appearance: none; - cursor: pointer; - background-image: url(../icons/trash.svg); +[name="destroyFile"] { + -webkit-appearance: none; + appearance: none; + cursor: pointer; + background-image: url(../icons/trash.svg); } -[name='destroyFile']:checked { - background-image: url(../icons/remove_circle.svg); +[name="destroyFile"]:checked { + background-image: url(../icons/remove_circle.svg); } .icon { - display: block; - width: 24px; - height: 24px; - outline: none !important; - border: none !important; - cursor: pointer; - margin: 0 2px !important; + display: block; + width: 24px; + height: 24px; + outline: none !important; + border: none !important; + cursor: pointer; + margin: 0 2px !important; } .icon:focus { - outline: none; - border: none; + outline: none; + border: none; } #forcemodule { - border-radius: 8px; - background: var(--color-error); - max-width: fit-content; - padding: 5px; - color: white; + border-radius: 8px; + background: var(--color-error); + max-width: fit-content; + padding: 5px; + color: white; } .demo { - width: 23px; - height: 13px; - display: inline-block; - border: solid 1px #333; -} \ No newline at end of file + width: 23px; + height: 13px; + display: inline-block; + border: solid 1px #333; +} + +.assi-liste { + border: 1px solid gray; + border-radius: 12px; + padding: 12px; +} +#options-tableau label { + font-weight: normal; + margin-right: 12px; +} diff --git a/app/tables/liste_assiduites.py b/app/tables/liste_assiduites.py index 6a61de53cb..756c12fde5 100644 --- a/app/tables/liste_assiduites.py +++ b/app/tables/liste_assiduites.py @@ -1,13 +1,14 @@ -from app.tables import table_builder as tb -from app.models import Identite, Assiduite, Justificatif -from app.auth.models import User from datetime import datetime -from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif -from flask_sqlalchemy.query import Query, Pagination -from sqlalchemy import union, literal, select, desc -from app import db, g + from flask import url_for -from app import log +from flask_sqlalchemy.query import Pagination, Query +from sqlalchemy import desc, literal, union + +from app import db, g +from app.auth.models import User +from app.models import Assiduite, Identite, Justificatif +from app.scodoc.sco_utils import EtatAssiduite, EtatJustificatif, to_bool +from app.tables import table_builder as tb class ListeAssiJusti(tb.Table): @@ -21,9 +22,9 @@ class ListeAssiJusti(tb.Table): def __init__( self, - table_data: "Data", - filtre: "Filtre" = None, - options: "Options" = None, + table_data: "AssiJustifData", + filtre: "AssiFiltre" = None, + options: "AssiDisplayOptions" = None, **kwargs, ) -> None: """ @@ -33,12 +34,12 @@ class ListeAssiJusti(tb.Table): filtre (Filtre, optional): Filtrage des objets à afficher. Defaults to None. page (int, optional): numéro de page de la pagination. Defaults to 1. """ - self.table_data: "Data" = table_data + self.table_data: "AssiJustifData" = table_data # Gestion du filtre, par défaut un filtre vide - self.filtre = filtre if filtre is not None else Filtre() + self.filtre = filtre if filtre is not None else AssiFiltre() # Gestion des options, par défaut un objet Options vide - self.options = options if options is not None else Options() + self.options = options if options is not None else AssiDisplayOptions() self.total_page: int = None @@ -383,7 +384,7 @@ class RowAssiJusti(tb.Row): self.add_cell("actions", "Actions", " ".join(html), no_excel=True) -class Filtre: +class AssiFiltre: """ Classe représentant le filtrage qui sera appliqué aux objets du Tableau `ListeAssiJusti` @@ -475,8 +476,8 @@ class Filtre: return self.filtres.get("type_obj", 0) -class Options: - VRAI = ["on", "true", "t", "v", "vrai", True, 1] +class AssiDisplayOptions: + "Options pour affichage tableau" def __init__( self, @@ -494,17 +495,18 @@ class Options: if self.nb_ligne_page is not None: self.nb_ligne_page = min(nb_ligne_page, ListeAssiJusti.MAX_PAR_PAGE) - self.show_pres: bool = show_pres in Options.VRAI - self.show_reta: bool = show_reta in Options.VRAI - self.show_desc: bool = show_desc in Options.VRAI - self.show_etu: bool = show_etu in Options.VRAI - self.show_actions: bool = show_actions in Options.VRAI - self.show_module: bool = show_module in Options.VRAI + self.show_pres = to_bool(show_pres) + self.show_reta = to_bool(show_reta) + self.show_desc = to_bool(show_desc) + self.show_etu = to_bool(show_etu) + self.show_actions = to_bool(show_actions) + self.show_module = to_bool(show_module) def remplacer(self, **kwargs): + "Positionnne options booléennes selon arguments" for k, v in kwargs.items(): if k.startswith("show_"): - setattr(self, k, v in Options.VRAI) + setattr(self, k, to_bool(v)) elif k in ["page", "nb_ligne_page"]: setattr(self, k, int(v)) if k == "nb_ligne_page": @@ -513,7 +515,9 @@ class Options: ) -class Data: +class AssiJustifData: + "Les assiduités et justificatifs" + def __init__( self, assiduites_query: Query = None, justificatifs_query: Query = None ): @@ -521,8 +525,8 @@ class Data: self.justificatifs_query: Query = justificatifs_query @staticmethod - def from_etudiants(*etudiants: Identite) -> "Data": - data = Data() + def from_etudiants(*etudiants: Identite) -> "AssiJustifData": + data = AssiJustifData() data.assiduites_query = Assiduite.query.filter( Assiduite.etudid.in_([e.etudid for e in etudiants]) ) diff --git a/app/templates/assiduites/pages/ajout_assiduites.j2 b/app/templates/assiduites/pages/ajout_assiduites.j2 index 49ac678e05..a2d92c563d 100644 --- a/app/templates/assiduites/pages/ajout_assiduites.j2 +++ b/app/templates/assiduites/pages/ajout_assiduites.j2 @@ -63,7 +63,7 @@ - + {{tableau | safe }} diff --git a/app/templates/assiduites/pages/ajout_justificatif.j2 b/app/templates/assiduites/pages/ajout_justificatif.j2 index c295fc9ec8..37b5e4eaed 100644 --- a/app/templates/assiduites/pages/ajout_justificatif.j2 +++ b/app/templates/assiduites/pages/ajout_justificatif.j2 @@ -55,7 +55,7 @@ - + {{tableau | safe }} @@ -242,4 +242,4 @@ document.getElementById("justi_date_fin").valueAsObject = { time: assi_evening } } -{% endblock pageContent %} \ No newline at end of file +{% endblock pageContent %} diff --git a/app/templates/assiduites/widgets/tableau.j2 b/app/templates/assiduites/widgets/tableau.j2 index 851eae4529..0f8f6c718e 100644 --- a/app/templates/assiduites/widgets/tableau.j2 +++ b/app/templates/assiduites/widgets/tableau.j2 @@ -1,50 +1,41 @@ - - Options + Évènements enregistrés pour cet étudiant {% if afficher_options != false %} - afficher les présences - {% if options.show_pres %} - - {% else %} - - {% endif %} + + afficher les présences - afficher les retards - {% if options.show_reta %} - - {% else %} - + + afficher les retards + + + afficher les descriptions + + {{scu.ICON_XLS|safe}} + {% endif %} - afficher les descriptions - {% if options.show_desc %} - - {% else %} - - {% endif %} - - {% endif %} - Nombre de ligne par page : - + Nombre de lignes par page : + Page n° {% for n in range(1,total_pages+1) %} - {% if n == options.page %} - {{n}} - {% else %} - {{n}} - {% endif %} + {{n}} {% endfor %} - valider - {{scu.ICON_XLS|safe}} + -{{tableau | safe}} +{{table.html() | safe}} @@ -70,4 +64,4 @@ .small-font { font-size: 9pt; } - \ No newline at end of file + diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 988f36593b..b08c2ebf6b 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -28,7 +28,7 @@ import datetime import re from flask import g, request, render_template, flash -from flask import abort, url_for, redirect +from flask import abort, url_for, redirect, Response from flask_login import current_user from app import db @@ -255,19 +255,17 @@ def signal_assiduites_etud(): Args: etudid (int): l'identifiant de l'étudiant - + date_deb, date_fin: heures début et fin (ISO sans timezone) + moduleimpl_id + evaluation_id + saisie_eval : si présent, mode "évaluation" Returns: str: l'html généré """ - - # Récupération de l'étudiant concerné etudid = request.args.get("etudid", -1) - etud: Identite = Identite.query.get_or_404(etudid) - if etud.dept_id != g.scodoc_dept_id: - abort(404, "étudiant inexistant dans ce département") - - # gestion évaluations (Appel à la page depuis les évaluations) + etud = Identite.get_etud(etudid) + # Gestion évaluations (appel à la page depuis les évaluations) saisie_eval: bool = request.args.get("saisie_eval") is not None date_deb: str = request.args.get("date_deb") date_fin: str = request.args.get("date_fin") @@ -298,17 +296,17 @@ def signal_assiduites_etud(): ], ) - tableau = _preparer_tableau( - liste_assi.Data.from_etudiants( + is_html, tableau = _prepare_tableau( + liste_assi.AssiJustifData.from_etudiants( etud, ), filename=f"assiduite-{etudid}", afficher_etu=False, - filtre=liste_assi.Filtre(type_obj=1), - options=liste_assi.Options(show_module=True), + filtre=liste_assi.AssiFiltre(type_obj=1), + options=liste_assi.AssiDisplayOptions(show_module=True), ) - if not tableau[0]: - return tableau[1] + if not is_html: + return tableau # Génération de la page return HTMLBuilder( header, @@ -328,7 +326,7 @@ def signal_assiduites_etud(): etud=etud, redirect_url=redirect_url, moduleimpl_id=moduleimpl_id, - tableau=tableau[1], + tableau=tableau, ), # render_template( # "assiduites/pages/signal_assiduites_etud.j2", @@ -390,14 +388,14 @@ def liste_assiduites_etud(): "css/assiduites.css", ], ) - tableau = _preparer_tableau( - liste_assi.Data.from_etudiants( + tableau = _prepare_tableau( + liste_assi.AssiJustifData.from_etudiants( etud, ), filename=f"assiduites-justificatifs-{etudid}", afficher_etu=False, - filtre=liste_assi.Filtre(type_obj=0), - options=liste_assi.Options(show_module=True), + filtre=liste_assi.AssiFiltre(type_obj=0), + options=liste_assi.AssiDisplayOptions(show_module=True), ) if not tableau[0]: return tableau[1] @@ -505,14 +503,14 @@ def ajout_justificatif_etud(): ], ) - tableau = _preparer_tableau( - liste_assi.Data.from_etudiants( + tableau = _prepare_tableau( + liste_assi.AssiJustifData.from_etudiants( etud, ), filename=f"justificatifs-{etudid}", afficher_etu=False, - filtre=liste_assi.Filtre(type_obj=2), - options=liste_assi.Options(show_module=False, show_desc=True), + filtre=liste_assi.AssiFiltre(type_obj=2), + options=liste_assi.AssiDisplayOptions(show_module=False, show_desc=True), afficher_options=False, ) if not tableau[0]: @@ -1062,30 +1060,25 @@ def visu_assi_group(): ) -def _preparer_tableau( - data: liste_assi.Data, +def _prepare_tableau( + data: liste_assi.AssiJustifData, filename: str = "tableau-assiduites", afficher_etu: bool = True, - filtre: liste_assi.Filtre = None, - options: liste_assi.Options = None, + filtre: liste_assi.AssiFiltre = None, + options: liste_assi.AssiDisplayOptions = None, afficher_options: bool = True, -) -> tuple[bool, "Response"]: +) -> tuple[bool, Response | str]: """ - _preparer_tableau prépare un tableau d'assiduités / justificatifs + Prépare un tableau d'assiduités / justificatifs - Cette fontion récupère dans la requête les arguments : - - valeurs possibles des booléens vrais ["on", "true", "t", "v", "vrai", True, 1] - toute autre valeur est considérée comme fausse. + Cette fonction récupère dans la requête les arguments : show_pres : bool -> Affiche les présences, par défaut False show_reta : bool -> Affiche les retard, par défaut False show_desc : bool -> Affiche les descriptions, par défaut False - - Returns: - tuple[bool | "Reponse" ]: + tuple[bool | Reponse|str ]: - bool : Vrai si la réponse est du Text/HTML - Reponse : du Text/HTML ou Une Reponse (téléchargement fichier) """ @@ -1111,7 +1104,7 @@ def _preparer_tableau( fmt = request.args.get("fmt", "html") if options is None: - options: liste_assi.Options = liste_assi.Options() + options: liste_assi.AssiDisplayOptions = liste_assi.AssiDisplayOptions() options.remplacer( page=page_number, @@ -1138,7 +1131,7 @@ def _preparer_tableau( return True, render_template( "assiduites/widgets/tableau.j2", - tableau=table.html(), + table=table, total_pages=table.total_pages, options=options, afficher_options=afficher_options, @@ -1509,11 +1502,7 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None): Alors l'absence sera sur la période de l'évaluation Sinon L'utilisateur sera redirigé vers la page de saisie des absences de l'étudiant """ - - # Récupération de l'étudiant concerné - etud: Identite = Identite.query.get_or_404(etudid) - if etud.dept_id != g.scodoc_dept_id: - abort(404, "étudiant inexistant dans ce département") + etud = Identite.get_etud(etudid) # Récupération de l'évaluation concernée evaluation: Evaluation = Evaluation.query.get_or_404(evaluation_id) @@ -1549,7 +1538,9 @@ def signal_evaluation_abs(etudid: int = None, evaluation_id: int = None): # En cas d'erreur msg: str = see.args[0] if "Duplication" in msg: - msg = "Une autre assiduité concerne déjà cette période. En cliquant sur continuer vous serez redirigé vers la page de saisie des assiduités de l'étudiant." + msg = """Une autre saisie concerne déjà cette période. + En cliquant sur continuer vous serez redirigé vers la page de + saisie des assiduités de l'étudiant.""" dest: str = url_for( "assiduites.signal_assiduites_etud", etudid=etudid,