Assiduités: ajout logs, style sur etuds dem.

This commit is contained in:
Emmanuel Viennet 2023-07-26 13:27:57 +02:00
parent 70cda5a553
commit 740749e37e
7 changed files with 83 additions and 100 deletions

View File

@ -6,18 +6,18 @@
"""ScoDoc 9 API : Assiduités """ScoDoc 9 API : Assiduités
""" """
from datetime import datetime 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_assiduites as scass
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app import db
from app.api import api_bp as bp from app.api import api_bp as bp
from app.api import api_web_bp from app.api import api_web_bp, get_model_api_object
from app.api import get_model_api_object
from app.decorators import permission_required, scodoc 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_exceptions import ScoValueError
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_utils import json_error 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() assiduite_unique: Assiduite = Assiduite.query.filter_by(id=assiduite_id).first()
if assiduite_unique is None: if assiduite_unique is None:
return (404, "Assiduite non existante") 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) database.session.delete(assiduite_unique)
scass.simple_invalidate_cache(ass_dict)
return (200, "OK") return (200, "OK")
@ -630,6 +637,12 @@ def assiduite_edit(assiduite_id: int):
err: str = ", ".join(errors) err: str = ", ".join(errors)
return json_error(404, err) 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.add(assiduite_unique)
db.session.commit() db.session.commit()
scass.simple_invalidate_cache(assiduite_unique.to_dict()) scass.simple_invalidate_cache(assiduite_unique.to_dict())
@ -645,14 +658,17 @@ def assiduite_edit(assiduite_id: int):
@permission_required(Permission.ScoAbsChange) @permission_required(Permission.ScoAbsChange)
def assiduites_edit(): 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": La requête doit avoir un content type "application/json":
{ [
"etat"?: str, {
"moduleimpl_id"?: int "assiduite_id" : int,
"desc"?: str "etat"?: str,
"est_just"?: bool "moduleimpl_id"?: int
} "desc"?: str
"est_just"?: bool
}
]
""" """
edit_list: list[object] = request.get_json(force=True) edit_list: list[object] = request.get_json(force=True)
@ -664,7 +680,7 @@ def assiduites_edit():
for i, data in enumerate(edit_list): for i, data in enumerate(edit_list):
assi: Identite = Assiduite.query.filter_by(id=data["assiduite_id"]).first() assi: Identite = Assiduite.query.filter_by(id=data["assiduite_id"]).first()
if assi is None: if assi is None:
errors[i] = "Cet assiduité n'existe pas." errors[i] = f"assiduité {data['assiduite_id']} n'existe pas."
continue continue
code, obj = _edit_singular(assi, data) code, obj = _edit_singular(assi, data)
@ -727,6 +743,12 @@ def _edit_singular(assiduite_unique, data):
err: str = ", ".join(errors) err: str = ", ".join(errors)
return (404, err) 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) db.session.add(assiduite_unique)
scass.simple_invalidate_cache(assiduite_unique.to_dict()) scass.simple_invalidate_cache(assiduite_unique.to_dict())

View File

