Add get_instance method to all ScoDoc models

This commit is contained in:
Emmanuel Viennet 2024-07-19 09:42:44 +02:00
parent 98127e7c1d
commit 24ccd8f9f7
16 changed files with 117 additions and 25 deletions

View File

@ -5,6 +5,7 @@
from flask import abort, g
import sqlalchemy
import app
from app import db
CODE_STR_LEN = 16 # chaine pour les codes
@ -133,11 +134,22 @@ class ScoDocModel(db.Model):
return None
abort(404, "oid invalide")
query = (
cls.query.filter_by(id=oid, dept_id=g.scodoc_dept_id)
if g.scodoc_dept
else cls.query.filter_by(id=oid)
)
if g.scodoc_dept:
if hasattr(cls, "_sco_dept_relations"):
# Quand dept_id n'est pas dans le modèle courant,
# cet attribut indique la liste des tables à joindre pour
# obtenir le departement.
query = cls.query.filter_by(id=oid)
for relation_name in cls._sco_dept_relations:
query = query.join(getattr(app.models, relation_name))
query = query.filter_by(dept_id=g.scodoc_dept_id)
else:
# département accessible dans le modèle courant
query = cls.query.filter_by(id=oid, dept_id=g.scodoc_dept_id)
else:
# Pas de département courant (API non départementale)
query = cls.query.filter_by(id=oid)
if accept_none:
return query.first()
return query.first_or_404()

View File

@ -7,7 +7,10 @@ from app import db
class Absence(db.Model):
"""une absence (sur une demi-journée)"""
"""LEGACY
Ce modèle n'est PLUS UTILISE depuis ScoDoc 9.6 et remplacé par assiduité.
une absence (sur une demi-journée)
"""
__tablename__ = "absences"
id = db.Column(db.Integer, primary_key=True)

View File

@ -61,6 +61,8 @@ class ApcValidationRCUE(ScoDocModel):
ue2 = db.relationship("UniteEns", foreign_keys=ue2_id)
parcour = db.relationship("ApcParcours")
_sco_dept_relations = ("Identite",) # pour accéder au département
def __repr__(self):
return f"""<{self.__class__.__name__} {self.id} {self.etud} {
self.ue1}/{self.ue2}:{self.code!r}>"""
@ -154,6 +156,8 @@ class ApcValidationAnnee(ScoDocModel):
etud = db.relationship("Identite", backref="apc_validations_annees")
formsemestre = db.relationship("FormSemestre", backref="apc_validations_annees")
_sco_dept_relations = ("Identite",) # pour accéder au département
def __repr__(self):
return f"""<{self.__class__.__name__} {self.id} {self.etud
} BUT{self.ordre}/{self.annee_scolaire}:{self.code!r}>"""
@ -317,6 +321,8 @@ class ValidationDUT120(ScoDocModel):
etud = db.relationship("Identite", backref="validations_dut120")
formsemestre = db.relationship("FormSemestre", backref="validations_dut120")
_sco_dept_relations = ("Identite",) # pour accéder au département
def __repr__(self):
return f"""<ValidationDUT120 {self.etud}>"""

View File

@ -1080,8 +1080,9 @@ class Admission(models.ScoDocModel):
return args_dict
# Suivi scolarité / débouchés
class ItemSuivi(db.Model):
class ItemSuivi(models.ScoDocModel):
"""Suivi scolarité / débouchés"""
__tablename__ = "itemsuivi"
id = db.Column(db.Integer, primary_key=True)
@ -1093,6 +1094,8 @@ class ItemSuivi(db.Model):
item_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
situation = db.Column(db.Text)
_sco_dept_relations = ("Identite",) # accès au dept_id
class ItemSuiviTag(db.Model):
__tablename__ = "itemsuivi_tags"
@ -1114,7 +1117,7 @@ itemsuivi_tags_assoc = db.Table(
)
class EtudAnnotation(db.Model):
class EtudAnnotation(models.ScoDocModel):
"""Annotation sur un étudiant"""
__tablename__ = "etud_annotations"
@ -1125,6 +1128,8 @@ class EtudAnnotation(db.Model):
author = db.Column(db.Text) # le pseudo (user_name), was zope_authenticated_user
comment = db.Column(db.Text)
_sco_dept_relations = ("Identite",) # accès au dept_id
def to_dict(self):
"""Représentation dictionnaire."""
e = dict(self.__dict__)

