forked from ScoDoc/ScoDoc
Billets absences: nouvelle API + modernisation code
This commit is contained in:
parent
fd06d8428a
commit
9a2c3b8174
@ -23,12 +23,15 @@ def requested_format(default_format="json", allowed_formats=None):
|
||||
|
||||
|
||||
from app.api import tokens
|
||||
from app.api import departements
|
||||
from app.api import etudiants
|
||||
from app.api import formations
|
||||
from app.api import formsemestres
|
||||
from app.api import partitions
|
||||
from app.api import evaluations
|
||||
from app.api import jury
|
||||
from app.api import absences
|
||||
from app.api import logos
|
||||
from app.api import (
|
||||
absences,
|
||||
billets_absences,
|
||||
departements,
|
||||
etudiants,
|
||||
formations,
|
||||
formsemestres,
|
||||
logos,
|
||||
partitions,
|
||||
evaluations,
|
||||
jury,
|
||||
)
|
||||
|
84
app/api/billets_absences.py
Normal file
84
app/api/billets_absences.py
Normal file
@ -0,0 +1,84 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
API : billets d'absences
|
||||
"""
|
||||
|
||||
from flask import g, jsonify, request
|
||||
from flask_login import login_required
|
||||
|
||||
import app
|
||||
from app import db
|
||||
from app.api import api_bp as bp, api_web_bp
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.api.errors import error_response
|
||||
from app.models import BilletAbsence
|
||||
from app.models.etudiants import Identite
|
||||
from app.scodoc import sco_abs_billets
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
|
||||
@bp.route("/billets_absence/etudiant/<int:etudid>")
|
||||
@api_web_bp.route("/billets_absence/etudiant/<int:etudid>")
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def billets_absence_etudiant(etudid: int):
|
||||
"""Liste des billets d'absence pour cet étudiant"""
|
||||
billets = sco_abs_billets.query_billets_etud(etudid)
|
||||
return jsonify([billet.to_dict() for billet in billets])
|
||||
|
||||
|
||||
@bp.route("/billets_absence/add", methods=["POST"])
|
||||
@api_web_bp.route("/billets_absence/add", methods=["POST"])
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsAddBillet)
|
||||
def billets_absence_add():
|
||||
"""Ajout d'un billet d'absence"""
|
||||
data = request.get_json(force=True) # may raise 400 Bad Request
|
||||
etudid = data.get("etudid")
|
||||
abs_begin = data.get("abs_begin")
|
||||
abs_end = data.get("abs_end")
|
||||
description = data.get("description", "")
|
||||
justified = data.get("justified", False)
|
||||
if None in (etudid, abs_begin, abs_end):
|
||||
return error_response(
|
||||
404, message="Paramètre manquant: etudid, abs_bein, abs_end requis"
|
||||
)
|
||||
query = Identite.query.filter_by(etudid=etudid)
|
||||
if g.scodoc_dept:
|
||||
query = query.filter_by(dept_id=g.scodoc_dept_id)
|
||||
etud = query.first_or_404()
|
||||
billet = BilletAbsence(
|
||||
etudid=etud.id,
|
||||
abs_begin=abs_begin,
|
||||
abs_end=abs_end,
|
||||
description=description,
|
||||
etat=False,
|
||||
justified=justified,
|
||||
)
|
||||
db.session.add(billet)
|
||||
db.session.commit()
|
||||
return jsonify(billet.to_dict())
|
||||
|
||||
|
||||
@bp.route("/billets_absence/<int:billet_id>/delete", methods=["POST"])
|
||||
@api_web_bp.route("/billets_absence/<int:billet_id>/delete", methods=["POST"])
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsAddBillet)
|
||||
def billets_absence_delete(billet_id: int):
|
||||
"""Suppression d'un billet d'absence"""
|
||||
query = BilletAbsence.query.filter_by(id=billet_id)
|
||||
if g.scodoc_dept is not None:
|
||||
# jointure avec departement de l'étudiant
|
||||
query = query.join(BilletAbsence.etudiant).filter_by(dept_id=g.scodoc_dept_id)
|
||||
billet = query.first_or_404()
|
||||
db.session.delete(billet)
|
||||
db.session.commit()
|
||||
return jsonify({"OK": True})
|
@ -88,7 +88,7 @@ class BilletAbsence(db.Model):
|
||||
justified = db.Column(db.Boolean(), default=False, server_default="false")
|
||||
|
||||
def to_dict(self):
|
||||
data = {
|
||||
return {
|
||||
"id": self.id,
|
||||
"billet_id": self.id,
|
||||
"etudid": self.etudid,
|
||||
@ -99,4 +99,3 @@ class BilletAbsence(db.Model):
|
||||
"entry_date": self.entry_date,
|
||||
"justified": self.justified,
|
||||
}
|
||||
return data
|
||||
|
@ -133,7 +133,7 @@ def sidebar(etudid: int = None):
|
||||
)
|
||||
if sco_preferences.get_preference("handle_billets_abs"):
|
||||
H.append(
|
||||
f"""<li><a href="{ url_for('absences.listeBilletsEtud', scodoc_dept=g.scodoc_dept, etudid=etudid) }">Billets</a></li>"""
|
||||
f"""<li><a href="{ url_for('absences.billets_etud', scodoc_dept=g.scodoc_dept, etudid=etudid) }">Billets</a></li>"""
|
||||
)
|
||||
H.append(
|
||||
f"""
|
||||
|
@ -770,33 +770,6 @@ def annule_justif(etudid, jour, matin):
|
||||
invalidate_abs_etud_date(etudid, jour)
|
||||
|
||||
|
||||
# ---- BILLETS
|
||||
|
||||
_billet_absenceEditor = ndb.EditableTable(
|
||||
"billet_absence",
|
||||
"billet_id",
|
||||
(
|
||||
"billet_id",
|
||||
"etudid",
|
||||
"abs_begin",
|
||||
"abs_end",
|
||||
"description",
|
||||
"etat",
|
||||
"entry_date",
|
||||
"justified",
|
||||
),
|
||||
sortkey="entry_date desc",
|
||||
input_formators={
|
||||
"etat": bool,
|
||||
"justified": bool,
|
||||
},
|
||||
)
|
||||
|
||||
billet_absence_create = _billet_absenceEditor.create
|
||||
billet_absence_delete = _billet_absenceEditor.delete
|
||||
billet_absence_list = _billet_absenceEditor.list
|
||||
billet_absence_edit = _billet_absenceEditor.edit
|
||||
|
||||
# ------ HTML Calendar functions (see YearTable function)
|
||||
|
||||
# MONTH/DAY NAMES:
|
||||
|
148
app/scodoc/sco_abs_billets.py
Normal file
148
app/scodoc/sco_abs_billets.py
Normal file
@ -0,0 +1,148 @@
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gestion scolarite IUT
|
||||
#
|
||||
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Emmanuel Viennet emmanuel.viennet@viennet.net
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""Fonctions sur les billets d'absences
|
||||
"""
|
||||
|
||||
from flask import g, url_for
|
||||
import flask_sqlalchemy
|
||||
from app.models.absences import BilletAbsence
|
||||
from app.models.etudiants import Identite
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import sco_preferences
|
||||
|
||||
|
||||
def query_billets_etud(
|
||||
etudid: int = None, etat: bool = None
|
||||
) -> flask_sqlalchemy.BaseQuery:
|
||||
"""Billets d'absences pour un étudiant.
|
||||
Si etat, filtre par état.
|
||||
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.
|
||||
"""
|
||||
if g.scodoc_dept is not None and not sco_preferences.get_preference(
|
||||
"handle_billets_abs"
|
||||
):
|
||||
return []
|
||||
billets = BilletAbsence.query.filter_by(etudid=etudid)
|
||||
if etat is not None:
|
||||
billets = billets.query.filter_by(etat=False)
|
||||
if g.scodoc_dept is not None:
|
||||
# jointure avec departement de l'étudiant
|
||||
billets = billets.join(BilletAbsence.etudiant).filter_by(
|
||||
dept_id=g.scodoc_dept_id
|
||||
)
|
||||
return billets
|
||||
|
||||
|
||||
def table_billets_etud(
|
||||
etudid: int = None, etat: bool = None, with_links=True
|
||||
) -> GenTable:
|
||||
"""Page avec table billets."""
|
||||
etud = Identite.query.get_or_404(etudid) if etudid is not None else None
|
||||
billets = query_billets_etud(etud.id, etat)
|
||||
return table_billets(billets, etud=etud, with_links=with_links)
|
||||
|
||||
|
||||
def table_billets(
|
||||
billets: list[BilletAbsence], etud: Identite = None, title="", with_links=True
|
||||
) -> GenTable:
|
||||
"""Construit une table de billets d'absences"""
|
||||
rows = []
|
||||
for billet in billets:
|
||||
billet_dict = billet.to_dict()
|
||||
rows.append(billet_dict)
|
||||
if billet_dict["abs_begin"].hour < 12:
|
||||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
billet_dict["abs_begin_str"] = billet.abs_begin.strftime("%d/%m/%Y") + m
|
||||
if billet.abs_end.hour < 12:
|
||||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
billet_dict["abs_end_str"] = billet.abs_end.strftime("%d/%m/%Y") + m
|
||||
if billet.etat == 0:
|
||||
if billet.justified:
|
||||
billet_dict["etat_str"] = "à traiter"
|
||||
else:
|
||||
billet_dict["etat_str"] = "à justifier"
|
||||
if with_links:
|
||||
if etud:
|
||||
billet_dict["_etat_str_target"] = url_for(
|
||||
"absences.process_billet_absence_form",
|
||||
billet_id=billet_dict["billet_id"],
|
||||
scodoc_dept=billet.etudiant.departement.acronym,
|
||||
etudid=etud.id,
|
||||
)
|
||||
else:
|
||||
billet_dict["_etat_str_target"] = url_for(
|
||||
"absences.process_billet_absence_form",
|
||||
billet_id=billet_dict["billet_id"],
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
)
|
||||
billet_dict["_billet_id_target"] = billet_dict["_etat_str_target"]
|
||||
else:
|
||||
billet_dict["etat_str"] = "ok"
|
||||
if not etud:
|
||||
# ajoute info etudiant
|
||||
etud = billet.etudiant
|
||||
if not etud:
|
||||
billet_dict["nomprenom"] = "???" # should not occur
|
||||
else:
|
||||
billet_dict["nomprenom"] = etud.nomprenom
|
||||
if with_links:
|
||||
billet_dict["_nomprenom_target"] = url_for(
|
||||
"scolar.ficheEtud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=billet_dict["etudid"],
|
||||
)
|
||||
|
||||
if etud and not title:
|
||||
title = f"Billets d'absence déclarés par {etud.nomprenom}"
|
||||
|
||||
columns_ids = ["billet_id"]
|
||||
if not etud:
|
||||
columns_ids += ["nomprenom"]
|
||||
columns_ids += ["abs_begin_str", "abs_end_str", "description", "etat_str"]
|
||||
|
||||
tab = GenTable(
|
||||
titles={
|
||||
"billet_id": "Numéro",
|
||||
"abs_begin_str": "Début",
|
||||
"abs_end_str": "Fin",
|
||||
"description": "Raison de l'absence",
|
||||
"etat_str": "Etat",
|
||||
},
|
||||
columns_ids=columns_ids,
|
||||
page_title=title,
|
||||
html_title=f"<h2>{title}</h2>",
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
rows=rows,
|
||||
html_sortable=True,
|
||||
)
|
||||
return tab
|
@ -60,7 +60,8 @@ from flask import g, request
|
||||
from flask import url_for
|
||||
from flask_login import current_user
|
||||
|
||||
from app import log
|
||||
from app import db, log
|
||||
from app import api
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.decorators import (
|
||||
@ -83,6 +84,7 @@ from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_abs_billets
|
||||
from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_find_etud
|
||||
@ -142,9 +144,11 @@ def index_html():
|
||||
Permission.ScoAbsChange
|
||||
) and sco_preferences.get_preference("handle_billets_abs"):
|
||||
H.append(
|
||||
"""
|
||||
f"""
|
||||
<h2 style="margin-top: 30px;">Billets d'absence</h2>
|
||||
<ul><li><a href="listeBillets">Traitement des billets d'absence en attente</a></li></ul>
|
||||
<ul><li><a href="{url_for("absences.list_billets", scodoc_dept=g.scodoc_dept)
|
||||
}">Traitement des billets d'absence en attente</a>
|
||||
</li></ul>
|
||||
"""
|
||||
)
|
||||
H.append(html_sco_header.sco_footer())
|
||||
@ -1105,18 +1109,12 @@ def AddBilletAbsence(
|
||||
"""Mémorise un "billet"
|
||||
begin et end sont au format ISO (eg "1999-01-08 04:05:06")
|
||||
"""
|
||||
t0 = time.time()
|
||||
log("Warning: calling deprecated AddBilletAbsence")
|
||||
begin = str(begin)
|
||||
end = str(end)
|
||||
code_nip = str(code_nip) if code_nip else None
|
||||
|
||||
# check etudid
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid, code_nip=code_nip, filled=True)
|
||||
if not etuds:
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
etud = api.tools.get_etud(etudid=None, nip=None, ine=None)
|
||||
# check dates
|
||||
begin_date = dateutil.parser.isoparse(begin) # may raises ValueError
|
||||
end_date = dateutil.parser.isoparse(end)
|
||||
@ -1125,36 +1123,33 @@ def AddBilletAbsence(
|
||||
#
|
||||
justified = bool(justified)
|
||||
xml_reply = bool(xml_reply)
|
||||
#
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billet_id = sco_abs.billet_absence_create(
|
||||
cnx,
|
||||
{
|
||||
"etudid": etud["etudid"],
|
||||
"abs_begin": begin,
|
||||
"abs_end": end,
|
||||
"description": description,
|
||||
"etat": False,
|
||||
"justified": justified,
|
||||
},
|
||||
)
|
||||
if xml_reply: # backward compat
|
||||
format = "xml"
|
||||
#
|
||||
billet = BilletAbsence(
|
||||
etudid=etud.id,
|
||||
abs_begin=begin,
|
||||
abs_end=end,
|
||||
description=description,
|
||||
etat=False,
|
||||
justified=justified,
|
||||
)
|
||||
db.session.add(billet)
|
||||
db.session.commit()
|
||||
|
||||
# Renvoie le nouveau billet au format demandé
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
log("AddBilletAbsence: new billet_id=%s (%gs)" % (billet_id, time.time() - t0))
|
||||
return tab.make_page(format=format)
|
||||
table = sco_abs_billets.table_billets([billet], etud=etud)
|
||||
log(f"AddBilletAbsence: new billet_id={billet.id}")
|
||||
return table.make_page(format=format)
|
||||
|
||||
|
||||
@bp.route("/AddBilletAbsenceForm", methods=["GET", "POST"])
|
||||
@bp.route("/add_billets_absence_form", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsAddBillet)
|
||||
@scodoc7func
|
||||
def AddBilletAbsenceForm(etudid):
|
||||
"""Formulaire ajout billet (pour tests seulement, le vrai formulaire accessible aux etudiants
|
||||
étant sur le portail étudiant).
|
||||
def add_billets_absence_form(etudid):
|
||||
"""Formulaire ajout billet (pour tests seulement, le vrai
|
||||
formulaire accessible aux etudiants étant sur le portail étudiant).
|
||||
"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
H = [
|
||||
@ -1195,125 +1190,50 @@ def AddBilletAbsenceForm(etudid):
|
||||
justified=tf[2]["justified"],
|
||||
)
|
||||
)
|
||||
return flask.redirect("listeBilletsEtud?etudid=" + str(etudid))
|
||||
return flask.redirect("billets_etud?etudid=" + str(etudid))
|
||||
|
||||
|
||||
def _tableBillets(billets, etud=None, title=""):
|
||||
for b in billets:
|
||||
if b["abs_begin"].hour < 12:
|
||||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
b["abs_begin_str"] = b["abs_begin"].strftime("%d/%m/%Y") + m
|
||||
if b["abs_end"].hour < 12:
|
||||
m = " matin"
|
||||
else:
|
||||
m = " après-midi"
|
||||
b["abs_end_str"] = b["abs_end"].strftime("%d/%m/%Y") + m
|
||||
if b["etat"] == 0:
|
||||
if b["justified"]:
|
||||
b["etat_str"] = "à traiter"
|
||||
else:
|
||||
b["etat_str"] = "à justifier"
|
||||
b["_etat_str_target"] = (
|
||||
"ProcessBilletAbsenceForm?billet_id=%s" % b["billet_id"]
|
||||
)
|
||||
if etud:
|
||||
b["_etat_str_target"] += "&etudid=%s" % etud["etudid"]
|
||||
b["_billet_id_target"] = b["_etat_str_target"]
|
||||
else:
|
||||
b["etat_str"] = "ok"
|
||||
if not etud:
|
||||
# ajoute info etudiant
|
||||
e = sco_etud.get_etud_info(etudid=b["etudid"], filled=1)
|
||||
if not e:
|
||||
b["nomprenom"] = "???" # should not occur
|
||||
else:
|
||||
b["nomprenom"] = e[0]["nomprenom"]
|
||||
b["_nomprenom_target"] = url_for(
|
||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=b["etudid"]
|
||||
)
|
||||
if etud and not title:
|
||||
title = "Billets d'absence déclarés par %(nomprenom)s" % etud
|
||||
else:
|
||||
title = title
|
||||
columns_ids = ["billet_id"]
|
||||
if not etud:
|
||||
columns_ids += ["nomprenom"]
|
||||
columns_ids += ["abs_begin_str", "abs_end_str", "description", "etat_str"]
|
||||
|
||||
tab = GenTable(
|
||||
titles={
|
||||
"billet_id": "Numéro",
|
||||
"abs_begin_str": "Début",
|
||||
"abs_end_str": "Fin",
|
||||
"description": "Raison de l'absence",
|
||||
"etat_str": "Etat",
|
||||
},
|
||||
columns_ids=columns_ids,
|
||||
page_title=title,
|
||||
html_title="<h2>%s</h2>" % title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
rows=billets,
|
||||
html_sortable=True,
|
||||
)
|
||||
return tab
|
||||
|
||||
|
||||
@bp.route("/listeBilletsEtud")
|
||||
@bp.route("/billets_etud/<int:etudid>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def listeBilletsEtud(etudid=False, format="html"):
|
||||
def billets_etud(etudid=False):
|
||||
"""Liste billets pour un etudiant"""
|
||||
etuds = sco_etud.get_etud_info(filled=True, etudid=etudid)
|
||||
if not etuds:
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
return tab.make_page(format=format)
|
||||
fmt = request.args.get("format", "html")
|
||||
if not fmt in {"html", "json", "xml", "xls", "xlsx"}:
|
||||
return ScoValueError("Format invalide")
|
||||
table = sco_abs_billets.table_billets_etud(etudid)
|
||||
if table:
|
||||
return table.make_page(format=fmt)
|
||||
return ""
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/XMLgetBilletsEtud", methods=["GET", "POST"]
|
||||
) # pour compat anciens clients PHP
|
||||
# DEEPRECATED: pour compat anciens clients PHP
|
||||
@bp.route("/XMLgetBilletsEtud", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def XMLgetBilletsEtud(etudid=False):
|
||||
"""Liste billets pour un etudiant"""
|
||||
if not sco_preferences.get_preference("handle_billets_abs"):
|
||||
log("Warning: called deprecated XMLgetBilletsEtud")
|
||||
table = sco_abs_billets.table_billets_etud(etudid)
|
||||
if table:
|
||||
return table.make_page(format="xml")
|
||||
return ""
|
||||
t0 = time.time()
|
||||
r = listeBilletsEtud(etudid, format="xml")
|
||||
log("XMLgetBilletsEtud (%gs)" % (time.time() - t0))
|
||||
return r
|
||||
|
||||
|
||||
@bp.route("/listeBillets", methods=["GET"])
|
||||
@bp.route("/list_billets", methods=["GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def listeBillets():
|
||||
"""Page liste des billets non traités et formulaire recherche d'un billet"""
|
||||
# utilise Flask, jointure avec departement de l'étudiant
|
||||
billets = (
|
||||
BilletAbsence.query.filter_by(etat=False)
|
||||
.join(BilletAbsence.etudiant, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id)
|
||||
)
|
||||
# reconverti en dict pour les fonctions scodoc7
|
||||
billets = [b.to_dict() for b in billets]
|
||||
#
|
||||
tab = _tableBillets(billets)
|
||||
T = tab.html()
|
||||
def list_billets():
|
||||
"""Page liste des billets non traités pour tous les étudiants du département
|
||||
et formulaire recherche d'un billet.
|
||||
"""
|
||||
table = sco_abs_billets.table_billets_etud(etat=False)
|
||||
T = table.html()
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Billet d'absence non traités"),
|
||||
"<h2>Billets d'absence en attente de traitement (%d)</h2>" % len(billets),
|
||||
f"<h2>Billets d'absence en attente de traitement ({table.get_nb_rows()})</h2>",
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
@ -1326,34 +1246,38 @@ def listeBillets():
|
||||
return "\n".join(H) + tf[1] + T + html_sco_header.sco_footer()
|
||||
else:
|
||||
return flask.redirect(
|
||||
"ProcessBilletAbsenceForm?billet_id=" + tf[2]["billet_id"]
|
||||
url_for(
|
||||
"absences.process_billet_absence_form",
|
||||
billet_id=tf[2]["billet_id"],
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/deleteBilletAbsence", methods=["POST", "GET"])
|
||||
@bp.route("/delete_billets_absence", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def deleteBilletAbsence(billet_id, dialog_confirmed=False):
|
||||
def delete_billets_absence(billet_id, dialog_confirmed=False):
|
||||
"""Supprime un billet."""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
if not billets:
|
||||
return flask.redirect(
|
||||
"listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id
|
||||
"list_billets?head_message=Billet%%20%s%%20inexistant !" % billet_id
|
||||
)
|
||||
if not dialog_confirmed:
|
||||
tab = _tableBillets(billets)
|
||||
tab = sco_abs_billets.table_billets(billets)
|
||||
return scu.confirm_dialog(
|
||||
"""<h2>Supprimer ce billet ?</h2>""" + tab.html(),
|
||||
dest_url="",
|
||||
cancel_url="listeBillets",
|
||||
cancel_url="list_billets",
|
||||
parameters={"billet_id": billet_id},
|
||||
)
|
||||
|
||||
sco_abs.billet_absence_delete(cnx, billet_id)
|
||||
|
||||
return flask.redirect("listeBillets?head_message=Billet%20supprimé")
|
||||
return flask.redirect("list_billets?head_message=Billet%20supprimé")
|
||||
|
||||
|
||||
def _ProcessBilletAbsence(billet, estjust, description):
|
||||
@ -1417,17 +1341,17 @@ def _ProcessBilletAbsence(billet, estjust, description):
|
||||
return n
|
||||
|
||||
|
||||
@bp.route("/ProcessBilletAbsenceForm", methods=["POST", "GET"])
|
||||
@bp.route("/process_billet_absence_form", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def ProcessBilletAbsenceForm(billet_id):
|
||||
def process_billet_absence_form(billet_id):
|
||||
"""Formulaire traitement d'un billet"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
if not billets:
|
||||
return flask.redirect(
|
||||
"listeBillets?head_message=Billet%%20%s%%20inexistant !" % billet_id
|
||||
"list_billets?head_message=Billet%%20%s%%20inexistant !" % billet_id
|
||||
)
|
||||
billet = billets[0]
|
||||
etudid = billet["etudid"]
|
||||
@ -1468,17 +1392,17 @@ def ProcessBilletAbsenceForm(billet_id):
|
||||
submitlabel="Enregistrer ces absences",
|
||||
)
|
||||
if tf[0] == 0:
|
||||
tab = _tableBillets([billet], etud=etud)
|
||||
tab = sco_abs_billets.table_billets([billet], etud=etud)
|
||||
H.append(tab.html())
|
||||
if billet["justified"]:
|
||||
H.append(
|
||||
"""<p>L'étudiant pense pouvoir justifier cette absence.<br/><em>Vérifiez le justificatif avant d'enregistrer.</em></p>"""
|
||||
)
|
||||
F = (
|
||||
"""<p><a class="stdlink" href="deleteBilletAbsence?billet_id=%s">Supprimer ce billet</a> (utiliser en cas d'erreur, par ex. billet en double)</p>"""
|
||||
"""<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>"""
|
||||
% billet_id
|
||||
)
|
||||
F += '<p><a class="stdlink" href="listeBillets">Liste de tous les billets en attente</a></p>'
|
||||
F += '<p><a class="stdlink" href="list_billets">Liste de tous les billets en attente</a></p>'
|
||||
|
||||
return "\n".join(H) + "<br/>" + tf[1] + F + html_sco_header.sco_footer()
|
||||
elif tf[0] == -1:
|
||||
@ -1497,11 +1421,11 @@ def ProcessBilletAbsenceForm(billet_id):
|
||||
elif n < 0:
|
||||
H.append("Ce billet avait déjà été traité !")
|
||||
H.append(
|
||||
'</div><p><a class="stdlink" href="listeBillets">Autre billets en attente</a></p><h4>Billets déclarés par %s</h4>'
|
||||
'</div><p><a class="stdlink" href="list_billets">Autre billets en attente</a></p><h4>Billets déclarés par %s</h4>'
|
||||
% (etud["nomprenom"])
|
||||
)
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
tab = sco_abs_billets.table_billets(billets, etud=etud)
|
||||
H.append(tab.html())
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
@ -38,3 +38,32 @@ def api_headers() -> dict:
|
||||
r0 = requests.post(API_URL + "/tokens", auth=(API_USER, API_PASSWORD))
|
||||
token = r0.json()["token"]
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def GET(path: str, headers={}, errmsg=None, dept=None):
|
||||
"""Get and returns as JSON"""
|
||||
if dept:
|
||||
url = SCODOC_URL + f"/ScoDoc/{dept}/api" + path
|
||||
else:
|
||||
url = API_URL + path
|
||||
r = requests.get(url, headers=headers, verify=CHECK_CERTIFICATE)
|
||||
if r.status_code != 200:
|
||||
raise APIError(errmsg or f"""erreur status={r.status_code} !\n{r.text}""")
|
||||
return r.json() # decode la reponse JSON
|
||||
|
||||
|
||||
def POST_JSON(path: str, data: dict = {}, headers={}, errmsg=None):
|
||||
"""Post"""
|
||||
r = requests.post(
|
||||
API_URL + path,
|
||||
json=data,
|
||||
headers=headers,
|
||||
verify=CHECK_CERTIFICATE,
|
||||
)
|
||||
if r.status_code != 200:
|
||||
raise APIError(errmsg or f"erreur status={r.status_code} !\n{r.text}")
|
||||
return r.json() # decode la reponse JSON
|
||||
|
54
tests/api/test_api_billets.py
Normal file
54
tests/api/test_api_billets.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Test API Billets Absences
|
||||
|
||||
Utilisation :
|
||||
pytest tests/api/test_api_billets.py
|
||||
"""
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from tests.api.setup_test_api import GET, POST_JSON, api_headers
|
||||
|
||||
ETUDID = 1
|
||||
|
||||
|
||||
def test_billets(api_headers):
|
||||
"""
|
||||
Ajout, Liste, Suppression billets absences
|
||||
|
||||
Routes :
|
||||
- /billets_absence/add
|
||||
- /billets_absence/etudiant/<int:etudid>[?format=xml|json]
|
||||
- /billets_absence/delete
|
||||
"""
|
||||
billet_d = dict(
|
||||
etudid=ETUDID,
|
||||
abs_begin="2022-07-31",
|
||||
abs_end="2022-08-01",
|
||||
description="test 1",
|
||||
)
|
||||
billet_r = POST_JSON("/billets_absence/add", billet_d, headers=api_headers)
|
||||
assert billet_r["etudid"] == billet_d["etudid"]
|
||||
assert datetime.datetime.fromisoformat(billet_r["abs_begin"]).replace(
|
||||
tzinfo=None
|
||||
) == datetime.datetime.fromisoformat(billet_d["abs_begin"])
|
||||
billets = GET("/billets_absence/etudiant/1", headers=api_headers)
|
||||
assert isinstance(billets, list)
|
||||
assert len(billets) == 1
|
||||
assert billets[0] == billet_r
|
||||
billet_d2 = dict(
|
||||
etudid=ETUDID,
|
||||
abs_begin="2022-08-01",
|
||||
abs_end="2022-08-03",
|
||||
description="test 2",
|
||||
)
|
||||
billet_r = POST_JSON("/billets_absence/add", billet_d2, headers=api_headers)
|
||||
billets = GET("/billets_absence/etudiant/1", headers=api_headers)
|
||||
assert len(billets) == 2
|
||||
# Suppression
|
||||
for billet in billets:
|
||||
reply = POST_JSON(
|
||||
f"/billets_absence/{billet['id']}/delete", headers=api_headers
|
||||
)
|
||||
assert reply["OK"] == True
|
@ -125,7 +125,7 @@ def test_abs_basic(test_client):
|
||||
- set_group
|
||||
- EtatAbsenceGr
|
||||
- AddBilletAbsence
|
||||
- listeBilletsEtud
|
||||
- billets_etud
|
||||
"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
@ -333,9 +333,7 @@ def test_abs_basic(test_client):
|
||||
code_ine=etuds[0]["code_ine"],
|
||||
)
|
||||
|
||||
li_bi = absences.listeBilletsEtud(etudid=etudid, format="json").get_data(
|
||||
as_text=True
|
||||
)
|
||||
li_bi = absences.billets_etud(etudid=etudid, format="json").get_data(as_text=True)
|
||||
assert isinstance(li_bi, str)
|
||||
load_li_bi = json.loads(li_bi)
|
||||
|
||||
|
@ -88,6 +88,9 @@ def create_users(dept: Departement) -> tuple:
|
||||
sys.exit(1)
|
||||
perm_sco_view = Permission.get_by_name("ScoView")
|
||||
role.add_permission(perm_sco_view)
|
||||
# Edition billets
|
||||
perm_billets = Permission.get_by_name("ScoAbsAddBillet")
|
||||
role.add_permission(perm_billets)
|
||||
db.session.add(role)
|
||||
|
||||
user.add_role(role, None)
|
||||
|
Loading…
Reference in New Issue
Block a user