@ -3,16 +3,10 @@
""" """
from datetime import datetime from datetime import datetime
from app import db from app import db, log
from app.models import ModuleImpl from app.models import ModuleImpl, Scolog
from app.models.etudiants import Identite from app.models.etudiants import Identite
from app.auth.models import User 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_exceptions import ScoValueError
from app.scodoc.sco_utils import ( from app.scodoc.sco_utils import (
EtatAssiduite, EtatAssiduite,
@ -92,6 +86,20 @@ class Assiduite(db.Model):
} }
return data 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 @classmethod
def create_assiduite( def create_assiduite(
cls, cls,
@ -140,33 +148,12 @@ class Assiduite(db.Model):
est_just=est_just, est_just=est_just,
) )
return nouv_assiduite log(f"create_assiduite: {nouv_assiduite}")
Scolog.logdb(
@classmethod method="create_assiduite",
def fast_create_assiduite( etudid=etud.id,
cls, msg=f"assiduité: {nouv_assiduite}",
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,
) )
return nouv_assiduite return nouv_assiduite
@ -266,29 +253,6 @@ class Justificatif(db.Model):
) )
return nouv_justificatif 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( def is_period_conflicting(
date_debut: datetime, date_debut: datetime,

View File

@ -93,7 +93,7 @@ _formsemestreEditor = ndb.EditableTable(
) )
def get_formsemestre(formsemestre_id: int): def get_formsemestre(formsemestre_id: int) -> dict:
"list ONE formsemestre" "list ONE formsemestre"
if formsemestre_id is None: if formsemestre_id is None:
raise ValueError("get_formsemestre: id manquant") raise ValueError("get_formsemestre: id manquant")

View File

@ -1608,12 +1608,12 @@ function deleteJustificatif(justif_id) {
function errorAlert() { function errorAlert() {
const html = ` const html = `
<h3>Avez vous les droits suffisant pour cette action ?</h3> <h3>Avez vous les droits suffisant pour cette action ?</h3>
<p>Si c'est bien le cas : veuillez de l'aide sur le canal Assistance de ScoDoc</p> <p>Si c'est bien le cas : demandez de l'aide sur le canal Assistance de ScoDoc</p>
<br> <br>
<p><i>pour les développeurs : l'erreur est affichée dans la console JS</i></p> <p><i>pour les développeurs : l'erreur est affichée dans la console JS</i></p>
`; `;
const div = document.createElement("div"); const div = document.createElement("div");
div.innerHTML = html; div.innerHTML = html;
openAlertModal("Une erreur s'est déclanchée", div); openAlertModal("Une erreur s'est produite", div);
} }

View File

@ -8,10 +8,12 @@
""" """
from flask import g, url_for 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 from app.tables import table_builder as tb
import app.scodoc.sco_assiduites as scass import app.scodoc.sco_assiduites as scass
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_utils as scu
class TableAssi(tb.Table): class TableAssi(tb.Table):
@ -23,12 +25,13 @@ class TableAssi(tb.Table):
self, self,
etuds: list[Identite] = None, etuds: list[Identite] = None,
dates: tuple[str, str] = None, dates: tuple[str, str] = None,
formsemestre: FormSemestre = None,
**kwargs, **kwargs,
): ):
self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows self.rows: list["RowEtud"] = [] # juste pour que VSCode nous aide sur .rows
classes = ["gt_table", "gt_left"] classes = ["gt_table", "gt_left"]
self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"] self.dates = [str(dates[0]) + "T00:00", str(dates[1]) + "T23:59"]
self.formsemestre = formsemestre
super().__init__( super().__init__(
row_class=RowAssi, row_class=RowAssi,
classes=classes, classes=classes,
@ -50,6 +53,17 @@ class RowAssi(tb.Row):
# pour le moment très simple, extensible (codes, liens bulletins, ...) # pour le moment très simple, extensible (codes, liens bulletins, ...)
def __init__(self, table: TableAssi, etud: Identite, *args, **kwargs): 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) super().__init__(table, etud.id, *args, **kwargs)
self.etud = etud self.etud = etud
self.dates = table.dates self.dates = table.dates

View File

@ -4,6 +4,7 @@ from flask import g, request, render_template
from flask import abort, url_for from flask import abort, url_for
from app import db
from app.comp import res_sem from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.decorators import ( from app.decorators import (
@ -669,9 +670,11 @@ def visu_assi_group():
map(str, group_ids) map(str, group_ids)
groups_infos = sco_groups_view.DisplayedGroupsInfos(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]) 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"): if fmt.startswith("xls"):
return scu.send_file( return scu.send_file(

View File

@ -121,15 +121,6 @@ class _Merger:
"entry_date": self.entry_date, "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): def _to_assi(self):
date_deb = _Merger._tuple_to_date(self.deb) 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): def export(self):
"""Génère un nouvel objet Assiduité ou Justificatif""" """Génère un nouvel objet Assiduité ou Justificatif"""
obj: Assiduite or Justificatif = None obj: Assiduite or Justificatif = None