Backend 'UniteEns': utilise uniquement modèles. + code modernization.

This commit is contained in:
ilona 2024-10-18 23:37:16 +02:00
parent ae02dc5821
commit 612f95efaa
10 changed files with 62 additions and 111 deletions

View File

@ -49,7 +49,6 @@ from app.models import (
) )
from app.models import ApcValidationRCUE, ScolarFormSemestreValidation, ScolarEvent from app.models import ApcValidationRCUE, ScolarFormSemestreValidation, ScolarEvent
from app.models import ScolarNews from app.models import ScolarNews
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType from app.scodoc.sco_utils import ModuleType
from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.TrivialFormulator import TrivialFormulator
@ -65,54 +64,13 @@ from app.scodoc import sco_edit_apc
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_moduleimpl from app.scodoc import sco_moduleimpl
_ueEditor = ndb.EditableTable(
"notes_ue",
"ue_id",
(
"ue_id",
"formation_id",
"acronyme",
"numero",
"titre",
"semestre_idx",
"type",
"ue_code",
"ects",
"is_external",
"code_apogee",
"code_apogee_rcue",
"coefficient",
"coef_rcue",
"color",
"niveau_competence_id",
),
convert_empty_to_nulls=False, # necessaire pour ue_code == ""
sortkey="numero",
input_formators={
"type": ndb.int_null_is_zero,
"is_external": scu.to_bool,
"ects": ndb.float_null_is_null,
},
output_formators={
"numero": ndb.int_null_is_zero,
"ects": ndb.float_null_is_null,
"coefficient": ndb.float_null_is_zero,
"semestre_idx": ndb.int_null_is_null,
},
)
def do_ue_create(args, allow_empty_ue_code=False) -> UniteEns:
def ue_list(*args, **kw):
"list UEs"
cnx = ndb.GetDBConnexion()
return _ueEditor.list(cnx, *args, **kw)
def do_ue_create(args, allow_empty_ue_code=False):
"create an ue" "create an ue"
cnx = ndb.GetDBConnexion()
# check duplicates # check duplicates
ues = ue_list({"formation_id": args["formation_id"], "acronyme": args["acronyme"]}) ues = UniteEns.query.filter_by(
formation_id=args["formation_id"], acronyme=args["acronyme"]
).all()
if ues: if ues:
raise ScoValueError( raise ScoValueError(
f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé ! f"""Acronyme d'UE "{args['acronyme']}" déjà utilisé !
@ -138,21 +96,21 @@ def do_ue_create(args, allow_empty_ue_code=False):
args["coefficient"] = None args["coefficient"] = None
# create # create
# XXX TODO utiliser UniteEns.create_from_dict ue = UniteEns.create_from_dict(args)
ue_id = _ueEditor.create(cnx, args) db.session.commit()
log(f"do_ue_create: created {ue_id} with {args}") log(f"do_ue_create: created {ue} with {args}")
formation: Formation = db.session.get(Formation, args["formation_id"]) # caches
formation.invalidate_module_coefs() ue.formation.invalidate_module_coefs()
ue.formation.invalidate_cached_sems()
# news # news
formation = db.session.get(Formation, args["formation_id"])
ScolarNews.add( ScolarNews.add(
typ=ScolarNews.NEWS_FORM, typ=ScolarNews.NEWS_FORM,
obj=args["formation_id"], obj=args["formation_id"],
text=f"Modification de la formation {formation.acronyme}", text=f"Modification de la formation {ue.formation.acronyme}",
) )
formation.invalidate_cached_sems()
return ue_id return ue
def do_ue_delete(ue: UniteEns, delete_validations=False, force=False): def do_ue_delete(ue: UniteEns, delete_validations=False, force=False):
@ -544,13 +502,13 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
tf[2]["numero"] = next_ue_numero( tf[2]["numero"] = next_ue_numero(
formation_id, int(tf[2]["semestre_idx"]) formation_id, int(tf[2]["semestre_idx"])
) )
ue_id = do_ue_create(tf[2]) ue = do_ue_create(tf[2])
matiere_id = None matiere_id = None
if is_apc or cursus.UE_IS_MODULE or tf[2]["create_matiere"]: if is_apc or cursus.UE_IS_MODULE or tf[2]["create_matiere"]:
# rappel: en APC, toutes les UE ont une matière, créée ici # rappel: en APC, toutes les UE ont une matière, créée ici
# (inutilisée mais à laquelle les modules sont rattachés) # (inutilisée mais à laquelle les modules sont rattachés)
matiere = Matiere.create_from_dict( matiere = Matiere.create_from_dict(
{"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1} {"ue_id": ue.id, "titre": tf[2]["titre"], "numero": 1}
) )
matiere_id = matiere.id matiere_id = matiere.id
if cursus.UE_IS_MODULE: if cursus.UE_IS_MODULE:
@ -561,14 +519,13 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
"code": tf[2]["acronyme"], "code": tf[2]["acronyme"],
# tous les modules auront coef 1, et on utilisera les ECTS: # tous les modules auront coef 1, et on utilisera les ECTS:
"coefficient": 1.0, "coefficient": 1.0,
"ue_id": ue_id, "ue_id": ue.id,
"matiere_id": matiere_id, "matiere_id": matiere_id,
"formation_id": formation_id, "formation_id": formation_id,
"semestre_id": tf[2]["semestre_idx"], "semestre_id": tf[2]["semestre_idx"],
}, },
) )
db.session.commit() db.session.commit()
ue = db.session.get(UniteEns, ue_id)
flash(f"UE créée (code {ue.ue_code})") flash(f"UE créée (code {ue.ue_code})")
else: else:
if not tf[2]["numero"]: if not tf[2]["numero"]:
@ -1118,12 +1075,12 @@ def _ue_table_ues(
ue.code_apogee or "" ue.code_apogee or ""
}</span>""" }</span>"""
if cur_ue_semestre_id != ue.semestre_id: if cur_ue_semestre_id != ue.get_semestre_id():
cur_ue_semestre_id = ue.semestre_id cur_ue_semestre_id = ue.get_semestre_id()
if ue.semestre_id == codes_cursus.UE_SEM_DEFAULT: if ue.semestre_idx == codes_cursus.UE_SEM_DEFAULT:
lab = "Pas d'indication de semestre:" lab = "Pas d'indication de semestre:"
else: else:
lab = f"""Semestre {ue.semestre_id}:""" lab = f"""Semestre {ue.get_semestre_id()}:"""
H.append( H.append(
f'<div class="ue_list_div"><div class="ue_list_tit_sem">{lab}</div>' f'<div class="ue_list_div"><div class="ue_list_tit_sem">{lab}</div>'
) )
@ -1205,8 +1162,8 @@ def _ue_table_ues(
H.append( H.append(
f"""</ul><ul><li><a href="{ f"""</ul><ul><li><a href="{
url_for('notes.ue_create', scodoc_dept=g.scodoc_dept, url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
formation_id=ue.formation_id, semestre_idx=ue.semestre_id) formation_id=ue.formation_id, semestre_idx=ue.get_semestre_id())
}">Ajouter une UE dans le semestre {ue.semestre_id or ''}</a></li></ul> }">Ajouter une UE dans le semestre {ue.get_semestre_id() or ''}</a></li></ul>
</div> </div>
""" """
) )

