ScoDocMM/app/scodoc/sco_bulletins_xml.py

453 lines
17 KiB
Python
Raw Permalink Normal View History

2020-09-26 16:19:37 +02:00
# -*- mode: python -*-
# -*- coding: utf-8 -*-
##############################################################################
#
# Gestion scolarite IUT
#
2022-01-01 14:49:42 +01:00
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
2020-09-26 16:19:37 +02:00
#
# 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
#
##############################################################################
"""Génération du bulletin en format XML
Note: la structure de ce XML est issue de (mauvais) choix historiques
2021-07-10 16:01:37 +02:00
et ne peut pas être modifiée car d'autres logiciels l'utilisent (portail publication
bulletins etudiants).
2020-09-26 16:19:37 +02:00
Je recommande d'utiliser la version JSON.
2021-07-10 16:01:37 +02:00
Malheureusement, le code de génération JSON et XML sont séparés, ce qui est absurde et
complique la maintenance (si on ajoute des informations aux bulletins).
2020-09-26 16:19:37 +02:00
"""
2021-07-10 16:01:37 +02:00
# revu en juillet 21 pour utiliser ElementTree au lieu de jaxml
2021-02-01 16:23:11 +01:00
import datetime
2021-07-10 16:01:37 +02:00
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
2021-02-01 16:23:11 +01:00
2022-02-09 23:22:00 +01:00
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
2021-08-29 19:57:32 +02:00
from app import log
from app.but.bulletin_but_xml_compat import bulletin_but_xml_compat
from app.models.formsemestre import FormSemestre
from app.scodoc import sco_abs
from app.scodoc import sco_codes_parcours
2021-07-19 19:53:01 +02:00
from app.scodoc import sco_cache
from app.scodoc import sco_edit_ue
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups
from app.scodoc import sco_photos
from app.scodoc import sco_preferences
from app.scodoc import sco_etud
2021-07-10 16:01:37 +02:00
from app.scodoc import sco_xml
2020-09-26 16:19:37 +02:00
# -------- Bulletin en XML
# (fonction séparée: n'utilise pas formsemestre_bulletinetud_dict()
# pour simplifier le code, mais attention a la maintenance !)
#
def make_xml_formsemestre_bulletinetud(
formsemestre_id,
etudid,
doc=None, # XML document
force_publishing=False,
xml_nodate=False,
xml_with_decisions=False, # inclue les decisions même si non publiées
version="long",
) -> str:
2020-09-26 16:19:37 +02:00
"bulletin au format XML"
from app.scodoc import sco_bulletins
2020-09-26 16:19:37 +02:00
log("xml_bulletin( formsemestre_id=%s, etudid=%s )" % (formsemestre_id, etudid))
2021-08-19 10:28:35 +02:00
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
2021-12-11 20:27:58 +01:00
if formsemestre.formation.is_apc():
return bulletin_but_xml_compat(
formsemestre_id,
etudid,
doc=doc,
force_publishing=force_publishing,
xml_nodate=xml_nodate,
xml_with_decisions=xml_with_decisions, # inclue les decisions même si non publiées
version=version,
)
2021-08-11 00:36:07 +02:00
if (not sem["bul_hide_xml"]) or force_publishing:
published = 1
2020-09-26 16:19:37 +02:00
else:
published = 0
2020-09-26 16:19:37 +02:00
if xml_nodate:
docdate = ""
else:
docdate = datetime.datetime.now().isoformat()
el = {
2021-08-22 17:18:15 +02:00
"etudid": str(etudid),
"formsemestre_id": str(formsemestre_id),
2020-09-26 16:19:37 +02:00
"date": docdate,
"publie": str(published),
2020-09-26 16:19:37 +02:00
}
if sem["etapes"]:
2021-07-10 16:01:37 +02:00
el["etape_apo"] = str(sem["etapes"][0]) or ""
2020-09-26 16:19:37 +02:00
n = 2
for et in sem["etapes"][1:]:
2021-07-10 16:01:37 +02:00
el["etape_apo" + str(n)] = str(et) or ""
2020-09-26 16:19:37 +02:00
n += 1
2021-07-10 16:01:37 +02:00
x = Element("bulletinetud", **el)
if doc:
2021-07-10 17:40:40 +02:00
is_appending = True
2021-07-10 16:01:37 +02:00
doc.append(x)
else:
2021-07-10 17:40:40 +02:00
is_appending = False
2021-07-10 16:01:37 +02:00
doc = x
2020-09-26 16:19:37 +02:00
# Infos sur l'etudiant
2021-08-22 13:24:36 +02:00
etudinfo = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"etudiant",
2021-08-22 17:18:15 +02:00
etudid=str(etudid),
code_nip=str(etudinfo["code_nip"]),
code_ine=str(etudinfo["code_ine"]),
2021-07-10 16:01:37 +02:00
nom=scu.quote_xml_attr(etudinfo["nom"]),
prenom=scu.quote_xml_attr(etudinfo["prenom"]),
civilite=scu.quote_xml_attr(etudinfo["civilite_str"]),
sexe=scu.quote_xml_attr(etudinfo["civilite_str"]), # compat
photo_url=scu.quote_xml_attr(sco_photos.etud_photo_url(etudinfo)),
2021-07-10 16:01:37 +02:00
email=scu.quote_xml_attr(etudinfo["email"]),
emailperso=scu.quote_xml_attr(etudinfo["emailperso"]),
)
2020-09-26 16:19:37 +02:00
)
# Disponible pour publication ?
if not published:
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(
scu.SCO_ENCODING
) # stop !
2020-09-26 16:19:37 +02:00
# Groupes:
2021-08-19 10:28:35 +02:00
partitions = sco_groups.get_partitions_list(formsemestre_id, with_default=False)
2020-09-26 16:19:37 +02:00
partitions_etud_groups = {} # { partition_id : { etudid : group } }
for partition in partitions:
pid = partition["partition_id"]
2021-08-19 10:28:35 +02:00
partitions_etud_groups[pid] = sco_groups.get_etud_groups_in_partition(pid)
2020-09-26 16:19:37 +02:00
2022-02-09 23:22:00 +01:00
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
2021-12-24 00:08:25 +01:00
ues = nt.get_ues_stat_dict()
2021-12-26 19:15:47 +01:00
modimpls = nt.get_modimpls_dict()
2021-12-24 00:08:25 +01:00
nbetuds = len(nt.etud_moy_gen_ranks)
2021-02-01 16:23:11 +01:00
mg = scu.fmt_note(nt.get_etud_moy_gen(etudid))
2020-09-26 16:19:37 +02:00
if (
nt.get_moduleimpls_attente()
or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
2020-09-26 16:19:37 +02:00
):
# n'affiche pas le rang sur le bulletin s'il y a des
# notes en attente dans ce semestre
rang = ""
rang_gr = {}
ninscrits_gr = {}
else:
rang = str(nt.get_etud_rang(etudid))
rang_gr, ninscrits_gr, gr_name = sco_bulletins.get_etud_rangs_groups(
etudid, formsemestre_id, partitions, partitions_etud_groups, nt
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"note",
value=mg,
min=scu.fmt_note(nt.moy_min),
max=scu.fmt_note(nt.moy_max),
moy=scu.fmt_note(nt.moy_moy),
)
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
doc.append(Element("rang", value=str(rang), ninscrits=str(nbetuds)))
2020-09-26 16:19:37 +02:00
if rang_gr:
for partition in partitions:
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"rang_group",
2021-08-22 21:55:01 +02:00
group_type=partition["partition_name"] or "",
group_name=gr_name[partition["partition_id"]] or "",
2021-07-10 16:01:37 +02:00
value=str(rang_gr[partition["partition_id"]]),
ninscrits=str(ninscrits_gr[partition["partition_id"]]),
)
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
doc.append(Element("note_max", value="20")) # notes toujours sur 20
2022-01-25 10:45:13 +01:00
doc.append(
Element(
"bonus_sport_culture",
value=str(nt.bonus[etudid] if nt.bonus is not None else 0.0),
)
)
2020-09-26 16:19:37 +02:00
# Liste les UE / modules /evals
for ue in ues:
ue_status = nt.get_etud_ue_status(etudid, ue["ue_id"])
2021-07-10 16:01:37 +02:00
x_ue = Element(
"ue",
2021-08-22 17:18:15 +02:00
id=str(ue["ue_id"]),
2021-02-01 16:23:11 +01:00
numero=scu.quote_xml_attr(ue["numero"]),
acronyme=scu.quote_xml_attr(ue["acronyme"]),
titre=scu.quote_xml_attr(ue["titre"]),
code_apogee=scu.quote_xml_attr(ue["code_apogee"]),
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
doc.append(x_ue)
2021-02-01 16:23:11 +01:00
if ue["type"] != sco_codes_parcours.UE_SPORT:
2022-02-12 22:57:46 +01:00
v = ue_status["cur_moy_ue"] if ue_status else ""
2020-09-26 16:19:37 +02:00
else:
2022-01-25 10:45:13 +01:00
v = nt.bonus[etudid] if nt.bonus is not None else 0.0
2021-07-10 16:01:37 +02:00
x_ue.append(
Element(
"note",
value=scu.fmt_note(v),
min=scu.fmt_note(ue["min"]),
max=scu.fmt_note(ue["max"]),
)
2021-02-01 16:23:11 +01:00
)
2020-09-26 16:19:37 +02:00
try:
ects_txt = str(int(ue["ects"]))
2021-07-10 16:01:37 +02:00
except (ValueError, TypeError):
2020-09-26 16:19:37 +02:00
ects_txt = ""
2021-07-10 16:01:37 +02:00
x_ue.append(Element("ects", value=ects_txt))
2022-02-09 23:22:00 +01:00
rang, effectif = nt.get_etud_ue_rang(ue["ue_id"], etudid)
x_ue.append(Element("rang", value=str(rang)))
x_ue.append(Element("effectif", value=str(effectif)))
2020-09-26 16:19:37 +02:00
# Liste les modules de l'UE
ue_modimpls = [mod for mod in modimpls if mod["module"]["ue_id"] == ue["ue_id"]]
for modimpl in ue_modimpls:
2021-02-01 16:23:11 +01:00
mod_moy = scu.fmt_note(
nt.get_etud_mod_moy(modimpl["moduleimpl_id"], etudid)
)
2020-09-26 16:19:37 +02:00
if mod_moy == "NI": # ne mentionne pas les modules ou n'est pas inscrit
continue
mod = modimpl["module"]
# if mod['ects'] is None:
# ects = ''
# else:
# ects = str(mod['ects'])
2021-07-10 16:01:37 +02:00
x_mod = Element(
"module",
2021-08-22 17:18:15 +02:00
id=str(modimpl["moduleimpl_id"]),
code=str(mod["code"] or ""),
2021-07-10 16:01:37 +02:00
coefficient=str(mod["coefficient"]),
numero=str(mod["numero"]),
2021-02-01 16:23:11 +01:00
titre=scu.quote_xml_attr(mod["titre"]),
abbrev=scu.quote_xml_attr(mod["abbrev"]),
code_apogee=scu.quote_xml_attr(mod["code_apogee"])
2020-09-26 16:19:37 +02:00
# ects=ects ects des modules maintenant inutilisés
)
2021-07-10 16:01:37 +02:00
x_ue.append(x_mod)
2020-09-26 16:19:37 +02:00
modstat = nt.get_mod_stats(modimpl["moduleimpl_id"])
2021-07-10 16:01:37 +02:00
x_mod.append(
Element(
"note",
value=mod_moy,
min=scu.fmt_note(modstat["min"]),
max=scu.fmt_note(modstat["max"]),
moy=scu.fmt_note(modstat["moy"]),
)
2020-09-26 16:19:37 +02:00
)
if (
sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id)
and nt.mod_rangs is not None
):
2021-07-10 16:01:37 +02:00
x_mod.append(
Element(
"rang",
value=str(nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid]),
)
)
x_mod.append(
Element(
"effectif", value=str(nt.mod_rangs[modimpl["moduleimpl_id"]][1])
)
)
2020-09-26 16:19:37 +02:00
# --- notes de chaque eval:
evals = nt.get_evals_in_mod(modimpl["moduleimpl_id"])
if version != "short":
for e in evals:
2021-08-11 00:36:07 +02:00
if e["visibulletin"] or version == "long":
2021-07-10 16:01:37 +02:00
x_eval = Element(
"evaluation",
2021-02-01 16:23:11 +01:00
jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
heure_debut=ndb.TimetoISO8601(
2020-09-26 16:19:37 +02:00
e["heure_debut"], null_is_empty=True
),
2021-02-01 16:23:11 +01:00
heure_fin=ndb.TimetoISO8601(
e["heure_fin"], null_is_empty=True
),
2021-07-10 16:01:37 +02:00
coefficient=str(e["coefficient"]),
evaluation_type=str(e["evaluation_type"]),
2021-02-01 16:23:11 +01:00
description=scu.quote_xml_attr(e["description"]),
2021-07-10 16:01:37 +02:00
# notes envoyées sur 20, ceci juste pour garder trace:
note_max_origin=str(e["note_max"]),
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
x_mod.append(x_eval)
2020-09-26 16:19:37 +02:00
val = e["notes"].get(etudid, {"value": "NP"})[
"value"
] # NA si etud demissionnaire
2021-02-01 16:23:11 +01:00
val = scu.fmt_note(val, note_max=e["note_max"])
2021-07-10 16:01:37 +02:00
x_eval.append(Element("note", value=val))
2020-09-26 16:19:37 +02:00
# Evaluations incomplètes ou futures:
complete_eval_ids = set([e["evaluation_id"] for e in evals])
2021-06-13 23:37:14 +02:00
if sco_preferences.get_preference(
"bul_show_all_evals", formsemestre_id
2021-06-13 23:37:14 +02:00
):
all_evals = sco_evaluation_db.do_evaluation_list(
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
2020-09-26 16:19:37 +02:00
)
all_evals.reverse() # plus ancienne d'abord
for e in all_evals:
if e["evaluation_id"] not in complete_eval_ids:
2021-07-10 16:01:37 +02:00
x_eval = Element(
"evaluation",
2021-02-01 16:23:11 +01:00
jour=ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
heure_debut=ndb.TimetoISO8601(
2020-09-26 16:19:37 +02:00
e["heure_debut"], null_is_empty=True
),
2021-02-01 16:23:11 +01:00
heure_fin=ndb.TimetoISO8601(
2020-09-26 16:19:37 +02:00
e["heure_fin"], null_is_empty=True
),
2021-07-10 16:01:37 +02:00
coefficient=str(e["coefficient"]),
2021-02-01 16:23:11 +01:00
description=scu.quote_xml_attr(e["description"]),
2020-09-26 16:19:37 +02:00
incomplete="1",
2021-07-10 16:01:37 +02:00
# notes envoyées sur 20, ceci juste pour garder trace:
2021-08-22 21:55:01 +02:00
note_max_origin=str(e["note_max"] or ""),
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
x_mod.append(x_eval)
2020-09-26 16:19:37 +02:00
# UE capitalisee (listee seulement si meilleure que l'UE courante)
if ue_status["is_capitalized"]:
try:
ects_txt = str(int(ue_status["ue"].get("ects", "")))
2021-07-10 16:01:37 +02:00
except (ValueError, TypeError):
2020-09-26 16:19:37 +02:00
ects_txt = ""
2021-07-10 16:01:37 +02:00
x_ue = Element(
"ue_capitalisee",
2021-08-22 17:18:15 +02:00
id=str(ue["ue_id"]),
2021-02-01 16:23:11 +01:00
numero=scu.quote_xml_attr(ue["numero"]),
acronyme=scu.quote_xml_attr(ue["acronyme"]),
titre=scu.quote_xml_attr(ue["titre"]),
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
doc.append(x_ue)
x_ue.append(Element("note", value=scu.fmt_note(ue_status["moy"])))
x_ue.append(Element("ects", value=ects_txt))
x_ue.append(
Element("coefficient_ue", value=scu.fmt_note(ue_status["coef_ue"]))
)
x_ue.append(
Element(
"date_capitalisation",
value=ndb.DateDMYtoISO(ue_status["event_date"]),
)
)
2020-09-26 16:19:37 +02:00
# --- Absences
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
2021-07-19 19:53:01 +02:00
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
2021-08-22 17:18:15 +02:00
doc.append(Element("absences", nbabs=str(nbabs), nbabsjust=str(nbabsjust)))
2020-09-26 16:19:37 +02:00
# --- Decision Jury
if (
sco_preferences.get_preference("bul_show_decision", formsemestre_id)
2020-09-26 16:19:37 +02:00
or xml_with_decisions
):
infos, dpv = sco_bulletins.etud_descr_situation_semestre(
etudid,
formsemestre_id,
format="xml",
2021-06-13 23:37:14 +02:00
show_uevalid=sco_preferences.get_preference(
"bul_show_uevalid", formsemestre_id
2021-06-13 23:37:14 +02:00
),
2020-09-26 16:19:37 +02:00
)
2021-07-10 16:01:37 +02:00
x_situation = Element("situation")
x_situation.text = scu.quote_xml_attr(infos["situation"])
doc.append(x_situation)
2020-09-26 16:19:37 +02:00
if dpv:
decision = dpv["decisions"][0]
etat = decision["etat"]
if decision["decision_sem"]:
2021-08-22 21:55:01 +02:00
code = decision["decision_sem"]["code"] or ""
2020-09-26 16:19:37 +02:00
else:
code = ""
if (
decision["decision_sem"]
and "compense_formsemestre_id" in decision["decision_sem"]
):
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"decision",
code=code,
etat=str(etat),
2021-08-24 05:59:19 +02:00
compense_formsemestre_id=str(
decision["decision_sem"]["compense_formsemestre_id"] or ""
),
2021-07-10 16:01:37 +02:00
)
2020-09-26 16:19:37 +02:00
)
else:
2021-07-10 16:01:37 +02:00
doc.append(Element("decision", code=code, etat=str(etat)))
2020-09-26 16:19:37 +02:00
if decision[
"decisions_ue"
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
2020-09-26 16:19:37 +02:00
for ue_id in decision["decisions_ue"].keys():
2021-10-17 23:19:26 +02:00
ue = sco_edit_ue.ue_list({"ue_id": ue_id})[0]
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"decision_ue",
2021-08-22 17:18:15 +02:00
ue_id=str(ue["ue_id"]),
2021-07-10 16:01:37 +02:00
numero=scu.quote_xml_attr(ue["numero"]),
acronyme=scu.quote_xml_attr(ue["acronyme"]),
titre=scu.quote_xml_attr(ue["titre"]),
code=decision["decisions_ue"][ue_id]["code"],
)
2020-09-26 16:19:37 +02:00
)
for aut in decision["autorisations"]:
2021-07-10 16:01:37 +02:00
doc.append(
Element(
"autorisation_inscription", semestre_id=str(aut["semestre_id"])
)
)
2020-09-26 16:19:37 +02:00
else:
2021-07-10 16:01:37 +02:00
doc.append(Element("decision", code="", etat="DEM"))
2020-09-26 16:19:37 +02:00
# --- Appreciations
2021-06-15 13:59:56 +02:00
cnx = ndb.GetDBConnexion()
apprecs = sco_etud.appreciations_list(
2020-09-26 16:19:37 +02:00
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
)
2021-07-10 17:40:40 +02:00
for appr in apprecs:
x_appr = Element(
"appreciation",
date=ndb.DateDMYtoISO(appr["date"]),
2021-02-01 16:23:11 +01:00
)
2021-07-10 17:40:40 +02:00
x_appr.text = scu.quote_xml_attr(appr["comment"])
doc.append(x_appr)
if is_appending:
return None
else:
2021-07-12 23:34:18 +02:00
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)