View File

@ -60,6 +60,8 @@ class Evaluation(models.ScoDocModel):
numero = db.Column(db.Integer, nullable=False, default=0)
ues = db.relationship("UniteEns", secondary="evaluation_ue_poids", viewonly=True)
_sco_dept_relations = ("ModuleImpl", "FormSemestre") # accès au dept_id
EVALUATION_NORMALE = 0 # valeurs stockées en base, ne pas changer !
EVALUATION_RATTRAPAGE = 1
EVALUATION_SESSION2 = 2

View File

@ -7,7 +7,7 @@ from flask_sqlalchemy.query import Query
import app
from app import db
from app.comp import df_cache
from app.models import SHORT_STR_LEN
from app.models import ScoDocModel, SHORT_STR_LEN
from app.models.but_refcomp import (
ApcAnneeParcours,
ApcCompetence,
@ -23,7 +23,7 @@ from app.scodoc import sco_utils as scu
from app.scodoc.codes_cursus import UE_STANDARD
class Formation(db.Model):
class Formation(ScoDocModel):
"""Programme pédagogique d'une formation"""
__tablename__ = "notes_formations"
@ -297,7 +297,7 @@ class Formation(db.Model):
db.session.commit()
class Matiere(db.Model):
class Matiere(ScoDocModel):
"""Matières: regroupe les modules d'une UE
La matière a peu d'utilité en dehors de la présentation des modules
d'une UE.
@ -313,6 +313,7 @@ class Matiere(db.Model):
numero = db.Column(db.Integer, nullable=False, default=0) # ordre de présentation
modules = db.relationship("Module", lazy="dynamic", backref="matiere")
_sco_dept_relations = ("UniteEns", "Formation") # accès au dept_id
def __repr__(self):
return f"""<{self.__class__.__name__}(id={self.id}, ue_id={

View File

@ -54,6 +54,7 @@ class Partition(ScoDocModel):
cascade="all, delete-orphan",
order_by="GroupDescr.numero, GroupDescr.group_name",
)
_sco_dept_relations = ("FormSemestre",)
def __init__(self, **kwargs):
super(Partition, self).__init__(**kwargs)
@ -225,6 +226,11 @@ class GroupDescr(ScoDocModel):
numero = db.Column(db.Integer, nullable=False, default=0)
"Numero = ordre de presentation"
_sco_dept_relations = (
"Partition",
"FormSemestre",
)
etuds = db.relationship(
"Identite",
secondary="group_membership",

View File

@ -60,6 +60,8 @@ class ModuleImpl(ScoDocModel):
)
"enseignants du module (sans le responsable)"
_sco_dept_relations = ("FormSemestre",) # accès au dept_id
def __repr__(self):
return f"<{self.__class__.__name__} {self.id} module={repr(self.module)}>"

View File

@ -75,6 +75,8 @@ class Module(models.ScoDocModel):
backref=db.backref("modules", lazy=True),
)
_sco_dept_relations = "Formation" # accès au dept_id
def __init__(self, **kwargs):
self.ue_coefs = []
super(Module, self).__init__(**kwargs)

View File

@ -5,11 +5,12 @@
import sqlalchemy as sa
from app import db
from app import models
from app.scodoc import safehtml
import app.scodoc.sco_utils as scu
class BulAppreciations(db.Model):
class BulAppreciations(models.ScoDocModel):
"""Appréciations sur bulletins"""
__tablename__ = "notes_appreciations"
@ -27,6 +28,8 @@ class BulAppreciations(db.Model):
author = db.Column(db.Text) # le pseudo (user_name), sans contrainte
comment = db.Column(db.Text) # texte libre
_sco_dept_relations = ("Identite",) # accès au dept_id
@classmethod
def get_appreciations_list(
cls, formsemestre_id: int, etudid: int

View File

@ -3,10 +3,10 @@
"""Model : preferences
"""
from app import db
from app import db, models
class ScoPreference(db.Model):
class ScoPreference(models.ScoDocModel):
"""ScoDoc preferences (par département)"""
__tablename__ = "sco_prefs"
@ -19,5 +19,8 @@ class ScoPreference(db.Model):
value = db.Column(db.Text())
formsemestre_id = db.Column(db.Integer, db.ForeignKey("notes_formsemestre.id"))
_sco_dept_relations = ("FormSemestre",) # accès au dept_id
def __repr__(self):
return f"<{self.__class__.__name__} {self.id} {self.departement.acronym} {self.name}={self.value}>"
return f"""<{self.__class__.__name__} {self.id} {self.departement.acronym
} {self.name}={self.value}>"""

View File

@ -767,15 +767,16 @@ def scolars_import_admission(
)
for group_id in group_ids:
group = db.session.get(GroupDescr, group_id)
group: GroupDescr = GroupDescr.get_instance(group_id)
if group.partition.groups_editable:
sco_groups.change_etud_group_in_partition(
args["etudid"], group
)
else:
elif not group.partition.is_parcours:
log("scolars_import_admission: partition non editable")
diag.append(
f"Attention: partition {group.partition} non editable (ignorée)"
f"""Attention: partition {
group.partition} (g{group.id}) non editable et ignorée"""
)
#

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.7.4"
SCOVERSION = "9.7.5"
SCONAME = "ScoDoc"

View File

@ -267,9 +267,7 @@ class ScoFake(object):
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
oids = sco_formsemestre.do_formsemestre_list(args={"formsemestre_id": oid})
if not oids:
raise ScoValueError("formsemestre not created !")
return oid

View File

@ -42,8 +42,7 @@ DEPT = TestConfig.DEPT_TEST
def test_formsemestres_associate_new_version(test_client):
"""Test association à une nouvelle version du programme"""
app.set_sco_dept(DEPT)
# Construit la base de test GB une seule fois
# puis lance les tests de jury
# Construit la base de test GB
doc, formation, formsemestre_titres = yaml_setup.setup_from_yaml(
"tests/ressources/yaml/simple_formsemestres.yaml"
)

49
tests/unit/test_models.py Normal file
View File

@ -0,0 +1,49 @@
""" Test méthodes génériques sur les modèles ScoDoc
"""
import pytest
from flask import g
import app
from app import db
from app.models import Departement, GroupDescr
from config import TestConfig
from tests.unit import yaml_setup
DEPT = TestConfig.DEPT_TEST
def test_get_instance(test_client):
"""Test accès instance avec ou sans dept"""
assert DEPT
app.set_sco_dept(DEPT)
# Création d'un autre départment
other_dept = Departement(acronym="X666")
db.session.add(other_dept)
db.session.flush()
# Crée qq semestres...
doc, formation, formsemestre_titres = yaml_setup.setup_from_yaml(
"tests/ressources/yaml/simple_formsemestres.yaml"
)
for formsemestre_titre in formsemestre_titres:
formsemestre = yaml_setup.create_formsemestre_with_etuds(
doc, formation, formsemestre_titre
)
assert formsemestre
# prend l'exemple de GroupDescr
# Crée un formsemestre et un groupe
gr = GroupDescr.query.first()
assert gr
oid = gr.id
# Accès sans département (comme le fait l'API non départementale)
g.scodoc_dept = None
g.scodoc_dept_id = -1 # invalide
assert GroupDescr.get_instance(oid, accept_none=True).id == oid
# Accès avec le bon département
app.set_sco_dept(DEPT)
assert GroupDescr.get_instance(oid, accept_none=True).id == oid
# Accès avec un autre département
app.set_sco_dept(other_dept.acronym)
assert GroupDescr.get_instance(oid, accept_none=True) is None