View File

@ -350,16 +350,15 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
) )
# Note: si le code est indiqué "" dans le xml, il faut le conserver vide # Note: si le code est indiqué "" dans le xml, il faut le conserver vide
# pour la comparaison ultérieure des formations XXX # pour la comparaison ultérieure des formations XXX
ue_id = edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True) ue = edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True)
ue: UniteEns = db.session.get(UniteEns, ue_id)
assert ue assert ue
if xml_ue_id: if xml_ue_id:
ues_old2new[xml_ue_id] = ue_id ues_old2new[xml_ue_id] = ue.id
# élément optionnel présent dans les exports BUT: # élément optionnel présent dans les exports BUT:
ue_reference = ue_info[1].get("reference") ue_reference = ue_info[1].get("reference")
if ue_reference: if ue_reference:
ue_reference_to_id[int(ue_reference)] = ue_id ue_reference_to_id[int(ue_reference)] = ue.id
# -- Create matieres # -- Create matieres
for mat_info in ue_info[2]: for mat_info in ue_info[2]:
@ -397,7 +396,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
continue continue
assert mat_info[0] == "matiere" assert mat_info[0] == "matiere"
mat_info[1]["ue_id"] = ue_id mat_info[1]["ue_id"] = ue.id
mat = Matiere.create_from_dict(mat_info[1]) mat = Matiere.create_from_dict(mat_info[1])
mat_id = mat.id mat_id = mat.id
# -- create modules # -- create modules
@ -410,7 +409,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
xml_module_id = None xml_module_id = None
mod_info[1]["formation_id"] = formation.id mod_info[1]["formation_id"] = formation.id
mod_info[1]["matiere_id"] = mat_id mod_info[1]["matiere_id"] = mat_id
mod_info[1]["ue_id"] = ue_id mod_info[1]["ue_id"] = ue.id
if not "module_type" in mod_info[1]: if not "module_type" in mod_info[1]:
mod_info[1]["module_type"] = scu.ModuleType.STANDARD mod_info[1]["module_type"] = scu.ModuleType.STANDARD
module = Module.create_from_dict( module = Module.create_from_dict(

View File

@ -19,6 +19,7 @@ from operator import attrgetter
from flask_login import current_user from flask_login import current_user
from flask import abort, flash, g, url_for from flask import abort, flash, g, url_for
from flask_sqlalchemy.query import Query
from sqlalchemy.sql import text from sqlalchemy.sql import text
from sqlalchemy import func from sqlalchemy import func
@ -799,7 +800,7 @@ class FormSemestre(models.ScoDocModel):
@classmethod @classmethod
def get_dept_formsemestres_courants( def get_dept_formsemestres_courants(
cls, dept: Departement, date_courante: datetime.datetime | None = None cls, dept: Departement, date_courante: datetime.datetime | None = None
) -> db.Query: ) -> Query:
"""Liste (query) ordonnée des formsemestres courants, c'est """Liste (query) ordonnée des formsemestres courants, c'est
à dire contenant la date courant (si None, la date actuelle)""" à dire contenant la date courant (si None, la date actuelle)"""
date_courante = date_courante or db.func.current_date() date_courante = date_courante or db.func.current_date()

View File

@ -120,7 +120,7 @@ class UniteEns(models.ScoDocModel):
if "is_external" in args: if "is_external" in args:
args["is_external"] = scu.to_bool(args["is_external"]) args["is_external"] = scu.to_bool(args["is_external"])
if "ects" in args: if "ects" in args:
args["ects"] = float(args["ects"]) args["ects"] = None if args["ects"] is None else float(args["ects"])
return args return args
@ -190,7 +190,7 @@ class UniteEns(models.ScoDocModel):
utilisée dans un formsemestre verrouillé ou validations de jury de cette UE. utilisée dans un formsemestre verrouillé ou validations de jury de cette UE.
Renvoie aussi une explication. Renvoie aussi une explication.
""" """
from app.models import FormSemestre, ModuleImpl, ScolarFormSemestreValidation from app.models import ModuleImpl, ScolarFormSemestreValidation
# before 9.7.23: contains modules used in a locked formsemestre # before 9.7.23: contains modules used in a locked formsemestre
# starting from 9.7.23: + existence de validations de jury de cette UE # starting from 9.7.23: + existence de validations de jury de cette UE

View File

@ -495,16 +495,16 @@ def dict_decision_jury(
# and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id):
# always publish (car utile pour export Apogee) # always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys(): for ue_id in decision["decisions_ue"].keys():
ue = edit_ue.ue_list({"ue_id": ue_id})[0] ue = UniteEns.get_ue(ue_id)
d["decision_ue"].append( d["decision_ue"].append(
dict( {
ue_id=ue["ue_id"], "ue_id": ue.ue_id,
numero=ue["numero"], "numero": ue.numero,
acronyme=ue["acronyme"], "acronyme": ue.acronyme,
titre=ue["titre"], "titre": ue.titre,
code=decision["decisions_ue"][ue_id]["code"], "code": decision["decisions_ue"][ue.id]["code"],
ects=ue["ects"] or "", "ects": ue.ects or "",
) }
) )
d["autorisation_inscription"] = [] d["autorisation_inscription"] = []
for aut in decision["autorisations"]: for aut in decision["autorisations"]:
@ -515,7 +515,7 @@ def dict_decision_jury(
) )
) )
else: else:
d["decision"] = dict(code="", etat="DEM") d["decision"] = {"code": "", "etat": "DEM"}
# Ajout jury BUT: # Ajout jury BUT:
if formsemestre.formation.is_apc(): if formsemestre.formation.is_apc():
d.update(but_validations.dict_decision_jury(etud, formsemestre)) d.update(but_validations.dict_decision_jury(etud, formsemestre))

