Assiduite: diverses améliorations, styles tableaux

This commit is contained in:
Emmanuel Viennet 2023-12-07 22:10:51 +01:00
parent 04335e521a
commit a5c927db75
10 changed files with 58 additions and 35 deletions

View File

@ -1254,20 +1254,25 @@ def annee_scolaire_debut(year, month) -> int:
return int(year) - 1 return int(year) - 1
def date_debut_anne_scolaire(annee_sco: int) -> datetime: def date_debut_anne_scolaire(annee_sco: int | None = None) -> datetime.datetime:
"""La date de début de l'année scolaire """La date de début de l'année scolaire
(par défaut, le 1er aout) Si annee_sco n'est pas spécifié, année courante
(par défaut, l'année scolaire en métropole commence le 1er aout)
""" """
if annee_sco is None:
annee_sco = annee_scolaire()
return datetime.datetime(year=annee_sco, month=MONTH_DEBUT_ANNEE_SCOLAIRE, day=1) return datetime.datetime(year=annee_sco, month=MONTH_DEBUT_ANNEE_SCOLAIRE, day=1)
def date_fin_anne_scolaire(annee_sco: int) -> datetime: def date_fin_anne_scolaire(annee_sco: int | None = None) -> datetime.datetime:
"""La date de fin de l'année scolaire """La date de fin de l'année scolaire
(par défaut, le 31 juillet de l'année suivante) (par défaut, le 31 juillet de l'année suivante)
""" """
# on prend la date de début de l'année scolaire suivante, # on prend la date de début de l'année scolaire suivante,
# et on lui retre 1 jour. # et on lui retire 1 jour.
# On s'affranchit ainsi des problèmes de durées de mois. # On s'affranchit ainsi des problèmes de durées de mois.
if annee_sco is None:
annee_sco = annee_scolaire()
return datetime.datetime( return datetime.datetime(
year=annee_sco + 1, month=MONTH_DEBUT_ANNEE_SCOLAIRE, day=1 year=annee_sco + 1, month=MONTH_DEBUT_ANNEE_SCOLAIRE, day=1
) - datetime.timedelta(days=1) ) - datetime.timedelta(days=1)

View File

@ -660,3 +660,16 @@ table.liste_assi td.date {
table.liste_assi td.actions { table.liste_assi td.actions {
white-space: nowrap; /* boutons horizontalement */ white-space: nowrap; /* boutons horizontalement */
} }
tr.row-assiduite td {
border-bottom: 1px solid grey;
}
tr.row-assiduite.absent td.assi-type {
background-color: var(--color-absent-clair);
}
tr.row-assiduite.retard td.assi-type {
background-color: var(--color-retard);
}
tr.row-assiduite.present td.assi-type {
background-color: var(--color-present);
}

View File

@ -1429,7 +1429,7 @@ function generateEtudRow(
<img class="pdp" src="${pdp_url}"> <img class="pdp" src="${pdp_url}">
<a class="name_set" href="BilanEtud?etudid=${etud.id}"> <a class="name_set" href="bilan_etud?etudid=${etud.id}">
<h4 class="nom">${etud.nom}</h4> <h4 class="nom">${etud.nom}</h4>
<h5 class="prenom">${etud.prenom}</h5> <h5 class="prenom">${etud.prenom}</h5>
</a> </a>

View File

@ -54,9 +54,10 @@ class ListeAssiJusti(tb.Table):
with_foot_titles=False, with_foot_titles=False,
) )
self.ajouter_lignes() self.add_assiduites()
def ajouter_lignes(self): def add_assiduites(self):
"Ajoute le contenu de la table, avec assiduités et justificatif réunis"
# Générer les query assiduités et justificatifs # Générer les query assiduités et justificatifs
assiduites_query_etudiants: Query = None assiduites_query_etudiants: Query = None
justificatifs_query_etudiants: Query = None justificatifs_query_etudiants: Query = None
@ -215,6 +216,8 @@ class ListeAssiJusti(tb.Table):
class RowAssiJusti(tb.Row): class RowAssiJusti(tb.Row):
"Ligne de table pour une assiduité"
def __init__(self, table: ListeAssiJusti, ligne: dict): def __init__(self, table: ListeAssiJusti, ligne: dict):
self.ligne: dict = ligne self.ligne: dict = ligne
self.etud: Identite = Identite.get_etud(ligne["etudid"]) self.etud: Identite = Identite.get_etud(ligne["etudid"])
@ -259,7 +262,7 @@ class RowAssiJusti(tb.Row):
# Ajout des colonnes optionnelles # Ajout des colonnes optionnelles
self._optionnelles() self._optionnelles()
# Ajout de l'utilisateur ayant saisie l'objet # Ajout de l'utilisateur ayant saisi l'objet
self._utilisateur() self._utilisateur()
# Date de saisie # Date de saisie
@ -277,25 +280,27 @@ class RowAssiJusti(tb.Row):
obj_type: str = "" obj_type: str = ""
is_assiduite: bool = self.ligne["type"] == "assiduite" is_assiduite: bool = self.ligne["type"] == "assiduite"
if is_assiduite: if is_assiduite:
self.classes.append("row-assiduite")
self.classes.append(EtatAssiduite(self.ligne["etat"]).name.lower())
etat: str = { etat: str = {
EtatAssiduite.PRESENT: "Présence", EtatAssiduite.PRESENT: "Présence",
EtatAssiduite.ABSENT: "Absence", EtatAssiduite.ABSENT: "Absence",
EtatAssiduite.RETARD: "Retard", EtatAssiduite.RETARD: "Retard",
}.get(self.ligne["etat"]) }.get(self.ligne["etat"])
justifiee: str = "Justifiée" if self.ligne["est_just"] else "" justifiee: str = "Justifiée" if self.ligne["est_just"] else ""
obj_type = f"{etat} {justifiee}" obj_type = f"{etat} {justifiee}"
else: else:
self.classes.append("row-justificatif")
self.classes.append(EtatJustificatif(self.ligne["etat"]).name.lower())
etat: str = { etat: str = {
EtatJustificatif.VALIDE: "valide", EtatJustificatif.VALIDE: "valide",
EtatJustificatif.ATTENTE: "soumis", EtatJustificatif.ATTENTE: "soumis",
EtatJustificatif.MODIFIE: "modifié", EtatJustificatif.MODIFIE: "modifié",
EtatJustificatif.NON_VALIDE: "invalide", EtatJustificatif.NON_VALIDE: "invalide",
}.get(self.ligne["etat"]) }.get(self.ligne["etat"])
obj_type = f"Justificatif {etat}" obj_type = f"Justificatif {etat}"
self.add_cell("obj_type", "Type", obj_type) self.add_cell("obj_type", "Type", obj_type, classes=["assi-type"])
def _etud(self, lien_redirection) -> None: def _etud(self, lien_redirection) -> None:
etud = self.etud etud = self.etud

View File

@ -2,7 +2,7 @@
{% include "assiduites/widgets/tableau_base.j2" %} {% include "assiduites/widgets/tableau_base.j2" %}
<div class="pageContent"> <div class="pageContent">
<h2>Bilan de l'assiduité de <span class="rouge">{{sco.etud.nomprenom}}</span></h2> <h2>Bilan de l'assiduité de {{sco.etud.html_link_fiche()|safe}}</span></h2>
<section class="alerte invisible"> <section class="alerte invisible">
<p>Attention, cet étudiant a trop d'absences</p> <p>Attention, cet étudiant a trop d'absences</p>
@ -383,4 +383,4 @@
.suppr { .suppr {
margin: 5px 0; margin: 5px 0;
} }
</style> </style>

View File

@ -1,18 +1,18 @@
<h1>Détails {{type}} </h1> <h2>Détails {{type}}</h2>
<div id="informations"> <div id="informations">
<div class="info-row"> <div class="info-row">
<span class="info-label">Étudiant.e concerné.e:</span> <span class="etudinfo" <span class="info-label">Étudiant{{etud.e}} concerné{{etud.e}}:</span> <span class="etudinfo"
id="etudid-{{objet.etudid}}">{{objet.etud_nom}}</span> id="etudid-{{objet.etudid}}">{{etud.html_link_fiche()|safe}}</span>
</div> </div>
<div class="info-row"> <div class="info-row">
<span class="info-label">Période concernée :</span> {{objet.date_debut}} au {{objet.date_fin}} <span class="info-label">Période :</span> {{objet.date_debut}} au {{objet.date_fin}}
</div> </div>
{% if type == "Assiduité" %} {% if type == "Assiduité" %}
<div class="info-row"> <div class="info-row">
<span class="info-label">Module concernée :</span> {{objet.module}} <span class="info-label">Module :</span> {{objet.module}}
</div> </div>
{% else %} {% else %}
{% endif %} {% endif %}
@ -23,7 +23,7 @@
{% else %} {% else %}
<span class="info-label">État de l'assiduité :</span> <span class="info-label">État de l'assiduité :</span>
{% endif %} {% endif %}
{{objet.etat}} <b>{{objet.etat}}</b>
</div> </div>
@ -59,7 +59,7 @@
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
<span class="text">Non</span> <span class="text fontred">Non</span>
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
@ -103,4 +103,4 @@
<div class="info-row"> <div class="info-row">
<span>Saisie par {{objet.saisie_par}} le {{objet.entry_date}}</span> <span>Saisie par {{objet.saisie_par}} le {{objet.entry_date}}</span>
</div> </div>

View File

@ -92,7 +92,7 @@
} else if (k.indexOf('etudid') != -1) { } else if (k.indexOf('etudid') != -1) {
const e = getEtudiant(assiduite.etudid); const e = getEtudiant(assiduite.etudid);
td.innerHTML = `<a class="etudinfo" id="line-${assiduite.etudid}" href="BilanEtud?etudid=${assiduite.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`; td.innerHTML = `<a class="etudinfo" id="line-${assiduite.etudid}" href="bilan_etud?etudid=${assiduite.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`;
} else { } else {
td.textContent = assiduite[k].capitalize() td.textContent = assiduite[k].capitalize()
} }
@ -217,7 +217,7 @@
<textarea name="desc" id="desc" cols="50" rows="10" maxlength="500"></textarea> <textarea name="desc" id="desc" cols="50" rows="10" maxlength="500"></textarea>
</div> </div>
</div> </div>
` `
const el = document.createElement('div') const el = document.createElement('div')
@ -462,4 +462,4 @@
try { getAllAssiduitesFromEtud(etudid, action, true, true, assi_limit_annee) } catch (_) { } try { getAllAssiduitesFromEtud(etudid, action, true, true, assi_limit_annee) } catch (_) { }
} }
</script> </script>

