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 ScolarNews
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
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_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 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):
def do_ue_create(args, allow_empty_ue_code=False) -> UniteEns:
"create an ue"
cnx = ndb.GetDBConnexion()
# 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:
raise ScoValueError(
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
# create
# XXX TODO utiliser UniteEns.create_from_dict
ue_id = _ueEditor.create(cnx, args)
log(f"do_ue_create: created {ue_id} with {args}")
ue = UniteEns.create_from_dict(args)
db.session.commit()
log(f"do_ue_create: created {ue} with {args}")
formation: Formation = db.session.get(Formation, args["formation_id"])
formation.invalidate_module_coefs()
# caches
ue.formation.invalidate_module_coefs()
ue.formation.invalidate_cached_sems()
# news
formation = db.session.get(Formation, args["formation_id"])
ScolarNews.add(
typ=ScolarNews.NEWS_FORM,
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):
@ -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(
formation_id, int(tf[2]["semestre_idx"])
)
ue_id = do_ue_create(tf[2])
ue = do_ue_create(tf[2])
matiere_id = None
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
# (inutilisée mais à laquelle les modules sont rattachés)
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
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"],
# tous les modules auront coef 1, et on utilisera les ECTS:
"coefficient": 1.0,
"ue_id": ue_id,
"ue_id": ue.id,
"matiere_id": matiere_id,
"formation_id": formation_id,
"semestre_id": tf[2]["semestre_idx"],
},
)
db.session.commit()
ue = db.session.get(UniteEns, ue_id)
flash(f"UE créée (code {ue.ue_code})")
else:
if not tf[2]["numero"]:
@ -1118,12 +1075,12 @@ def _ue_table_ues(
ue.code_apogee or ""
}</span>"""
if cur_ue_semestre_id != ue.semestre_id:
cur_ue_semestre_id = ue.semestre_id
if ue.semestre_id == codes_cursus.UE_SEM_DEFAULT:
if cur_ue_semestre_id != ue.get_semestre_id():
cur_ue_semestre_id = ue.get_semestre_id()
if ue.semestre_idx == codes_cursus.UE_SEM_DEFAULT:
lab = "Pas d'indication de semestre:"
else:
lab = f"""Semestre {ue.semestre_id}:"""
lab = f"""Semestre {ue.get_semestre_id()}:"""
H.append(
f'<div class="ue_list_div"><div class="ue_list_tit_sem">{lab}</div>'
)
@ -1205,8 +1162,8 @@ def _ue_table_ues(
H.append(
f"""</ul><ul><li><a href="{
url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
formation_id=ue.formation_id, semestre_idx=ue.semestre_id)
}">Ajouter une UE dans le semestre {ue.semestre_id or ''}</a></li></ul>
formation_id=ue.formation_id, semestre_idx=ue.get_semestre_id())
}">Ajouter une UE dans le semestre {ue.get_semestre_id() or ''}</a></li></ul>
</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
# pour la comparaison ultérieure des formations XXX
ue_id = edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True)
ue: UniteEns = db.session.get(UniteEns, ue_id)
ue = edit_ue.do_ue_create(ue_info[1], allow_empty_ue_code=True)
assert ue
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:
ue_reference = ue_info[1].get("reference")
if ue_reference:
ue_reference_to_id[int(ue_reference)] = ue_id
ue_reference_to_id[int(ue_reference)] = ue.id
# -- Create matieres
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
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_id = mat.id
# -- create modules
@ -410,7 +409,7 @@ def formation_import_xml(doc: str | bytes, import_tags=True, use_local_refcomp=F
xml_module_id = None
mod_info[1]["formation_id"] = formation.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]:
mod_info[1]["module_type"] = scu.ModuleType.STANDARD
module = Module.create_from_dict(

View File

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

View File

@ -120,7 +120,7 @@ class UniteEns(models.ScoDocModel):
if "is_external" in args:
args["is_external"] = scu.to_bool(args["is_external"])
if "ects" in args:
args["ects"] = float(args["ects"])
args["ects"] = None if args["ects"] is None else float(args["ects"])
return args
@ -190,7 +190,7 @@ class UniteEns(models.ScoDocModel):
utilisée dans un formsemestre verrouillé ou validations de jury de cette UE.
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
# 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):
# always publish (car utile pour export Apogee)
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(
dict(
ue_id=ue["ue_id"],
numero=ue["numero"],
acronyme=ue["acronyme"],
titre=ue["titre"],
code=decision["decisions_ue"][ue_id]["code"],
ects=ue["ects"] or "",
)
{
"ue_id": ue.ue_id,
"numero": ue.numero,
"acronyme": ue.acronyme,
"titre": ue.titre,
"code": decision["decisions_ue"][ue.id]["code"],
"ects": ue.ects or "",
}
)
d["autorisation_inscription"] = []
for aut in decision["autorisations"]:
@ -515,7 +515,7 @@ def dict_decision_jury(
)
)
else:
d["decision"] = dict(code="", etat="DEM")
d["decision"] = {"code": "", "etat": "DEM"}
# Ajout jury BUT:
if formsemestre.formation.is_apc():
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
from app import log
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 codes_cursus
from app.scodoc import sco_formsemestre
@ -389,19 +389,19 @@ def make_xml_formsemestre_bulletinetud(
else:
doc.append(Element("decision", code=code, etat=str(etat)))
if decision[
"decisions_ue"
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
if decision["decisions_ue"]:
# and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id):
# always publish (car utile pour export Apogee)
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(
Element(
"decision_ue",
ue_id=str(ue["ue_id"]),
numero=quote_xml_attr(ue["numero"]),
acronyme=quote_xml_attr(ue["acronyme"]),
titre=quote_xml_attr(ue["titre"]),
code=decision["decisions_ue"][ue_id]["code"],
ue_id=str(ue.id),
numero=quote_xml_attr(ue.numero),
acronyme=quote_xml_attr(ue.acronyme),
titre=quote_xml_attr(ue.titre or ""),
code=decision["decisions_ue"][ue.id]["code"],
)
)

View File

@ -453,7 +453,7 @@ def _ue_form_description(
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
for each UE:
code != None => note and coef

View File

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

View File

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

View File

@ -51,7 +51,7 @@ from app.formations import (
edit_ue,
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_exceptions
from app.scodoc import sco_formsemestre_edit
@ -292,11 +292,9 @@ def test_formations(test_client):
li_mat2 = Matiere.query.all()
assert len(li_mat2) == 3 # verification de la suppression de la matiere
li_ue = edit_ue.ue_list()
assert len(li_ue) == 4
assert UniteEns.query.count() == 4
edit_ue.ue_delete(ue_id=uet_id, dialog_confirmed=True)
li_ue2 = edit_ue.ue_list()
assert len(li_ue2) == 3 # verification de la suppression de l'UE
assert UniteEns.query.count() == 3 # verification de la suppression de l'UE
# --- 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
formation_id = f[0]
# --- 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 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
formsemestre_ids = [
G.create_formsemestre(