ScoDoc/tests/unit/sco_fake_gen.py
2022-12-13 10:12:19 +01:00

444 lines
14 KiB
Python

# -*- mode: python -*-
# -*- coding: utf-8 -*-
"""Creation environnement pour test.
A utiliser avec debug.py (côté serveur).
La classe ScoFake offre un ensemble de raccourcis permettant d'écrire
facilement des tests ou de reproduire des bugs.
"""
from functools import wraps
import random
import sys
import string
import typing
from config import Config
from app.auth.models import User
from app.models import FormationModalite, Matiere
from app.scodoc import notesdb as ndb
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_edit_formation
from app.scodoc import sco_edit_matiere
from app.scodoc import sco_edit_module
from app.scodoc import sco_edit_ue
from app.scodoc import sco_etud
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
from app.scodoc import sco_formsemestre_inscriptions
from app.scodoc import sco_formsemestre_validation
from app.scodoc import sco_moduleimpl
from app.scodoc import sco_saisie_notes
from app.scodoc import sco_synchro_etuds
from app.scodoc import sco_utils as scu
from app import log
from app.scodoc.sco_exceptions import ScoValueError
from tests.unit.setup import NOTES_T
random.seed(12345) # tests reproductibles
NOMS_DIR = Config.SCODOC_DIR + "/tools/fakeportal/nomsprenoms"
NOMS = [
x.strip()
for x in open(NOMS_DIR + "/noms.txt", encoding=scu.SCO_ENCODING).readlines()
]
PRENOMS_H = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-h.txt", encoding=scu.SCO_ENCODING).readlines()
]
PRENOMS_F = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-f.txt", encoding=scu.SCO_ENCODING).readlines()
]
PRENOMS_X = [
x.strip()
for x in open(NOMS_DIR + "/prenoms-x.txt", encoding=scu.SCO_ENCODING).readlines()
]
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
return "".join(random.choice(chars) for _ in range(size))
def logging_meth(func):
@wraps(func)
def wrapper_logging_meth(self, *args, **kwargs):
r = func(self, *args, **kwargs)
# self.log("%s(%s) -> \n%s" % (func.__name__, kwargs, pprint.pformat(r)))
return r
return wrapper_logging_meth
class ScoFake(object):
"""Helper for ScoDoc tests"""
def __init__(self, verbose=True):
self.verbose = verbose
self.default_user = User.query.filter_by(user_name="bach").first()
if not self.default_user:
raise ScoValueError('User test "bach" not found !')
def log(self, msg):
if self.verbose:
print("ScoFake: " + str(msg), file=sys.stderr)
sys.stderr.flush()
log("ScoFake: " + str(msg))
def civilitenomprenom(self):
"""un nom et un prenom au hasard,
toujours en majuscules.
"""
civilite = random.choice(("M", "M", "M", "F", "F", "F", "X"))
if civilite == "F":
prenom = random.choice(PRENOMS_F)
elif civilite == "M":
prenom = random.choice(PRENOMS_H)
elif civilite == "X":
prenom = random.choice(PRENOMS_X)
else:
raise ValueError("invalid civilite value")
return civilite, random.choice(NOMS).upper(), prenom.upper()
@logging_meth
def create_etud(
self,
cnx=None,
code_nip="",
nom="",
prenom="",
code_ine="",
civilite="",
etape="TST1",
email="test@localhost",
emailperso="perso@localhost",
date_naissance="01/01/2001",
lieu_naissance="Paris",
dept_naissance="75",
domicile="1, rue du test",
codepostaldomicile="75123",
villedomicile="TestCity",
paysdomicile="France",
telephone="0102030405",
typeadresse="domicile",
boursier=None,
description="etudiant test",
):
"""Crée un étudiant"""
if not cnx:
cnx = ndb.GetDBConnexion()
if code_nip == "":
code_nip = str(random.randint(10000, 99999))
if not civilite or not nom or not prenom:
r_civilite, r_nom, r_prenom = self.civilitenomprenom()
if not civilite:
civilite = r_civilite
if not nom:
nom = r_nom
if not prenom:
prenom = r_prenom
etud = sco_etud.create_etud(cnx, args=locals())
inscription = "2020" # pylint: disable=possibly-unused-variable
sco_synchro_etuds.do_import_etud_admission(cnx, etud["etudid"], locals())
return etud
@logging_meth
def create_formation(
self,
acronyme="test",
titre="Formation test",
titre_officiel="Le titre officiel de la formation test",
type_parcours=sco_codes_parcours.ParcoursDUT.TYPE_PARCOURS,
formation_code=None,
code_specialite=None,
) -> int:
"""Crée une formation"""
if not acronyme:
acronyme = "TEST" + str(random.randint(100000, 999999))
oid = sco_edit_formation.do_formation_create(locals())
oids = sco_formations.formation_list(formation_id=oid)
if not oids:
raise ScoValueError("formation not created !")
return oid
@logging_meth
def create_ue(
self,
formation_id=None,
acronyme=None,
numero=None,
titre="",
type=None,
ue_code=None,
ects=None,
is_external=None,
code_apogee=None,
coefficient=None,
semestre_idx=None,
) -> int:
"""Crée une UE.
return: ue_id
"""
if numero is None:
numero = sco_edit_ue.next_ue_numero(formation_id, 0)
oid = sco_edit_ue.do_ue_create(locals())
oids = sco_edit_ue.ue_list(args={"ue_id": oid})
if not oids:
raise ScoValueError("ue not created !")
return oid
@logging_meth
def create_matiere(self, ue_id=None, titre=None, numero=None) -> int:
oid = sco_edit_matiere.do_matiere_create(locals())
oids = sco_edit_matiere.matiere_list(args={"matiere_id": oid})
if not oids:
raise ScoValueError("matiere not created !")
return oid
@logging_meth
def create_module(
self,
titre=None,
code=None,
heures_cours=None,
heures_td=None,
heures_tp=None,
coefficient=None,
matiere_id=None,
semestre_id=1,
numero=None,
abbrev=None,
ects=None,
code_apogee=None,
module_type=scu.ModuleType.STANDARD,
) -> int:
matiere = Matiere.query.get(matiere_id)
ue_id = matiere.ue.id
formation_id = matiere.ue.formation.id
oid = sco_edit_module.do_module_create(locals())
oids = sco_edit_module.module_list(args={"module_id": oid})
if not oids:
raise ScoValueError(f"module not created ! (oid={oid})")
return oid
@logging_meth
def create_formsemestre(
self,
formation_id=None,
semestre_id=None,
titre=None,
date_debut=None,
date_fin=None,
etat=None,
gestion_compensation=None,
bul_hide_xml=None,
block_moyennes=None,
block_moyenne_generale=None,
gestion_semestrielle=None,
bul_bgcolor=None,
modalite=FormationModalite.DEFAULT_MODALITE,
resp_can_edit=None,
resp_can_change_ens=None,
ens_can_edit_eval=None,
elt_sem_apo=None,
elt_annee_apo=None,
etapes=None,
responsables=None, # sequence of resp. ids
) -> int:
if responsables is None:
responsables = (self.default_user.id,)
oid = sco_formsemestre.do_formsemestre_create(locals())
oids = sco_formsemestre.do_formsemestre_list(
args={"formsemestre_id": oid}
) # API inconsistency
if not oids:
raise ScoValueError("formsemestre not created !")
return oid
@logging_meth
def create_moduleimpl(
self,
module_id: int = None,
formsemestre_id: int = None,
responsable_id: typing.Optional[int] = None,
) -> int:
if not responsable_id:
responsable_id = self.default_user.id
oid = sco_moduleimpl.do_moduleimpl_create(locals())
oids = sco_moduleimpl.moduleimpl_list(moduleimpl_id=oid) # API inconsistency
if not oids:
raise ScoValueError("moduleimpl not created !")
return oid
@logging_meth
def inscrit_etudiant(self, formsemestre_id: int, etud: dict):
sco_formsemestre_inscriptions.do_formsemestre_inscription_with_modules(
formsemestre_id,
etud["etudid"],
etat="I",
etape=etud.get("etape", None),
method="test_inscrit_etudiant",
)
@logging_meth
def create_evaluation(
self,
moduleimpl_id=None,
jour=None,
heure_debut="8h00",
heure_fin="9h00",
description=None,
note_max=20,
coefficient=None,
visibulletin=None,
publish_incomplete=None,
evaluation_type=None,
numero=None,
):
args = locals()
del args["self"]
oid = sco_evaluation_db.do_evaluation_create(**args)
oids = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": oid})
if not oids:
raise ScoValueError("evaluation not created !")
return oids[0]
@logging_meth
def create_note(
self,
evaluation_id: int = None,
etudid: int = None,
note=None,
comment: str = None,
user: User = None, # User instance
):
if user is None:
user = self.default_user
return sco_saisie_notes.notes_add(
user,
evaluation_id,
[(etudid, note)],
comment=comment,
)
def setup_formation(
self,
nb_semestre=1,
nb_ue_per_semestre=2,
nb_module_per_ue=2,
acronyme=None,
titre=None,
):
"""Création d'une formation, avec UE, modules et évaluations.
Formation avec `nb_semestre` comportant chacun `nb_ue_per_semestre` UE
et dans chaque UE `nb_module_per_ue` modules (on a une seule matière par UE).
Returns:
formation_id, liste d'ue (dicts), liste de modules.
"""
formation_id = self.create_formation(acronyme=acronyme, titre=titre)
ue_ids = []
mod_ids = []
for semestre_id in range(1, nb_semestre + 1):
for n in range(1, nb_ue_per_semestre + 1):
ue_id = self.create_ue(
formation_id=formation_id,
acronyme="TSU%s%s" % (semestre_id, n),
titre="ue test %s%s" % (semestre_id, n),
)
ue_ids.append(ue_id)
matiere_id = self.create_matiere(ue_id=ue_id, titre="matière test")
for _ in range(nb_module_per_ue):
mod = self.create_module(
matiere_id=matiere_id,
semestre_id=semestre_id,
code="TSM%s" % len(mod_ids),
coefficient=1.0,
titre="module test",
)
mod_ids.append(mod)
return formation_id, ue_ids, mod_ids
def setup_formsemestre(
self,
f,
mod_list,
semestre_id=1,
date_debut="01/01/2020",
date_fin="30/06/2020",
nb_evaluations_per_module=1,
titre=None,
responsables=None, # list of users ids
modalite=None,
):
"""Création semestre, avec modules et évaluations."""
formsemestre_id = self.create_formsemestre(
formation_id=f["formation_id"],
semestre_id=semestre_id,
date_debut=date_debut,
date_fin=date_fin,
titre=titre,
responsables=responsables,
modalite=modalite,
)
eval_list = []
for mod in mod_list:
if mod["semestre_id"] == semestre_id:
moduleimpl_id = self.create_moduleimpl(
module_id=mod["module_id"],
formsemestre_id=formsemestre_id,
responsable_id="bach",
)
for e_idx in range(1, nb_evaluations_per_module + 1):
e = self.create_evaluation(
moduleimpl_id=moduleimpl_id,
jour=date_debut,
description="evaluation test %s" % e_idx,
coefficient=1.0,
)
eval_list.append(e)
return formsemestre_id, eval_list
def set_etud_notes_evals(
self, eval_list: list[dict], etuds: list[dict], notes=None
):
"""Met des notes aux étudiants indiqués des evals indiquées.
Args:
notes: liste des notes (float).
Si non spécifié, utilise la liste NOTES_T
"""
if notes is None:
notes = NOTES_T
for e in eval_list:
for idx, etud in enumerate(etuds):
self.create_note(
evaluation_id=e["id"],
etudid=etud["etudid"],
note=notes[idx % len(notes)],
)
def set_code_jury(
self,
formsemestre_id,
etud,
code_etat=sco_codes_parcours.ADM,
devenir=sco_codes_parcours.NEXT,
assidu=True,
):
"""Affecte décision de jury"""
sco_formsemestre_validation.formsemestre_validation_etud_manu(
formsemestre_id=formsemestre_id,
etudid=etud["etudid"],
code_etat=code_etat,
devenir=devenir,
assidu=assidu,
)