View File

@ -51,7 +51,7 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
from app import log from app import log
from app.but.bulletin_but_xml_compat import bulletin_but_xml_compat from app.but.bulletin_but_xml_compat import bulletin_but_xml_compat
from app.models import BulAppreciations, Evaluation, FormSemestre from app.models import BulAppreciations, Evaluation, FormSemestre, UniteEns
from app.scodoc import sco_assiduites from app.scodoc import sco_assiduites
from app.scodoc import codes_cursus from app.scodoc import codes_cursus
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
@ -389,19 +389,19 @@ def make_xml_formsemestre_bulletinetud(
else: else:
doc.append(Element("decision", code=code, etat=str(etat))) doc.append(Element("decision", code=code, etat=str(etat)))
if decision[ if decision["decisions_ue"]:
"decisions_ue" # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id):
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee) # always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys(): for ue_id in decision["decisions_ue"].keys():
ue = edit_ue.ue_list({"ue_id": ue_id})[0] ue = UniteEns.get_ue(ue_id)
doc.append( doc.append(
Element( Element(
"decision_ue", "decision_ue",
ue_id=str(ue["ue_id"]), ue_id=str(ue.id),
numero=quote_xml_attr(ue["numero"]), numero=quote_xml_attr(ue.numero),
acronyme=quote_xml_attr(ue["acronyme"]), acronyme=quote_xml_attr(ue.acronyme),
titre=quote_xml_attr(ue["titre"]), titre=quote_xml_attr(ue.titre or ""),
code=decision["decisions_ue"][ue_id]["code"], code=decision["decisions_ue"][ue.id]["code"],
) )
) )

View File

@ -453,7 +453,7 @@ def _ue_form_description(
return descr return descr
def _check_values(formsemestre: FormSemestre, ue_list, values): def _check_values(formsemestre: FormSemestre, ue_list: list[UniteEns], values):
"""Check that form values are ok """Check that form values are ok
for each UE: for each UE:
code != None => note and coef code != None => note and coef

View File

@ -96,7 +96,7 @@ def external_ue_create(
formation_id = formsemestre.formation.id formation_id = formsemestre.formation.id
numero = edit_ue.next_ue_numero(formation_id, semestre_id=formsemestre.semestre_id) numero = edit_ue.next_ue_numero(formation_id, semestre_id=formsemestre.semestre_id)
ue_id = edit_ue.do_ue_create( ue = edit_ue.do_ue_create(
{ {
"formation_id": formation_id, "formation_id": formation_id,
"semestre_idx": formsemestre.semestre_id, "semestre_idx": formsemestre.semestre_id,
@ -108,10 +108,9 @@ def external_ue_create(
"is_external": True, "is_external": True,
}, },
) )
ue = db.session.get(UniteEns, ue_id)
flash(f"UE créée (code {ue.ue_code})") flash(f"UE créée (code {ue.ue_code})")
matiere = Matiere.create_from_dict( matiere = Matiere.create_from_dict(
{"ue_id": ue_id, "titre": titre or acronyme, "numero": 1} {"ue_id": ue.id, "titre": titre or acronyme, "numero": 1}
) )
module = Module.create_from_dict( module = Module.create_from_dict(
@ -119,7 +118,7 @@ def external_ue_create(
"titre": "UE extérieure", "titre": "UE extérieure",
"code": acronyme, "code": acronyme,
"coefficient": ects, # tous le coef. module est egal à la quantite d'ECTS "coefficient": ects, # tous le coef. module est egal à la quantite d'ECTS
"ue_id": ue_id, "ue_id": ue.id,
"matiere_id": matiere.id, "matiere_id": matiere.id,
"formation_id": formation_id, "formation_id": formation_id,
"semestre_id": formsemestre.semestre_id, "semestre_id": formsemestre.semestre_id,

View File

@ -197,11 +197,8 @@ class ScoFake(object):
""" """
if numero is None: if numero is None:
numero = edit_ue.next_ue_numero(formation_id, 0) numero = edit_ue.next_ue_numero(formation_id, 0)
oid = edit_ue.do_ue_create(locals()) ue = edit_ue.do_ue_create(locals())
oids = edit_ue.ue_list(args={"ue_id": oid}) return ue.id
if not oids:
raise ScoValueError("ue not created !")
return oid
@logging_meth @logging_meth
def create_matiere(self, ue_id=None, titre=None, numero=0) -> int: def create_matiere(self, ue_id=None, titre=None, numero=0) -> int:

View File

@ -51,7 +51,7 @@ from app.formations import (
edit_ue, edit_ue,
formation_io, formation_io,
) )
from app.models import Formation, Matiere, Module, ModuleImpl from app.models import Formation, Matiere, Module, ModuleImpl, UniteEns
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_exceptions from app.scodoc import sco_exceptions
from app.scodoc import sco_formsemestre_edit from app.scodoc import sco_formsemestre_edit
@ -292,11 +292,9 @@ def test_formations(test_client):
li_mat2 = Matiere.query.all() li_mat2 = Matiere.query.all()
assert len(li_mat2) == 3 # verification de la suppression de la matiere assert len(li_mat2) == 3 # verification de la suppression de la matiere
li_ue = edit_ue.ue_list() assert UniteEns.query.count() == 4
assert len(li_ue) == 4
edit_ue.ue_delete(ue_id=uet_id, dialog_confirmed=True) edit_ue.ue_delete(ue_id=uet_id, dialog_confirmed=True)
li_ue2 = edit_ue.ue_list() assert UniteEns.query.count() == 3 # verification de la suppression de l'UE
assert len(li_ue2) == 3 # verification de la suppression de l'UE
# --- Suppression d'une formation # --- Suppression d'une formation
@ -321,9 +319,9 @@ def test_import_formation(test_client, filename="formation-exemple-1.xml"):
assert len(f) == 3 # 3-uple assert len(f) == 3 # 3-uple
formation_id = f[0] formation_id = f[0]
# --- Vérification des UE # --- Vérification des UE
ues = edit_ue.ue_list({"formation_id": formation_id}) ues = UniteEns.query.filter_by(formation_id=formation_id).all()
assert len(ues) == 10 assert len(ues) == 10
assert all(not ue["is_external"] for ue in ues) # aucune UE externe dans le XML assert all(not ue.is_external for ue in ues) # aucune UE externe dans le XML
# --- Mise en place de 4 semestres # --- Mise en place de 4 semestres
formsemestre_ids = [ formsemestre_ids = [
G.create_formsemestre( G.create_formsemestre(