460 lines
14 KiB
Python
460 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.
|
|
"""
|
|
import datetime
|
|
from functools import wraps
|
|
import random
|
|
import sys
|
|
import string
|
|
import typing
|
|
|
|
from app import db, log
|
|
from app.auth.models import User
|
|
from app.models import (
|
|
Departement,
|
|
Evaluation,
|
|
Formation,
|
|
FormationModalite,
|
|
Matiere,
|
|
ModuleImpl,
|
|
)
|
|
from app.scodoc import notesdb as ndb
|
|
from app.scodoc import codes_cursus
|
|
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_evaluation_db
|
|
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.scodoc.sco_exceptions import ScoValueError
|
|
from config import Config, TestConfig
|
|
|
|
|
|
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 !')
|
|
self.dept = Departement.query.filter_by(acronym=TestConfig.DEPT_TEST).first()
|
|
assert self.dept
|
|
|
|
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: int = codes_cursus.CursusDUT.TYPE_CURSUS,
|
|
formation_code=None,
|
|
code_specialite=None,
|
|
) -> int:
|
|
"""Crée une formation"""
|
|
if not acronyme:
|
|
acronyme = "TEST" + str(random.randint(100000, 999999))
|
|
formation = Formation(
|
|
acronyme=scu.strip_str(acronyme),
|
|
titre=scu.strip_str(titre),
|
|
titre_officiel=scu.strip_str(titre_officiel),
|
|
type_parcours=type_parcours,
|
|
formation_code=scu.strip_str(formation_code),
|
|
code_specialite=scu.strip_str(code_specialite),
|
|
dept_id=self.dept.id,
|
|
)
|
|
db.session.add(formation)
|
|
db.session.commit()
|
|
return formation.id
|
|
|
|
@logging_meth
|
|
def create_ue(
|
|
self,
|
|
formation_id=None,
|
|
acronyme=None,
|
|
numero=0,
|
|
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=0) -> 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=0,
|
|
abbrev=None,
|
|
ects=None,
|
|
code_apogee=None,
|
|
module_type=scu.ModuleType.STANDARD,
|
|
) -> int:
|
|
matiere = db.session.get(Matiere, 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="",
|
|
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,)
|
|
titre = titre or "sans titre"
|
|
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,
|
|
date_debut: datetime.datetime = None,
|
|
date_fin: datetime.datetime = None,
|
|
description=None,
|
|
note_max=20,
|
|
coefficient=None,
|
|
visibulletin=None,
|
|
publish_incomplete=None,
|
|
evaluation_type=None,
|
|
numero=None,
|
|
) -> dict:
|
|
args = locals()
|
|
del args["self"]
|
|
moduleimpl: ModuleImpl = db.session.get(ModuleImpl, moduleimpl_id)
|
|
assert moduleimpl
|
|
evaluation: Evaluation = Evaluation.create(moduleimpl=moduleimpl, **args)
|
|
db.session.add(evaluation)
|
|
db.session.commit()
|
|
eval_dict = evaluation.to_dict()
|
|
eval_dict["id"] = evaluation.id
|
|
return eval_dict
|
|
|
|
@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,
|
|
date_debut=datetime.datetime.strptime(date_debut, "%d/%m/%Y"),
|
|
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["evaluation_id"],
|
|
etudid=etud["etudid"],
|
|
note=notes[idx % len(notes)],
|
|
)
|
|
|
|
def set_code_jury(
|
|
self,
|
|
formsemestre_id,
|
|
etud,
|
|
code_etat=codes_cursus.ADM,
|
|
devenir=codes_cursus.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,
|
|
)
|