Billets absences: modernisation

This commit is contained in:
Emmanuel Viennet 2022-09-11 09:35:47 +02:00
parent f31ccbee9a
commit f5ee63dd5c
3 changed files with 104 additions and 68 deletions

View File

@ -101,6 +101,7 @@ def sidebar(etudid: int = None):
etudid = request.form.get("etudid", None) etudid = request.form.get("etudid", None)
if etudid is not None: if etudid is not None:
etudi = int(etudid)
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0] etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
params.update(etud) params.update(etud)
params["fiche_url"] = url_for( params["fiche_url"] = url_for(

View File

@ -39,7 +39,7 @@ from app.scodoc import sco_preferences
def query_billets_etud( def query_billets_etud(
etudid: int = None, etat: bool = None etudid: int = None, etat: bool = None
) -> flask_sqlalchemy.BaseQuery: ) -> flask_sqlalchemy.BaseQuery:
"""Billets d'absences pour un étudiant. """Billets d'absences pour un étudiant, ou tous si etudid is None.
Si etat, filtre par état. Si etat, filtre par état.
Si dans un département et que la gestion des billets n'a pas été activée Si dans un département et que la gestion des billets n'a pas été activée
dans ses préférences, table toujours vide. dans ses préférences, table toujours vide.
@ -48,9 +48,11 @@ def query_billets_etud(
"handle_billets_abs" "handle_billets_abs"
): ):
return [] return []
billets = BilletAbsence.query.filter_by(etudid=etudid) billets = BilletAbsence.query
if etudid is not None:
billets = billets.filter_by(etudid=etudid)
if etat is not None: if etat is not None:
billets = billets.query.filter_by(etat=False) billets = billets.filter_by(etat=False)
if g.scodoc_dept is not None: if g.scodoc_dept is not None:
# jointure avec departement de l'étudiant # jointure avec departement de l'étudiant
billets = billets.join(BilletAbsence.etudiant).filter_by( billets = billets.join(BilletAbsence.etudiant).filter_by(

View File

@ -57,7 +57,7 @@ from xml.etree import ElementTree
import flask import flask
from flask import g, request from flask import g, request
from flask import url_for from flask import abort, flash, url_for
from flask_login import current_user from flask_login import current_user
from app import db, log from app import db, log
@ -70,7 +70,7 @@ from app.decorators import (
permission_required, permission_required,
permission_required_compat_scodoc7, permission_required_compat_scodoc7,
) )
from app.models import FormSemestre, GroupDescr from app.models import FormSemestre, GroupDescr, Partition
from app.models.absences import BilletAbsence from app.models.absences import BilletAbsence
from app.models.etudiants import Identite from app.models.etudiants import Identite
from app.views import absences_bp as bp from app.views import absences_bp as bp
@ -162,7 +162,13 @@ def index_html():
@scodoc7func @scodoc7func
def choix_semaine(group_id): def choix_semaine(group_id):
"""Page choix semaine sur calendrier pour saisie absences d'un groupe""" """Page choix semaine sur calendrier pour saisie absences d'un groupe"""
group = GroupDescr.query.get_or_404(group_id) group = (
GroupDescr.query.filter_by(id=group_id)
.join(Partition)
.join(FormSemestre)
.filter_by(dept_id=g.scodoc_dept_id)
.first_or_404()
)
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
page_title="Saisie des absences", page_title="Saisie des absences",
@ -357,6 +363,9 @@ def SignaleAbsenceGrHebdo(
) )
formsemestre_id = groups_infos.formsemestre_id formsemestre_id = groups_infos.formsemestre_id
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if formsemestre.dept_id != g.scodoc_dept_id:
abort(404, "groupes inexistants dans ce département")
require_module = sco_preferences.get_preference( require_module = sco_preferences.get_preference(
"abs_require_module", formsemestre_id "abs_require_module", formsemestre_id
) )
@ -381,11 +390,8 @@ def SignaleAbsenceGrHebdo(
# Si aucun etudiant n'est inscrit au module choisi... # Si aucun etudiant n'est inscrit au module choisi...
moduleimpl_id = None moduleimpl_id = None
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
sem = formsemestre.to_dict() sem = formsemestre.to_dict()
# sem = sco_formsemestre.do_formsemestre_list({"formsemestre_id": formsemestre_id})[0]
# calcule dates jours de cette semaine # calcule dates jours de cette semaine
# liste de dates iso "yyyy-mm-dd" # liste de dates iso "yyyy-mm-dd"
@ -506,6 +512,8 @@ def SignaleAbsenceGrSemestre(
) )
formsemestre_id = groups_infos.formsemestre_id formsemestre_id = groups_infos.formsemestre_id
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
if formsemestre.dept_id != g.scodoc_dept_id:
return abort(404, "groupes inexistants dans ce département")
sem = formsemestre.to_dict() sem = formsemestre.to_dict()
require_module = sco_preferences.get_preference( require_module = sco_preferences.get_preference(
"abs_require_module", formsemestre_id "abs_require_module", formsemestre_id
@ -1217,7 +1225,9 @@ def XMLgetBilletsEtud(etudid=False, code_nip=False):
"""Liste billets pour un etudiant""" """Liste billets pour un etudiant"""
log("Warning: called deprecated XMLgetBilletsEtud") log("Warning: called deprecated XMLgetBilletsEtud")
if etudid is False: if etudid is False:
etud = Identite.query.filter_by(code_nip=str(code_nip)).first_or_404() etud = Identite.query.filter_by(
code_nip=str(code_nip), dept_id=g.scodoc_dept_id
).first_or_404()
etudid = etud.id etudid = etud.id
table = sco_abs_billets.table_billets_etud(etudid) table = sco_abs_billets.table_billets_etud(etudid)
if table: if table:
@ -1243,7 +1253,8 @@ def list_billets():
tf = TrivialFormulator( tf = TrivialFormulator(
request.base_url, request.base_url,
scu.get_request_args(), scu.get_request_args(),
(("billet_id", {"input_type": "text", "title": "Numéro du billet"}),), (("billet_id", {"input_type": "text", "title": "Numéro du billet :"}),),
method="get",
submitbutton=False, submitbutton=False,
) )
if tf[0] == 0: if tf[0] == 0:
@ -1264,14 +1275,14 @@ def list_billets():
@scodoc7func @scodoc7func
def delete_billets_absence(billet_id, dialog_confirmed=False): def delete_billets_absence(billet_id, dialog_confirmed=False):
"""Supprime un billet.""" """Supprime un billet."""
cnx = ndb.GetDBConnexion() billet: BilletAbsence = (
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) BilletAbsence.query.filter_by(id=billet_id)
if not billets: .join(Identite)
return flask.redirect( .filter_by(dept_id=g.scodoc_dept_id)
"list_billets?head_message=Billet%%20%s%%20inexistant !" % billet_id .first_or_404()
) )
if not dialog_confirmed: if not dialog_confirmed:
tab = sco_abs_billets.table_billets(billets) tab = sco_abs_billets.table_billets([billet])
return scu.confirm_dialog( return scu.confirm_dialog(
"""<h2>Supprimer ce billet ?</h2>""" + tab.html(), """<h2>Supprimer ce billet ?</h2>""" + tab.html(),
dest_url="", dest_url="",
@ -1279,30 +1290,35 @@ def delete_billets_absence(billet_id, dialog_confirmed=False):
parameters={"billet_id": billet_id}, parameters={"billet_id": billet_id},
) )
sco_abs.billet_absence_delete(cnx, billet_id) db.session.delete(billet)
db.session.commit()
return flask.redirect("list_billets?head_message=Billet%20supprimé") flash("Billet supprimé")
return flask.redirect(url_for("absences.list_billets", scodoc_dept=g.scodoc_dept))
def _ProcessBilletAbsence(billet, estjust, description): def _ProcessBilletAbsence(
billet: BilletAbsence, estjust: bool, description: str
) -> int:
"""Traite un billet: ajoute absence(s) et éventuellement justificatifs, """Traite un billet: ajoute absence(s) et éventuellement justificatifs,
et change l'état du billet à 1. et change l'état du billet à True.
NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après-midi. return: nombre de demi-journées d'absence ajoutées, -1 si billet déjà traité.
NB: actuellement, les heures ne sont utilisées que pour déterminer
si matin et/ou après-midi.
""" """
cnx = ndb.GetDBConnexion() if billet.etat:
if billet["etat"] != 0: log(f"billet deja traite: {billet} !")
log("billet=%s" % billet)
log("billet deja traité !")
return -1 return -1
n = 0 # nombre de demi-journées d'absence ajoutées n = 0 # nombre de demi-journées d'absence ajoutées
# 1-- ajout des absences (et justifs)
datedebut = billet["abs_begin"].strftime("%d/%m/%Y") # 1-- Ajout des absences (et justifs)
datefin = billet["abs_end"].strftime("%d/%m/%Y") datedebut = billet.abs_begin.strftime("%d/%m/%Y")
datefin = billet.abs_end.strftime("%d/%m/%Y")
dates = sco_abs.DateRangeISO(datedebut, datefin) dates = sco_abs.DateRangeISO(datedebut, datefin)
# commence après-midi ? # commence après-midi ?
if dates and billet["abs_begin"].hour > 11: if dates and billet.abs_begin.hour > 11:
sco_abs.add_absence( sco_abs.add_absence(
billet["etudid"], billet.etudid,
dates[0], dates[0],
0, 0,
estjust, estjust,
@ -1311,9 +1327,9 @@ def _ProcessBilletAbsence(billet, estjust, description):
n += 1 n += 1
dates = dates[1:] dates = dates[1:]
# termine matin ? # termine matin ?
if dates and billet["abs_end"].hour < 12: if dates and billet.abs_end.hour < 12:
sco_abs.add_absence( sco_abs.add_absence(
billet["etudid"], billet.etudid,
dates[-1], dates[-1],
1, 1,
estjust, estjust,
@ -1324,14 +1340,14 @@ def _ProcessBilletAbsence(billet, estjust, description):
for jour in dates: for jour in dates:
sco_abs.add_absence( sco_abs.add_absence(
billet["etudid"], billet.etudid,
jour, jour,
0, 0,
estjust, estjust,
description=description, description=description,
) )
sco_abs.add_absence( sco_abs.add_absence(
billet["etudid"], billet.etudid,
jour, jour,
1, 1,
estjust, estjust,
@ -1339,9 +1355,10 @@ def _ProcessBilletAbsence(billet, estjust, description):
) )
n += 2 n += 2
# 2- change etat du billet # 2- Change état du billet
sco_abs.billet_absence_edit(cnx, {"billet_id": billet["billet_id"], "etat": 1}) billet.etat = True
db.session.add(billet)
db.session.commit()
return n return n
@ -1351,26 +1368,27 @@ def _ProcessBilletAbsence(billet, estjust, description):
@scodoc7func @scodoc7func
def process_billet_absence_form(billet_id): def process_billet_absence_form(billet_id):
"""Formulaire traitement d'un billet""" """Formulaire traitement d'un billet"""
cnx = ndb.GetDBConnexion() billet: BilletAbsence = (
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id}) BilletAbsence.query.filter_by(id=billet_id)
if not billets: .join(Identite)
return flask.redirect( .filter_by(dept_id=g.scodoc_dept_id)
"list_billets?head_message=Billet%%20%s%%20inexistant !" % billet_id .first()
)
if billet is None:
raise ScoValueError(
f"Aucun billet avec le numéro <tt>{billet_id}</tt> dans ce département.",
dest_url=url_for("absences.list_billets", scodoc_dept=g.scodoc_dept),
) )
billet = billets[0] etud = billet.etudiant
etudid = billet["etudid"]
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
H = [ H = [
html_sco_header.sco_header( html_sco_header.sco_header(
page_title="Traitement billet d'absence de %s" % etud["nomprenom"], page_title=f"Traitement billet d'absence de {etud.nomprenom}",
),
'<h2>Traitement du billet %s : <a class="discretelink" href="%s">%s</a></h2>'
% (
billet_id,
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
etud["nomprenom"],
), ),
f"""<h2>Traitement du billet {billet.id} : <a class="discretelink" href="{
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id)
}">{etud.nomprenom}</a></h2>
""",
] ]
tf = TrivialFormulator( tf = TrivialFormulator(
@ -1381,7 +1399,7 @@ def process_billet_absence_form(billet_id):
( (
"etudid", "etudid",
{"input_type": "hidden"}, {"input_type": "hidden"},
), # pour centrer l'UI sur l'étudiant ),
( (
"estjust", "estjust",
{"input_type": "boolcheckbox", "title": "Absences justifiées"}, {"input_type": "boolcheckbox", "title": "Absences justifiées"},
@ -1389,24 +1407,31 @@ def process_billet_absence_form(billet_id):
("description", {"input_type": "text", "size": 42, "title": "Raison"}), ("description", {"input_type": "text", "size": 42, "title": "Raison"}),
), ),
initvalues={ initvalues={
"description": billet["description"], "description": billet.description or "",
"estjust": billet["justified"], "estjust": billet.justified,
"etudid": etudid, "etudid": etud.id,
}, },
submitlabel="Enregistrer ces absences", submitlabel="Enregistrer ces absences",
) )
if tf[0] == 0: if tf[0] == 0:
tab = sco_abs_billets.table_billets([billet], etud=etud) tab = sco_abs_billets.table_billets([billet], etud=etud)
H.append(tab.html()) H.append(tab.html())
if billet["justified"]: if billet.justified:
H.append( H.append(
"""<p>L'étudiant pense pouvoir justifier cette absence.<br/><em>Vérifiez le justificatif avant d'enregistrer.</em></p>""" """<p>L'étudiant pense pouvoir justifier cette absence.<br/>
<em>Vérifiez le justificatif avant d'enregistrer.</em></p>"""
) )
F = ( F = f"""<p><a class="stdlink" href="{
"""<p><a class="stdlink" href="delete_billets_absence?billet_id=%s">Supprimer ce billet</a> (utiliser en cas d'erreur, par ex. billet en double)</p>""" url_for("absences.delete_billets_absence",
% billet_id scodoc_dept=g.scodoc_dept, billet_id=billet_id)
) }">Supprimer ce billet</a>
F += '<p><a class="stdlink" href="list_billets">Liste de tous les billets en attente</a></p>' (utiliser en cas d'erreur, par ex. billet en double)
</p>
<p><a class="stdlink" href="{
url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
}">Liste de tous les billets en attente</a>
</p>
"""
return "\n".join(H) + "<br/>" + tf[1] + F + html_sco_header.sco_footer() return "\n".join(H) + "<br/>" + tf[1] + F + html_sco_header.sco_footer()
elif tf[0] == -1: elif tf[0] == -1:
@ -1425,10 +1450,18 @@ def process_billet_absence_form(billet_id):
elif n < 0: elif n < 0:
H.append("Ce billet avait déjà été traité !") H.append("Ce billet avait déjà été traité !")
H.append( H.append(
'</div><p><a class="stdlink" href="list_billets">Autre billets en attente</a></p><h4>Billets déclarés par %s</h4>' f"""</div><p><a class="stdlink" href="{
% (etud["nomprenom"]) url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
}">Autre billets en attente</a>
</p>
<h4>Billets déclarés par {etud.nomprenom}</h4>
"""
)
billets = (
BilletAbsence.query.filter_by(etudid=etud.id)
.join(Identite)
.filter_by(dept_id=g.scodoc_dept_id)
) )
billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
tab = sco_abs_billets.table_billets(billets, etud=etud) tab = sco_abs_billets.table_billets(billets, etud=etud)
H.append(tab.html()) H.append(tab.html())
return "\n".join(H) + html_sco_header.sco_footer() return "\n".join(H) + html_sco_header.sco_footer()