View File

@ -88,7 +88,7 @@
} else if (k.indexOf('etudid') != -1) { } else if (k.indexOf('etudid') != -1) {
const e = getEtudiant(justificatif.etudid); const e = getEtudiant(justificatif.etudid);
td.innerHTML = `<a class="etudinfo" id="line-${justificatif.etudid}" href="BilanEtud?etudid=${justificatif.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`; td.innerHTML = `<a class="etudinfo" id="line-${justificatif.etudid}" href="bilan_etud?etudid=${justificatif.etudid}">${e.prenom.capitalize()} ${e.nom.toUpperCase()}</a>`;
} else if (k == "formsemestre") { } else if (k == "formsemestre") {
if (justificatif.hasOwnProperty("formsemestre")) { if (justificatif.hasOwnProperty("formsemestre")) {
td.textContent = justificatif.formsemestre.title.replaceAll('-', ' '); td.textContent = justificatif.formsemestre.title.replaceAll('-', ' ');
@ -213,8 +213,8 @@
method: "POST" method: "POST"
}) })
// This returns a promise inside of which we are checking for errors from the server. // This returns a promise inside of which we are checking for errors from the server.
// The catch promise at the end of the call does not getting called when the server returns an error. // The catch promise at the end of the call does not getting called when the server returns an error.
// More information about the error catching can be found here: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/. // More information about the error catching can be found here: https://www.tjvantoll.com/2015/09/13/fetch-and-errors/.
.then((result) => { .then((result) => {
if (!result.ok) { if (!result.ok) {
@ -228,8 +228,8 @@
return result.blob(); return result.blob();
}) })
// We use the download property for triggering the download of the file from our browser. // We use the download property for triggering the download of the file from our browser.
// More information about the following code can be found here: https://stackoverflow.com/questions/32545632/how-can-i-download-a-file-using-window-fetch. // More information about the following code can be found here: https://stackoverflow.com/questions/32545632/how-can-i-download-a-file-using-window-fetch.
// The filename from the first promise is used as name of the file. // The filename from the first promise is used as name of the file.
.then((blob) => { .then((blob) => {
if (blob != null) { if (blob != null) {
@ -676,4 +676,4 @@
align-items: center; align-items: center;
gap: 5px; gap: 5px;
} }
</style> </style>

View File

@ -524,7 +524,7 @@ def liste_assiduites_etud():
).build() ).build()
@bp.route("/BilanEtud") @bp.route("/bilan_etud")
@scodoc @scodoc
@permission_required(Permission.ScoView) @permission_required(Permission.ScoView)
def bilan_etud(): def bilan_etud():
@ -557,8 +557,8 @@ def bilan_etud():
) )
# Gestion des dates du bilan (par défaut l'année scolaire) # Gestion des dates du bilan (par défaut l'année scolaire)
date_debut: str = f"01/09/{scu.annee_scolaire()}" date_debut = scu.date_debut_anne_scolaire().strftime("%d/%m/%Y")
date_fin: str = f"30/06/{scu.annee_scolaire()+1}" date_fin: str = scu.date_fin_anne_scolaire().strftime("%d/%m/%Y")
# Récupération de la métrique d'assiduité # Récupération de la métrique d'assiduité
assi_metric = scu.translate_assiduites_metric( assi_metric = scu.translate_assiduites_metric(
@ -1251,7 +1251,7 @@ def _prepare_tableau(
) )
@bp.route("/TableauAssiduiteActions", methods=["GET", "POST"]) @bp.route("/tableau_assiduite_actions", methods=["GET", "POST"])
@scodoc @scodoc
@permission_required(Permission.AbsChange) @permission_required(Permission.AbsChange)
def tableau_assiduite_actions(): def tableau_assiduite_actions():
@ -1295,7 +1295,7 @@ def tableau_assiduite_actions():
module = _module_selector(formsemestre, module) module = _module_selector(formsemestre, module)
return render_template( return render_template(
"assiduites/pages/tableau_actions.j2", "assiduites/pages/tableau_assiduite_actions.j2",
sco=ScoData(etud=objet.etudiant), sco=ScoData(etud=objet.etudiant),
# XXX type semble être utilisé qq part, ne pas changer # XXX type semble être utilisé qq part, ne pas changer
type="Justificatif" if obj_type == "justificatif" else "Assiduité", type="Justificatif" if obj_type == "justificatif" else "Assiduité",