diff --git a/app/api/assiduites.py b/app/api/assiduites.py index c40273ad..0e3fc6e5 100644 --- a/app/api/assiduites.py +++ b/app/api/assiduites.py @@ -6,18 +6,18 @@ """ScoDoc 9 API : Assiduités """ from datetime import datetime -from flask_json import as_json -from flask import g, request -from flask_login import login_required, current_user +from flask import g, request +from flask_json import as_json +from flask_login import current_user, login_required + +from app import db, log import app.scodoc.sco_assiduites as scass import app.scodoc.sco_utils as scu -from app import db from app.api import api_bp as bp -from app.api import api_web_bp -from app.api import get_model_api_object +from app.api import api_web_bp, get_model_api_object from app.decorators import permission_required, scodoc -from app.models import Assiduite, FormSemestre, Identite, ModuleImpl +from app.models import Assiduite, FormSemestre, Identite, ModuleImpl, Scolog from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_permissions import Permission from app.scodoc.sco_utils import json_error @@ -556,8 +556,15 @@ def _delete_singular(assiduite_id: int, database): assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first() if assiduite_unique is None: return (404, "Assiduite non existante") - scass.simple_invalidate_cache(assiduite_unique.to_dict()) + ass_dict = assiduite_unique.to_dict() + log(f"delete_assiduite: {assiduite_unique}") + Scolog.logdb( + method="delete_assiduite", + etudid=assiduite_unique.etudiant.id, + msg=f"assiduité: {assiduite_unique}", + ) database.session.delete(assiduite_unique) + scass.simple_invalidate_cache(ass_dict) return (200, "OK") @@ -630,6 +637,12 @@ def assiduite_edit(assiduite_id: int): err: str = ", ".join(errors) return json_error(404, err) + log(f"assiduite_edit: {assiduite_unique}") + Scolog.logdb( + "assiduite_edit", + 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()) @@ -645,14 +658,17 @@ def assiduite_edit(assiduite_id: int): @permission_required(Permission.ScoAbsChange) def assiduites_edit(): """ - Edition d'une assiduité à partir de son id + Edition de plusieurs assiduités La requête doit avoir un content type "application/json": - { - "etat"?: str, - "moduleimpl_id"?: int - "desc"?: str - "est_just"?: bool - } + [ + { + "assiduite_id" : int, + "etat"?: str, + "moduleimpl_id"?: int + "desc"?: str + "est_just"?: bool + } + ] """ edit_list: list[object] = request.get_json(force=True) @@ -664,7 +680,7 @@ def assiduites_edit(): for i, data in enumerate(edit_list): assi: Identite = Assiduite.query.filter_by(id=data["assiduite_id"]).first() if assi is None: - errors[i] = "Cet assiduité n'existe pas." + errors[i] = f"assiduité {data['assiduite_id']} n'existe pas." continue code, obj = _edit_singular(assi, data) @@ -727,6 +743,12 @@ def _edit_singular(assiduite_unique, data): err: str = ", ".join(errors) return (404, err) + log(f"_edit_singular: {assiduite_unique}") + Scolog.logdb( + "assiduite_edit", + assiduite_unique.etudiant.id, + msg=f"assiduite: modif {assiduite_unique}", + ) db.session.add(assiduite_unique) scass.simple_invalidate_cache(assiduite_unique.to_dict()) diff --git a/app/models/assiduites.py b/app/models/assiduites.py index 3b467904..dd1910f3 100644 --- a/app/models/assiduites.py +++ b/app/models/assiduites.py @@ -3,16 +3,10 @@ """ from datetime import datetime -from app import db -from app.models import ModuleImpl +from app import db, log +from app.models import ModuleImpl, Scolog from app.models.etudiants import Identite from app.auth.models import User -from app.scodoc.sco_utils import ( - EtatAssiduite, - EtatJustificatif, - localize_datetime, - is_period_overlapping, -) from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.sco_utils import ( EtatAssiduite, @@ -92,6 +86,20 @@ class Assiduite(db.Model): } return data + def __str__(self) -> str: + "chaine pour journaux et debug (lisible par humain français)" + try: + etat_str = EtatAssiduite(self.etat).name.lower().capitalize() + except ValueError: + etat_str = "Invalide" + return f"""{etat_str} { + "just." if self.est_just else "non just." + } de { + self.date_debut.strftime("%d/%m/%Y %Hh%M") + } à { + self.date_fin.strftime("%d/%m/%Y %Hh%M") + }""" + @classmethod def create_assiduite( cls, @@ -140,33 +148,12 @@ class Assiduite(db.Model): est_just=est_just, ) - return nouv_assiduite - - @classmethod - def fast_create_assiduite( - cls, - etudid: int, - date_debut: datetime, - date_fin: datetime, - etat: EtatAssiduite, - moduleimpl_id: int = None, - description: str = None, - entry_date: datetime = None, - est_just: bool = False, - ) -> object or int: - """Créer une nouvelle assiduité pour l'étudiant""" - # Vérification de non duplication des périodes - nouv_assiduite = Assiduite( - date_debut=date_debut, - date_fin=date_fin, - etat=etat, - etudid=etudid, - moduleimpl_id=moduleimpl_id, - description=description, - entry_date=entry_date, - est_just=est_just, + log(f"create_assiduite: {nouv_assiduite}") + Scolog.logdb( + method="create_assiduite", + etudid=etud.id, + msg=f"assiduité: {nouv_assiduite}", ) - return nouv_assiduite @@ -266,29 +253,6 @@ class Justificatif(db.Model): ) return nouv_justificatif - @classmethod - def fast_create_justificatif( - cls, - etudid: int, - date_debut: datetime, - date_fin: datetime, - etat: EtatJustificatif, - raison: str = None, - entry_date: datetime = None, - ) -> object or int: - """Créer un nouveau justificatif pour l'étudiant""" - - nouv_justificatif = Justificatif( - date_debut=date_debut, - date_fin=date_fin, - etat=etat, - etudid=etudid, - raison=raison, - entry_date=entry_date, - ) - - return nouv_justificatif - def is_period_conflicting( date_debut: datetime, diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index bac3352a..40a34a2a 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -93,7 +93,7 @@ _formsemestreEditor = ndb.EditableTable( ) -def get_formsemestre(formsemestre_id: int): +def get_formsemestre(formsemestre_id: int) -> dict: "list ONE formsemestre" if formsemestre_id is None: raise ValueError("get_formsemestre: id manquant") diff --git a/app/static/js/assiduites.js b/app/static/js/assiduites.js index c5b7ac9b..c506e345 100644 --- a/app/static/js/assiduites.js +++ b/app/static/js/assiduites.js @@ -1608,12 +1608,12 @@ function deleteJustificatif(justif_id) { function errorAlert() { const html = `

Avez vous les droits suffisant pour cette action ?

-

Si c'est bien le cas : veuillez de l'aide sur le canal Assistance de ScoDoc

+

Si c'est bien le cas : demandez de l'aide sur le canal Assistance de ScoDoc


pour les développeurs : l'erreur est affichée dans la console JS

`; const div = document.createElement("div"); div.innerHTML = html; - openAlertModal("Une erreur s'est déclanchée", div); + openAlertModal("Une erreur s'est produite", div); } diff --git a/app/tables/visu_assiduites.py b/app/tables/visu_assiduites.py index 9522341b..304e827f 100644 --- a/app/tables/visu_assiduites.py +++ b/app/tables/visu_assiduites.py @@ -8,10 +8,12 @@ """ from flask import g, url_for -from app.models import Identite, Justificatif +from app import log +from app.models import FormSemestre, Identite, Justificatif from app.tables import table_builder as tb import app.scodoc.sco_assiduites as scass from app.scodoc import sco_preferences +from app.scodoc import sco_utils as scu class TableAssi(tb.Table): @@ -23,12 +25,13 @@ class TableAssi(tb.Table): self, etuds: list[Identite] = None, dates: tuple[str, str] = None, + formsemestre: FormSemestre = None, **kwargs, ): self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows classes = ["gt_table", "gt_left"] self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"] - + self.formsemestre = formsemestre super().__init__( row_class=RowAssi, classes=classes, @@ -50,6 +53,17 @@ class RowAssi(tb.Row): # pour le moment très simple, extensible (codes, liens bulletins, ...) def __init__(self, table: TableAssi, etud: Identite, *args, **kwargs): + # Etat de l'inscription au formsemestre + if "classes" not in kwargs: + kwargs["classes"] = [] + try: + inscription = table.formsemestre.etuds_inscriptions[etud.id] + if inscription.etat == scu.DEMISSION: + kwargs["classes"].append("etuddem") + except KeyError: + log(f"RowAssi: etudid {etud.id} non inscrit à {table.formsemestre.id}") + kwargs["classes"].append("non_inscrit") # ne devrait pas arriver ! + super().__init__(table, etud.id, *args, **kwargs) self.etud = etud self.dates = table.dates diff --git a/app/views/assiduites.py b/app/views/assiduites.py index 77bcc938..a42b5f05 100644 --- a/app/views/assiduites.py +++ b/app/views/assiduites.py @@ -4,6 +4,7 @@ from flask import g, request, render_template from flask import abort, url_for +from app import db from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.decorators import ( @@ -669,9 +670,11 @@ def visu_assi_group(): map(str, group_ids) groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids) - + formsemestre = db.session.get(FormSemestre, groups_infos.formsemestre_id) etuds = etuds_sorted_from_ids([m["etudid"] for m in groups_infos.members]) - table: TableAssi = TableAssi(etuds=etuds, dates=list(dates.values())) + table: TableAssi = TableAssi( + etuds=etuds, dates=list(dates.values()), formsemestre=formsemestre + ) if fmt.startswith("xls"): return scu.send_file( diff --git a/tools/migrate_abs_to_assiduites.py b/tools/migrate_abs_to_assiduites.py index 75f5ac02..d4eae657 100644 --- a/tools/migrate_abs_to_assiduites.py +++ b/tools/migrate_abs_to_assiduites.py @@ -121,15 +121,6 @@ class _Merger: "entry_date": self.entry_date, }, ) - # retour = Justificatif.fast_create_justificatif( - # etudid=self.etudid, - # date_debut=date_deb, - # date_fin=date_fin, - # etat=EtatJustificatif.VALIDE, - # raison=self.raison, - # entry_date=self.entry_date, - # ) - # return retour def _to_assi(self): date_deb = _Merger._tuple_to_date(self.deb) @@ -161,17 +152,6 @@ class _Merger: }, ) - # retour = Assiduite.fast_create_assiduite( - # etudid=self.etudid, - # date_debut=date_deb, - # date_fin=date_fin, - # etat=EtatAssiduite.ABSENT, - # moduleimpl_id=self.moduleimpl, - # description=self.raison, - # entry_date=self.entry_date, - # ) - # return retour - def export(self): """Génère un nouvel objet Assiduité ou Justificatif""" obj: Assiduite or Justificatif = None