forked from ScoDoc/ScoDoc
WIP: distinction SAE/ressources, poids de evals
This commit is contained in:
parent
981f2c0e46
commit
3bb9a5cb76
@ -51,9 +51,11 @@ from app.models.formsemestre import (
|
|||||||
notes_modules_enseignants,
|
notes_modules_enseignants,
|
||||||
NotesModuleImplInscription,
|
NotesModuleImplInscription,
|
||||||
NotesEvaluation,
|
NotesEvaluation,
|
||||||
|
EvaluationUEPoids,
|
||||||
NotesSemSet,
|
NotesSemSet,
|
||||||
notes_semset_formsemestre,
|
notes_semset_formsemestre,
|
||||||
)
|
)
|
||||||
|
from app.models.but_pn import AppCrit
|
||||||
from app.models.groups import Partition, GroupDescr, group_membership
|
from app.models.groups import Partition, GroupDescr, group_membership
|
||||||
from app.models.notes import (
|
from app.models.notes import (
|
||||||
ScolarEvent,
|
ScolarEvent,
|
||||||
|
@ -1,4 +1,41 @@
|
|||||||
"""ScoDoc 9 models : Formation BUT 2021
|
"""ScoDoc 9 models : Formation BUT 2021
|
||||||
"""
|
"""
|
||||||
|
from enum import unique
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
# insérer ici idk
|
from app import db
|
||||||
|
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
Modules_ACs = db.Table(
|
||||||
|
"modules_acs",
|
||||||
|
db.Column("module_id", db.ForeignKey("notes_modules.id")),
|
||||||
|
db.Column("ac_id", db.ForeignKey("app_crit.id")),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AppCrit(db.Model):
|
||||||
|
"Apprentissage Critique BUT"
|
||||||
|
__tablename__ = "app_crit"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
code = db.Column(db.Text(), nullable=False, info={"label": "Code"})
|
||||||
|
titre = db.Column(db.Text(), info={"label": "Titre"})
|
||||||
|
|
||||||
|
modules = db.relationship(
|
||||||
|
"NotesModule", secondary=Modules_ACs, lazy="dynamic", backref="acs"
|
||||||
|
)
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
result = dict(self.__dict__)
|
||||||
|
result.pop("_sa_instance_state", None)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_label(self):
|
||||||
|
return self.code + " - " + self.titre
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<AC {}>".format(self.code)
|
||||||
|
|
||||||
|
def get_saes(self):
|
||||||
|
"""Liste des SAE associées"""
|
||||||
|
return [m for m in self.modules if m.module_type == ModuleType.SAE]
|
||||||
|
@ -5,6 +5,7 @@ from typing import Any
|
|||||||
from app import db
|
from app import db
|
||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
|
from app.scodoc.sco_utils import ModuleType
|
||||||
|
|
||||||
|
|
||||||
class NotesFormation(db.Model):
|
class NotesFormation(db.Model):
|
||||||
@ -116,10 +117,16 @@ class NotesModule(db.Model):
|
|||||||
numero = db.Column(db.Integer) # ordre de présentation
|
numero = db.Column(db.Integer) # ordre de présentation
|
||||||
# id de l'element pedagogique Apogee correspondant:
|
# id de l'element pedagogique Apogee correspondant:
|
||||||
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
|
code_apogee = db.Column(db.String(APO_CODE_STR_LEN))
|
||||||
module_type = db.Column(db.Integer) # NULL ou 0:defaut, 1: malus (NOTES_MALUS)
|
# Type: ModuleType: DEFAULT, MALUS, RESSOURCE, MODULE_SAE (enum)
|
||||||
|
module_type = db.Column(db.Integer)
|
||||||
# Relations:
|
# Relations:
|
||||||
modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic")
|
modimpls = db.relationship("NotesModuleImpl", backref="module", lazy="dynamic")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"<Module{ModuleType(self.module_type).name} id={self.id} code={self.code}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class NotesTag(db.Model):
|
class NotesTag(db.Model):
|
||||||
"""Tag sur un module"""
|
"""Tag sur un module"""
|
||||||
|
@ -8,6 +8,7 @@ from app import db
|
|||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import CODE_STR_LEN
|
from app.models import CODE_STR_LEN
|
||||||
|
from app.models import NotesUE
|
||||||
|
|
||||||
|
|
||||||
class FormSemestre(db.Model):
|
class FormSemestre(db.Model):
|
||||||
@ -305,7 +306,7 @@ class NotesEvaluation(db.Model):
|
|||||||
heure_fin = db.Column(db.Time)
|
heure_fin = db.Column(db.Time)
|
||||||
description = db.Column(db.Text)
|
description = db.Column(db.Text)
|
||||||
note_max = db.Column(db.Float)
|
note_max = db.Column(db.Float)
|
||||||
coefficient = db.Column(db.Float)
|
coefficient = db.Column(db.Float) # non BUT
|
||||||
visibulletin = db.Column(
|
visibulletin = db.Column(
|
||||||
db.Boolean, nullable=False, default=True, server_default="true"
|
db.Boolean, nullable=False, default=True, server_default="true"
|
||||||
)
|
)
|
||||||
@ -319,6 +320,56 @@ class NotesEvaluation(db.Model):
|
|||||||
# ordre de presentation (par défaut, le plus petit numero
|
# ordre de presentation (par défaut, le plus petit numero
|
||||||
# est la plus ancienne eval):
|
# est la plus ancienne eval):
|
||||||
numero = db.Column(db.Integer)
|
numero = db.Column(db.Integer)
|
||||||
|
ues = db.relationship("NotesUE", secondary="evaluation_ue_poids", viewonly=True)
|
||||||
|
|
||||||
|
def set_ue_poids(self, ue, poids: float):
|
||||||
|
"""Set poids évaluation vers cette UE"""
|
||||||
|
self.update_ue_poids_dict({ue.id: poids})
|
||||||
|
|
||||||
|
def set_ue_poids_dict(self, ue_poids_dict: dict):
|
||||||
|
"""set poids vers les UE (remplace existants)
|
||||||
|
ue_poids_dict = { ue_id : poids }
|
||||||
|
"""
|
||||||
|
L = []
|
||||||
|
for ue_id, poids in ue_poids_dict.items():
|
||||||
|
ue = NotesUE.query.get(ue_id)
|
||||||
|
L.append(EvaluationUEPoids(evaluation=self, ue=ue, poids=poids))
|
||||||
|
self.ue_poids = L
|
||||||
|
|
||||||
|
def update_ue_poids_dict(self, ue_poids_dict: dict):
|
||||||
|
"""update poids vers UE (ajoute aux existants)"""
|
||||||
|
current = self.get_ue_poids_dict()
|
||||||
|
current.update(ue_poids_dict)
|
||||||
|
self.set_ue_poids_dict(current)
|
||||||
|
|
||||||
|
def get_ue_poids_dict(self):
|
||||||
|
"""returns { ue_id : poids }"""
|
||||||
|
return {p.ue.id: p.poids for p in self.ue_poids}
|
||||||
|
|
||||||
|
|
||||||
|
class EvaluationUEPoids(db.Model):
|
||||||
|
"""Poids des évaluations (BUT)
|
||||||
|
association many to many
|
||||||
|
"""
|
||||||
|
|
||||||
|
evaluation_id = db.Column(
|
||||||
|
db.Integer, db.ForeignKey("notes_evaluation.id"), primary_key=True
|
||||||
|
)
|
||||||
|
ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), primary_key=True)
|
||||||
|
poids = db.Column(
|
||||||
|
db.Float,
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
evaluation = db.relationship(
|
||||||
|
NotesEvaluation,
|
||||||
|
backref=db.backref("ue_poids", cascade="all, delete-orphan"),
|
||||||
|
)
|
||||||
|
ue = db.relationship(
|
||||||
|
NotesUE, backref=db.backref("evaluation_ue_poids", cascade="all, delete-orphan")
|
||||||
|
)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<EvaluationUEPoids {self.evaluation} {self.ue} poids={self.poids}>"
|
||||||
|
|
||||||
|
|
||||||
class NotesSemSet(db.Model):
|
class NotesSemSet(db.Model):
|
||||||
|
@ -40,7 +40,7 @@ from app.scodoc.sco_permissions import Permission
|
|||||||
def sidebar_common():
|
def sidebar_common():
|
||||||
"partie commune à toutes les sidebar"
|
"partie commune à toutes les sidebar"
|
||||||
H = [
|
H = [
|
||||||
f"""<a class="scodoc_title" href="{url_for("scodoc.index", scodoc_dept=g.scodoc_dept)}">ScoDoc 9</a>
|
f"""<a class="scodoc_title" href="{url_for("scodoc.index", scodoc_dept=g.scodoc_dept)}">ScoDoc 9.1</a>
|
||||||
<div id="authuser"><a id="authuserlink" href="{
|
<div id="authuser"><a id="authuserlink" href="{
|
||||||
url_for("users.user_info_page",
|
url_for("users.user_info_page",
|
||||||
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
||||||
|
@ -28,6 +28,39 @@
|
|||||||
"""Semestres: Codes gestion parcours (constantes)
|
"""Semestres: Codes gestion parcours (constantes)
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
|
import enum
|
||||||
|
|
||||||
|
|
||||||
|
@enum.unique
|
||||||
|
class CodesParcours(enum.IntEnum):
|
||||||
|
"""Codes numériques de sparcours, enregistrés en base
|
||||||
|
dans notes_formations.type_parcours
|
||||||
|
Ne pas modifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
Legacy = 0
|
||||||
|
DUT = 100
|
||||||
|
DUT4 = 110
|
||||||
|
DUTMono = 120
|
||||||
|
DUT2 = 130
|
||||||
|
LP = 200
|
||||||
|
LP2sem = 210
|
||||||
|
LP2semEvry = 220
|
||||||
|
LP2014 = 230
|
||||||
|
LP2sem2014 = 240
|
||||||
|
M2 = 250
|
||||||
|
M2noncomp = 251
|
||||||
|
Mono = 300
|
||||||
|
MasterLMD = 402
|
||||||
|
MasterIG = 403
|
||||||
|
LicenceUCAC3 = 501
|
||||||
|
MasterUCAC2 = 502
|
||||||
|
MonoUCAC = 503
|
||||||
|
GEN_6_SEM = 600
|
||||||
|
BUT = 700
|
||||||
|
ISCID6 = 1001
|
||||||
|
ISCID4 = 1002
|
||||||
|
|
||||||
|
|
||||||
NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok
|
NOTES_TOLERANCE = 0.00499999999999 # si note >= (BARRE-TOLERANCE), considere ok
|
||||||
# (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999)
|
# (permet d'eviter d'afficher 10.00 sous barre alors que la moyenne vaut 9.999)
|
||||||
@ -214,6 +247,7 @@ class TypeParcours(object):
|
|||||||
ALLOWED_UE_TYPES = list(
|
ALLOWED_UE_TYPES = list(
|
||||||
UE_TYPE_NAME.keys()
|
UE_TYPE_NAME.keys()
|
||||||
) # par defaut, autorise tous les types d'UE
|
) # par defaut, autorise tous les types d'UE
|
||||||
|
APC_SAE = False # Approche par compétences avec ressources et SAÉs
|
||||||
|
|
||||||
def check(self, formation=None):
|
def check(self, formation=None):
|
||||||
return True, "" # status, diagnostic_message
|
return True, "" # status, diagnostic_message
|
||||||
@ -262,6 +296,20 @@ def register_parcours(Parcours):
|
|||||||
TYPES_PARCOURS[Parcours.TYPE_PARCOURS] = Parcours
|
TYPES_PARCOURS[Parcours.TYPE_PARCOURS] = Parcours
|
||||||
|
|
||||||
|
|
||||||
|
class ParcoursBUT(TypeParcours):
|
||||||
|
"""BUT Bachelor Universitaire de Technologie"""
|
||||||
|
|
||||||
|
TYPE_PARCOURS = 700
|
||||||
|
NAME = "BUT"
|
||||||
|
NB_SEM = 6
|
||||||
|
COMPENSATION_UE = False
|
||||||
|
APC_SAE = True
|
||||||
|
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||||||
|
|
||||||
|
|
||||||
|
register_parcours(ParcoursBUT())
|
||||||
|
|
||||||
|
|
||||||
class ParcoursDUT(TypeParcours):
|
class ParcoursDUT(TypeParcours):
|
||||||
"""DUT selon l'arrêté d'août 2005"""
|
"""DUT selon l'arrêté d'août 2005"""
|
||||||
|
|
||||||
@ -302,7 +350,7 @@ register_parcours(ParcoursDUTMono())
|
|||||||
class ParcoursDUT2(ParcoursDUT):
|
class ParcoursDUT2(ParcoursDUT):
|
||||||
"""DUT en deux semestres (par ex.: années spéciales semestrialisées)"""
|
"""DUT en deux semestres (par ex.: années spéciales semestrialisées)"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 130
|
TYPE_PARCOURS = CodesParcours.DUT2
|
||||||
NAME = "DUT2"
|
NAME = "DUT2"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
|
|
||||||
@ -315,7 +363,7 @@ class ParcoursLP(TypeParcours):
|
|||||||
(pour anciennes LP. Après 2014, préférer ParcoursLP2014)
|
(pour anciennes LP. Après 2014, préférer ParcoursLP2014)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 200
|
TYPE_PARCOURS = CodesParcours.LP
|
||||||
NAME = "LP"
|
NAME = "LP"
|
||||||
NB_SEM = 1
|
NB_SEM = 1
|
||||||
COMPENSATION_UE = False
|
COMPENSATION_UE = False
|
||||||
@ -332,7 +380,7 @@ register_parcours(ParcoursLP())
|
|||||||
class ParcoursLP2sem(ParcoursLP):
|
class ParcoursLP2sem(ParcoursLP):
|
||||||
"""Licence Pro (en deux "semestres")"""
|
"""Licence Pro (en deux "semestres")"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 210
|
TYPE_PARCOURS = CodesParcours.LP2sem
|
||||||
NAME = "LP2sem"
|
NAME = "LP2sem"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
COMPENSATION_UE = True
|
COMPENSATION_UE = True
|
||||||
@ -345,7 +393,7 @@ register_parcours(ParcoursLP2sem())
|
|||||||
class ParcoursLP2semEvry(ParcoursLP):
|
class ParcoursLP2semEvry(ParcoursLP):
|
||||||
"""Licence Pro (en deux "semestres", U. Evry)"""
|
"""Licence Pro (en deux "semestres", U. Evry)"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 220
|
TYPE_PARCOURS = CodesParcours.LP2semEvry
|
||||||
NAME = "LP2semEvry"
|
NAME = "LP2semEvry"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
COMPENSATION_UE = True
|
COMPENSATION_UE = True
|
||||||
@ -371,7 +419,7 @@ class ParcoursLP2014(TypeParcours):
|
|||||||
# l'établissement d'un coefficient qui peut varier dans un rapport de 1 à 3. ", etc ne sont _pas_
|
# l'établissement d'un coefficient qui peut varier dans un rapport de 1 à 3. ", etc ne sont _pas_
|
||||||
# vérifiés par ScoDoc)
|
# vérifiés par ScoDoc)
|
||||||
|
|
||||||
TYPE_PARCOURS = 230
|
TYPE_PARCOURS = CodesParcours.LP2014
|
||||||
NAME = "LP2014"
|
NAME = "LP2014"
|
||||||
NB_SEM = 1
|
NB_SEM = 1
|
||||||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
||||||
@ -415,7 +463,7 @@ register_parcours(ParcoursLP2014())
|
|||||||
class ParcoursLP2sem2014(ParcoursLP):
|
class ParcoursLP2sem2014(ParcoursLP):
|
||||||
"""Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)"""
|
"""Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 240
|
TYPE_PARCOURS = CodesParcours.LP2sem2014
|
||||||
NAME = "LP2014_2sem"
|
NAME = "LP2014_2sem"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
|
|
||||||
@ -427,7 +475,7 @@ register_parcours(ParcoursLP2sem2014())
|
|||||||
class ParcoursM2(TypeParcours):
|
class ParcoursM2(TypeParcours):
|
||||||
"""Master 2 (en deux "semestres")"""
|
"""Master 2 (en deux "semestres")"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 250
|
TYPE_PARCOURS = CodesParcours.M2
|
||||||
NAME = "M2sem"
|
NAME = "M2sem"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
COMPENSATION_UE = True
|
COMPENSATION_UE = True
|
||||||
@ -440,7 +488,7 @@ register_parcours(ParcoursM2())
|
|||||||
class ParcoursM2noncomp(ParcoursM2):
|
class ParcoursM2noncomp(ParcoursM2):
|
||||||
"""Master 2 (en deux "semestres") sans compensation"""
|
"""Master 2 (en deux "semestres") sans compensation"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 251
|
TYPE_PARCOURS = CodesParcours.M2noncomp
|
||||||
NAME = "M2noncomp"
|
NAME = "M2noncomp"
|
||||||
COMPENSATION_UE = False
|
COMPENSATION_UE = False
|
||||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||||
@ -452,7 +500,7 @@ register_parcours(ParcoursM2noncomp())
|
|||||||
class ParcoursMono(TypeParcours):
|
class ParcoursMono(TypeParcours):
|
||||||
"""Formation générique en une session"""
|
"""Formation générique en une session"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 300
|
TYPE_PARCOURS = CodesParcours.Mono
|
||||||
NAME = "Mono"
|
NAME = "Mono"
|
||||||
NB_SEM = 1
|
NB_SEM = 1
|
||||||
COMPENSATION_UE = False
|
COMPENSATION_UE = False
|
||||||
@ -465,7 +513,7 @@ register_parcours(ParcoursMono())
|
|||||||
class ParcoursLegacy(TypeParcours):
|
class ParcoursLegacy(TypeParcours):
|
||||||
"""DUT (ancien ScoDoc, ne plus utiliser)"""
|
"""DUT (ancien ScoDoc, ne plus utiliser)"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 0
|
TYPE_PARCOURS = CodesParcours.Legacy
|
||||||
NAME = "DUT"
|
NAME = "DUT"
|
||||||
NB_SEM = 4
|
NB_SEM = 4
|
||||||
COMPENSATION_UE = None # backward compat: defini dans formsemestre
|
COMPENSATION_UE = None # backward compat: defini dans formsemestre
|
||||||
@ -499,7 +547,7 @@ class ParcoursBachelorISCID6(ParcoursISCID):
|
|||||||
"""ISCID: Bachelor en 3 ans (6 sem.)"""
|
"""ISCID: Bachelor en 3 ans (6 sem.)"""
|
||||||
|
|
||||||
NAME = "ParcoursBachelorISCID6"
|
NAME = "ParcoursBachelorISCID6"
|
||||||
TYPE_PARCOURS = 1001
|
TYPE_PARCOURS = CodesParcours.ISCID6
|
||||||
NAME = ""
|
NAME = ""
|
||||||
NB_SEM = 6
|
NB_SEM = 6
|
||||||
ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme
|
ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme
|
||||||
@ -510,7 +558,7 @@ register_parcours(ParcoursBachelorISCID6())
|
|||||||
|
|
||||||
class ParcoursMasterISCID4(ParcoursISCID):
|
class ParcoursMasterISCID4(ParcoursISCID):
|
||||||
"ISCID: Master en 2 ans (4 sem.)"
|
"ISCID: Master en 2 ans (4 sem.)"
|
||||||
TYPE_PARCOURS = 1002
|
TYPE_PARCOURS = CodesParcours.ISCID4
|
||||||
NAME = "ParcoursMasterISCID4"
|
NAME = "ParcoursMasterISCID4"
|
||||||
NB_SEM = 4
|
NB_SEM = 4
|
||||||
ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme
|
ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme
|
||||||
@ -536,7 +584,7 @@ class ParcoursUCAC(TypeParcours):
|
|||||||
class ParcoursLicenceUCAC3(ParcoursUCAC):
|
class ParcoursLicenceUCAC3(ParcoursUCAC):
|
||||||
"""UCAC: Licence en 3 sessions d'un an"""
|
"""UCAC: Licence en 3 sessions d'un an"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 501
|
TYPE_PARCOURS = CodesParcours.LicenceUCAC3
|
||||||
NAME = "Licence UCAC en 3 sessions d'un an"
|
NAME = "Licence UCAC en 3 sessions d'un an"
|
||||||
NB_SEM = 3
|
NB_SEM = 3
|
||||||
|
|
||||||
@ -547,7 +595,7 @@ register_parcours(ParcoursLicenceUCAC3())
|
|||||||
class ParcoursMasterUCAC2(ParcoursUCAC):
|
class ParcoursMasterUCAC2(ParcoursUCAC):
|
||||||
"""UCAC: Master en 2 sessions d'un an"""
|
"""UCAC: Master en 2 sessions d'un an"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 502
|
TYPE_PARCOURS = CodesParcours.MasterUCAC2
|
||||||
NAME = "Master UCAC en 2 sessions d'un an"
|
NAME = "Master UCAC en 2 sessions d'un an"
|
||||||
NB_SEM = 2
|
NB_SEM = 2
|
||||||
|
|
||||||
@ -558,7 +606,7 @@ register_parcours(ParcoursMasterUCAC2())
|
|||||||
class ParcoursMonoUCAC(ParcoursUCAC):
|
class ParcoursMonoUCAC(ParcoursUCAC):
|
||||||
"""UCAC: Formation en 1 session de durée variable"""
|
"""UCAC: Formation en 1 session de durée variable"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 503
|
TYPE_PARCOURS = CodesParcours.MonoUCAC
|
||||||
NAME = "Formation UCAC en 1 session de durée variable"
|
NAME = "Formation UCAC en 1 session de durée variable"
|
||||||
NB_SEM = 1
|
NB_SEM = 1
|
||||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||||
@ -570,7 +618,7 @@ register_parcours(ParcoursMonoUCAC())
|
|||||||
class Parcours6Sem(TypeParcours):
|
class Parcours6Sem(TypeParcours):
|
||||||
"""Parcours générique en 6 semestres"""
|
"""Parcours générique en 6 semestres"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 600
|
TYPE_PARCOURS = CodesParcours.GEN_6_SEM
|
||||||
NAME = "Formation en 6 semestres"
|
NAME = "Formation en 6 semestres"
|
||||||
NB_SEM = 6
|
NB_SEM = 6
|
||||||
COMPENSATION_UE = True
|
COMPENSATION_UE = True
|
||||||
@ -592,7 +640,7 @@ register_parcours(Parcours6Sem())
|
|||||||
class ParcoursMasterLMD(TypeParcours):
|
class ParcoursMasterLMD(TypeParcours):
|
||||||
"""Master générique en 4 semestres dans le LMD"""
|
"""Master générique en 4 semestres dans le LMD"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 402
|
TYPE_PARCOURS = CodesParcours.MasterLMD
|
||||||
NAME = "Master LMD"
|
NAME = "Master LMD"
|
||||||
NB_SEM = 4
|
NB_SEM = 4
|
||||||
COMPENSATION_UE = True # variabale inutilisée
|
COMPENSATION_UE = True # variabale inutilisée
|
||||||
@ -605,7 +653,7 @@ register_parcours(ParcoursMasterLMD())
|
|||||||
class ParcoursMasterIG(ParcoursMasterLMD):
|
class ParcoursMasterIG(ParcoursMasterLMD):
|
||||||
"""Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)"""
|
"""Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)"""
|
||||||
|
|
||||||
TYPE_PARCOURS = 403
|
TYPE_PARCOURS = CodesParcours.MasterIG
|
||||||
NAME = "Master IG P13"
|
NAME = "Master IG P13"
|
||||||
BARRE_MOY = 10.0
|
BARRE_MOY = 10.0
|
||||||
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
||||||
|
@ -420,8 +420,8 @@ def module_edit(module_id=None):
|
|||||||
"input_type": "menu",
|
"input_type": "menu",
|
||||||
"title": "Type",
|
"title": "Type",
|
||||||
"explanation": "",
|
"explanation": "",
|
||||||
"labels": ("Standard", "Malus"),
|
"labels": [x.name.capitalize() for x in scu.ModuleType],
|
||||||
"allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)),
|
"allowed_values": [str(int(x)) for x in scu.ModuleType],
|
||||||
"enabled": unlocked,
|
"enabled": unlocked,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -775,8 +775,12 @@ def _ue_table_ues(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
H.append('<span class="locked">[verrouillé]</span>')
|
H.append('<span class="locked">[verrouillé]</span>')
|
||||||
|
if parcours.APC_SAE:
|
||||||
|
func_html_list = _ue_table_ressources_saes
|
||||||
|
else:
|
||||||
|
func_html_list = _ue_table_matieres
|
||||||
H.append(
|
H.append(
|
||||||
_ue_table_matieres(
|
func_html_list(
|
||||||
parcours,
|
parcours,
|
||||||
ue,
|
ue,
|
||||||
editable,
|
editable,
|
||||||
@ -837,6 +841,8 @@ def _ue_table_matieres(
|
|||||||
delete_disabled_icon,
|
delete_disabled_icon,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
if not parcours.UE_IS_MODULE:
|
||||||
|
H.append("</li>")
|
||||||
if not matieres:
|
if not matieres:
|
||||||
H.append("<li>Aucune matière dans cette UE ! ")
|
H.append("<li>Aucune matière dans cette UE ! ")
|
||||||
if editable:
|
if editable:
|
||||||
@ -855,6 +861,76 @@ def _ue_table_matieres(
|
|||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def _ue_table_ressources_saes(
|
||||||
|
parcours,
|
||||||
|
ue,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
):
|
||||||
|
"""Édition de programme: liste des ressources et SAÉs d'une UE.
|
||||||
|
(pour les parcours APC_SAE)
|
||||||
|
"""
|
||||||
|
matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||||
|
if not matieres:
|
||||||
|
# Les formations APC (BUT) n'utilisent pas de matières
|
||||||
|
# mais il doit y en avoir une par UE
|
||||||
|
# silently fix this on-the-fly to ease migration
|
||||||
|
_ = sco_edit_matiere.do_matiere_create(
|
||||||
|
{"ue_id": ue["ue_id"], "titre": "APC", "numero": 1},
|
||||||
|
)
|
||||||
|
matieres = sco_edit_matiere.matiere_list(args={"ue_id": ue["ue_id"]})
|
||||||
|
assert matieres
|
||||||
|
mat = matieres[0]
|
||||||
|
|
||||||
|
H = [
|
||||||
|
"""
|
||||||
|
<ul class="notes_matiere_list but_matiere_list">
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
modules = sco_edit_module.module_list(args={"ue_id": ue["ue_id"]})
|
||||||
|
for titre, element_name, module_type in (
|
||||||
|
("Ressources", "ressource", scu.ModuleType.RESSOURCE),
|
||||||
|
("SAÉs", "SAÉ", scu.ModuleType.SAE),
|
||||||
|
("Autres modules", "xxx", None),
|
||||||
|
):
|
||||||
|
H.append(f'<li class="notes_matiere_list">{titre}')
|
||||||
|
elements = [
|
||||||
|
m
|
||||||
|
for m in modules
|
||||||
|
if module_type == m["module_type"]
|
||||||
|
or (
|
||||||
|
(module_type is None)
|
||||||
|
and m["module_type"]
|
||||||
|
not in (scu.ModuleType.RESSOURCE, scu.ModuleType.SAE)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
H.append(
|
||||||
|
_ue_table_modules(
|
||||||
|
parcours,
|
||||||
|
mat,
|
||||||
|
elements,
|
||||||
|
editable,
|
||||||
|
tag_editable,
|
||||||
|
arrow_up,
|
||||||
|
arrow_down,
|
||||||
|
arrow_none,
|
||||||
|
delete_icon,
|
||||||
|
delete_disabled_icon,
|
||||||
|
empty_list_msg="Aucune " + element_name,
|
||||||
|
create_element_msg="créer une " + element_name,
|
||||||
|
add_suppress_link=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
H.append("</li></ul>")
|
||||||
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
def _ue_table_modules(
|
def _ue_table_modules(
|
||||||
parcours,
|
parcours,
|
||||||
mat,
|
mat,
|
||||||
@ -866,6 +942,10 @@ def _ue_table_modules(
|
|||||||
arrow_none,
|
arrow_none,
|
||||||
delete_icon,
|
delete_icon,
|
||||||
delete_disabled_icon,
|
delete_disabled_icon,
|
||||||
|
unit_name="matière",
|
||||||
|
add_suppress_link=True, # lien "supprimer cette matière"
|
||||||
|
empty_list_msg="Aucun élément dans cette matière",
|
||||||
|
create_element_msg="créer un module",
|
||||||
):
|
):
|
||||||
"""Édition de programme: liste des modules d'une matière d'une UE"""
|
"""Édition de programme: liste des modules d'une matière d'une UE"""
|
||||||
H = ['<ul class="notes_module_list">']
|
H = ['<ul class="notes_module_list">']
|
||||||
@ -948,8 +1028,8 @@ def _ue_table_modules(
|
|||||||
)
|
)
|
||||||
H.append("</li>")
|
H.append("</li>")
|
||||||
if not modules:
|
if not modules:
|
||||||
H.append("<li>Aucun module dans cette matière ! ")
|
H.append(f"<li>{empty_list_msg} ! ")
|
||||||
if editable:
|
if editable and add_suppress_link:
|
||||||
H.append(
|
H.append(
|
||||||
f"""<a class="stdlink" href="{
|
f"""<a class="stdlink" href="{
|
||||||
url_for("notes.matiere_delete",
|
url_for("notes.matiere_delete",
|
||||||
@ -963,11 +1043,10 @@ def _ue_table_modules(
|
|||||||
f"""<li> <a class="stdlink" href="{
|
f"""<li> <a class="stdlink" href="{
|
||||||
url_for("notes.module_create",
|
url_for("notes.module_create",
|
||||||
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
scodoc_dept=g.scodoc_dept, matiere_id=mat["matiere_id"])}"
|
||||||
>créer un module</a></li>
|
>{create_element_msg}</a></li>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
H.append("</ul>")
|
H.append("</ul>")
|
||||||
H.append("</li>")
|
|
||||||
return "\n".join(H)
|
return "\n".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,13 +32,12 @@ import base64
|
|||||||
import bisect
|
import bisect
|
||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
from enum import IntEnum
|
||||||
import json
|
import json
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
import numbers
|
import numbers
|
||||||
import os
|
import os
|
||||||
import pydot
|
|
||||||
import re
|
import re
|
||||||
import requests
|
|
||||||
import _thread
|
import _thread
|
||||||
import time
|
import time
|
||||||
import unicodedata
|
import unicodedata
|
||||||
@ -46,6 +45,8 @@ import urllib
|
|||||||
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
||||||
|
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
import pydot
|
||||||
|
import requests
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for, make_response
|
from flask import url_for, make_response
|
||||||
@ -73,6 +74,17 @@ NOTES_ATTENTE = -1002.0 # note "en attente" (se calcule comme une note neutrali
|
|||||||
MODULE_STANDARD = 0
|
MODULE_STANDARD = 0
|
||||||
MODULE_MALUS = 1
|
MODULE_MALUS = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleType(IntEnum):
|
||||||
|
"""Code des types de module."""
|
||||||
|
|
||||||
|
# Stockés en BD dans NotesModule.module_type: ne pas modifier ces valeurs
|
||||||
|
STANDARD = 0
|
||||||
|
MALUS = 1
|
||||||
|
RESSOURCE = 2 # BUT
|
||||||
|
SAE = 3 # BUT
|
||||||
|
|
||||||
|
|
||||||
MALUS_MAX = 20.0
|
MALUS_MAX = 20.0
|
||||||
MALUS_MIN = -20.0
|
MALUS_MIN = -20.0
|
||||||
|
|
||||||
|
64
migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py
Normal file
64
migrations/versions/ada0d1f3d84f_but_poids_evaluations_ac.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
"""BUT: poids evaluations, AC
|
||||||
|
|
||||||
|
Revision ID: ada0d1f3d84f
|
||||||
|
Revises: 75cf18659984
|
||||||
|
Create Date: 2021-11-07 22:49:22.697211
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = "ada0d1f3d84f"
|
||||||
|
down_revision = "75cf18659984"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table(
|
||||||
|
"app_crit",
|
||||||
|
sa.Column("id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("code", sa.Text(), nullable=False),
|
||||||
|
sa.Column("titre", sa.Text(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint("id"),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"modules_acs",
|
||||||
|
sa.Column("module_id", sa.Integer(), nullable=True),
|
||||||
|
sa.Column("ac_id", sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["ac_id"],
|
||||||
|
["app_crit.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["module_id"],
|
||||||
|
["notes_modules.id"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"evaluation_ue_poids",
|
||||||
|
sa.Column("evaluation_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("ue_id", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("poids", sa.Float(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["evaluation_id"],
|
||||||
|
["notes_evaluation.id"],
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["ue_id"],
|
||||||
|
["notes_ue.id"],
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("evaluation_id", "ue_id"),
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table("evaluation_ue_poids")
|
||||||
|
op.drop_table("modules_acs")
|
||||||
|
op.drop_table("app_crit")
|
||||||
|
# ### end Alembic commands ###
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.0.61"
|
SCOVERSION = "9.1.0"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
83
tests/unit/test_but_modules.py
Normal file
83
tests/unit/test_but_modules.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
"""
|
||||||
|
Test modèles évaluations avec poids BUT
|
||||||
|
"""
|
||||||
|
|
||||||
|
from tests.unit import sco_fake_gen
|
||||||
|
from app import db
|
||||||
|
from app import models
|
||||||
|
|
||||||
|
"""
|
||||||
|
mapp.set_sco_dept("RT")
|
||||||
|
from app.auth.models import get_super_admin
|
||||||
|
admin_user = get_super_admin()
|
||||||
|
ctx.push()
|
||||||
|
login_user(admin_user)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_evaluation_poids(test_client):
|
||||||
|
"""Association de poids vers les UE"""
|
||||||
|
G = sco_fake_gen.ScoFake(verbose=False)
|
||||||
|
_f = G.create_formation(
|
||||||
|
acronyme="F3", titre="Formation 2", titre_officiel="Titre officiel 2"
|
||||||
|
)
|
||||||
|
_ue1 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE1", titre="ue 1")
|
||||||
|
_ue2 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE2", titre="ue 2")
|
||||||
|
_ue3 = G.create_ue(formation_id=_f["formation_id"], acronyme="UE3", titre="ue 3")
|
||||||
|
_mat = G.create_matiere(ue_id=_ue1["ue_id"], titre="matière test")
|
||||||
|
_mod = G.create_module(
|
||||||
|
matiere_id=_mat["matiere_id"],
|
||||||
|
code="TSM1",
|
||||||
|
coefficient=1.0,
|
||||||
|
titre="module test",
|
||||||
|
ue_id=_ue1["ue_id"],
|
||||||
|
formation_id=_f["formation_id"],
|
||||||
|
)
|
||||||
|
sem = G.create_formsemestre(
|
||||||
|
formation_id=_f["formation_id"],
|
||||||
|
semestre_id=1,
|
||||||
|
date_debut="01/01/2021",
|
||||||
|
date_fin="30/06/2021",
|
||||||
|
) # formsemestre_id=716
|
||||||
|
mi = G.create_moduleimpl(
|
||||||
|
module_id=_mod["module_id"],
|
||||||
|
formsemestre_id=sem["formsemestre_id"],
|
||||||
|
)
|
||||||
|
moduleimpl_id = mi["id"]
|
||||||
|
_e1 = G.create_evaluation(
|
||||||
|
moduleimpl_id=moduleimpl_id,
|
||||||
|
jour="01/01/2021",
|
||||||
|
description="evaluation 1",
|
||||||
|
coefficient=0,
|
||||||
|
)
|
||||||
|
evaluation_id = _e1["evaluation_id"] # evaluation_id=25246
|
||||||
|
ue1_id = _ue1["id"] # ue1_id=1684
|
||||||
|
formation_id = _f["id"] # formation_id=199
|
||||||
|
#
|
||||||
|
e1 = models.NotesEvaluation.query.get(evaluation_id)
|
||||||
|
ue1 = models.NotesUE.query.get(ue1_id)
|
||||||
|
assert e1.ue_poids == []
|
||||||
|
p1 = 3.14
|
||||||
|
e1.set_ue_poids(ue1, p1)
|
||||||
|
db.session.commit()
|
||||||
|
assert e1.get_ue_poids_dict()[ue1_id] == p1
|
||||||
|
ues = models.NotesUE.query.filter_by(formation_id=formation_id).all()
|
||||||
|
poids = [1.0, 2.0, 3.0]
|
||||||
|
for (ue, p) in zip(ues, poids):
|
||||||
|
e1.set_ue_poids(ue, p)
|
||||||
|
assert len(e1.ue_poids) == len(ues)
|
||||||
|
assert e1.get_ue_poids_dict()[ues[1].id] == poids[1]
|
||||||
|
e1.set_ue_poids(ue1, p1)
|
||||||
|
db.session.commit()
|
||||||
|
poids2 = [10, 20]
|
||||||
|
e1.update_ue_poids_dict({ue.id: p for (ue, p) in zip(ues[:-1], poids2)})
|
||||||
|
assert e1.get_ue_poids_dict()[ues[0].id] == poids2[0]
|
||||||
|
assert e1.get_ue_poids_dict()[ues[1].id] == poids2[1]
|
||||||
|
assert e1.get_ue_poids_dict()[ues[2].id] == poids[2]
|
||||||
|
# Delete UE
|
||||||
|
db.session.delete(ues[2])
|
||||||
|
db.session.commit()
|
||||||
|
# Delete eval
|
||||||
|
db.session.delete(e1)
|
||||||
|
db.session.commit()
|
||||||
|
assert len(models.EvaluationUEPoids.query.all()) == 0
|
Loading…
Reference in New Issue
Block a user