forked from ScoDoc/DocScoDoc
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,
|
||||
NotesModuleImplInscription,
|
||||
NotesEvaluation,
|
||||
EvaluationUEPoids,
|
||||
NotesSemSet,
|
||||
notes_semset_formsemestre,
|
||||
)
|
||||
from app.models.but_pn import AppCrit
|
||||
from app.models.groups import Partition, GroupDescr, group_membership
|
||||
from app.models.notes import (
|
||||
ScolarEvent,
|
||||
|
@ -1,4 +1,41 @@
|
||||
"""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.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.scodoc.sco_utils import ModuleType
|
||||
|
||||
|
||||
class NotesFormation(db.Model):
|
||||
@ -116,10 +117,16 @@ class NotesModule(db.Model):
|
||||
numero = db.Column(db.Integer) # ordre de présentation
|
||||
# id de l'element pedagogique Apogee correspondant:
|
||||
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:
|
||||
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):
|
||||
"""Tag sur un module"""
|
||||
|
@ -8,6 +8,7 @@ from app import db
|
||||
from app.models import APO_CODE_STR_LEN
|
||||
from app.models import SHORT_STR_LEN
|
||||
from app.models import CODE_STR_LEN
|
||||
from app.models import NotesUE
|
||||
|
||||
|
||||
class FormSemestre(db.Model):
|
||||
@ -305,7 +306,7 @@ class NotesEvaluation(db.Model):
|
||||
heure_fin = db.Column(db.Time)
|
||||
description = db.Column(db.Text)
|
||||
note_max = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float)
|
||||
coefficient = db.Column(db.Float) # non BUT
|
||||
visibulletin = db.Column(
|
||||
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
|
||||
# est la plus ancienne eval):
|
||||
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):
|
||||
|
@ -40,7 +40,7 @@ from app.scodoc.sco_permissions import Permission
|
||||
def sidebar_common():
|
||||
"partie commune à toutes les sidebar"
|
||||
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="{
|
||||
url_for("users.user_info_page",
|
||||
scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
|
||||
|
@ -28,6 +28,39 @@
|
||||
"""Semestres: Codes gestion parcours (constantes)
|
||||
"""
|
||||
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
|
||||
# (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(
|
||||
UE_TYPE_NAME.keys()
|
||||
) # 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):
|
||||
return True, "" # status, diagnostic_message
|
||||
@ -262,6 +296,20 @@ def register_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):
|
||||
"""DUT selon l'arrêté d'août 2005"""
|
||||
|
||||
@ -302,7 +350,7 @@ register_parcours(ParcoursDUTMono())
|
||||
class ParcoursDUT2(ParcoursDUT):
|
||||
"""DUT en deux semestres (par ex.: années spéciales semestrialisées)"""
|
||||
|
||||
TYPE_PARCOURS = 130
|
||||
TYPE_PARCOURS = CodesParcours.DUT2
|
||||
NAME = "DUT2"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -315,7 +363,7 @@ class ParcoursLP(TypeParcours):
|
||||
(pour anciennes LP. Après 2014, préférer ParcoursLP2014)
|
||||
"""
|
||||
|
||||
TYPE_PARCOURS = 200
|
||||
TYPE_PARCOURS = CodesParcours.LP
|
||||
NAME = "LP"
|
||||
NB_SEM = 1
|
||||
COMPENSATION_UE = False
|
||||
@ -332,7 +380,7 @@ register_parcours(ParcoursLP())
|
||||
class ParcoursLP2sem(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres")"""
|
||||
|
||||
TYPE_PARCOURS = 210
|
||||
TYPE_PARCOURS = CodesParcours.LP2sem
|
||||
NAME = "LP2sem"
|
||||
NB_SEM = 2
|
||||
COMPENSATION_UE = True
|
||||
@ -345,7 +393,7 @@ register_parcours(ParcoursLP2sem())
|
||||
class ParcoursLP2semEvry(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres", U. Evry)"""
|
||||
|
||||
TYPE_PARCOURS = 220
|
||||
TYPE_PARCOURS = CodesParcours.LP2semEvry
|
||||
NAME = "LP2semEvry"
|
||||
NB_SEM = 2
|
||||
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_
|
||||
# vérifiés par ScoDoc)
|
||||
|
||||
TYPE_PARCOURS = 230
|
||||
TYPE_PARCOURS = CodesParcours.LP2014
|
||||
NAME = "LP2014"
|
||||
NB_SEM = 1
|
||||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
||||
@ -415,7 +463,7 @@ register_parcours(ParcoursLP2014())
|
||||
class ParcoursLP2sem2014(ParcoursLP):
|
||||
"""Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)"""
|
||||
|
||||
TYPE_PARCOURS = 240
|
||||
TYPE_PARCOURS = CodesParcours.LP2sem2014
|
||||
NAME = "LP2014_2sem"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -427,7 +475,7 @@ register_parcours(ParcoursLP2sem2014())
|
||||
class ParcoursM2(TypeParcours):
|
||||
"""Master 2 (en deux "semestres")"""
|
||||
|
||||
TYPE_PARCOURS = 250
|
||||
TYPE_PARCOURS = CodesParcours.M2
|
||||
NAME = "M2sem"
|
||||
NB_SEM = 2
|
||||
COMPENSATION_UE = True
|
||||
@ -440,7 +488,7 @@ register_parcours(ParcoursM2())
|
||||
class ParcoursM2noncomp(ParcoursM2):
|
||||
"""Master 2 (en deux "semestres") sans compensation"""
|
||||
|
||||
TYPE_PARCOURS = 251
|
||||
TYPE_PARCOURS = CodesParcours.M2noncomp
|
||||
NAME = "M2noncomp"
|
||||
COMPENSATION_UE = False
|
||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||
@ -452,7 +500,7 @@ register_parcours(ParcoursM2noncomp())
|
||||
class ParcoursMono(TypeParcours):
|
||||
"""Formation générique en une session"""
|
||||
|
||||
TYPE_PARCOURS = 300
|
||||
TYPE_PARCOURS = CodesParcours.Mono
|
||||
NAME = "Mono"
|
||||
NB_SEM = 1
|
||||
COMPENSATION_UE = False
|
||||
@ -465,7 +513,7 @@ register_parcours(ParcoursMono())
|
||||
class ParcoursLegacy(TypeParcours):
|
||||
"""DUT (ancien ScoDoc, ne plus utiliser)"""
|
||||
|
||||
TYPE_PARCOURS = 0
|
||||
TYPE_PARCOURS = CodesParcours.Legacy
|
||||
NAME = "DUT"
|
||||
NB_SEM = 4
|
||||
COMPENSATION_UE = None # backward compat: defini dans formsemestre
|
||||
@ -499,7 +547,7 @@ class ParcoursBachelorISCID6(ParcoursISCID):
|
||||
"""ISCID: Bachelor en 3 ans (6 sem.)"""
|
||||
|
||||
NAME = "ParcoursBachelorISCID6"
|
||||
TYPE_PARCOURS = 1001
|
||||
TYPE_PARCOURS = CodesParcours.ISCID6
|
||||
NAME = ""
|
||||
NB_SEM = 6
|
||||
ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme
|
||||
@ -510,7 +558,7 @@ register_parcours(ParcoursBachelorISCID6())
|
||||
|
||||
class ParcoursMasterISCID4(ParcoursISCID):
|
||||
"ISCID: Master en 2 ans (4 sem.)"
|
||||
TYPE_PARCOURS = 1002
|
||||
TYPE_PARCOURS = CodesParcours.ISCID4
|
||||
NAME = "ParcoursMasterISCID4"
|
||||
NB_SEM = 4
|
||||
ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme
|
||||
@ -536,7 +584,7 @@ class ParcoursUCAC(TypeParcours):
|
||||
class ParcoursLicenceUCAC3(ParcoursUCAC):
|
||||
"""UCAC: Licence en 3 sessions d'un an"""
|
||||
|
||||
TYPE_PARCOURS = 501
|
||||
TYPE_PARCOURS = CodesParcours.LicenceUCAC3
|
||||
NAME = "Licence UCAC en 3 sessions d'un an"
|
||||
NB_SEM = 3
|
||||
|
||||
@ -547,7 +595,7 @@ register_parcours(ParcoursLicenceUCAC3())
|
||||
class ParcoursMasterUCAC2(ParcoursUCAC):
|
||||
"""UCAC: Master en 2 sessions d'un an"""
|
||||
|
||||
TYPE_PARCOURS = 502
|
||||
TYPE_PARCOURS = CodesParcours.MasterUCAC2
|
||||
NAME = "Master UCAC en 2 sessions d'un an"
|
||||
NB_SEM = 2
|
||||
|
||||
@ -558,7 +606,7 @@ register_parcours(ParcoursMasterUCAC2())
|
||||
class ParcoursMonoUCAC(ParcoursUCAC):
|
||||
"""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"
|
||||
NB_SEM = 1
|
||||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||||
@ -570,7 +618,7 @@ register_parcours(ParcoursMonoUCAC())
|
||||
class Parcours6Sem(TypeParcours):
|
||||
"""Parcours générique en 6 semestres"""
|
||||
|
||||
TYPE_PARCOURS = 600
|
||||
TYPE_PARCOURS = CodesParcours.GEN_6_SEM
|
||||
NAME = "Formation en 6 semestres"
|
||||
NB_SEM = 6
|
||||
COMPENSATION_UE = True
|
||||
@ -592,7 +640,7 @@ register_parcours(Parcours6Sem())
|
||||
class ParcoursMasterLMD(TypeParcours):
|
||||
"""Master générique en 4 semestres dans le LMD"""
|
||||
|
||||
TYPE_PARCOURS = 402
|
||||
TYPE_PARCOURS = CodesParcours.MasterLMD
|
||||
NAME = "Master LMD"
|
||||
NB_SEM = 4
|
||||
COMPENSATION_UE = True # variabale inutilisée
|
||||
@ -605,7 +653,7 @@ register_parcours(ParcoursMasterLMD())
|
||||
class ParcoursMasterIG(ParcoursMasterLMD):
|
||||
"""Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)"""
|
||||
|
||||
TYPE_PARCOURS = 403
|
||||
TYPE_PARCOURS = CodesParcours.MasterIG
|
||||
NAME = "Master IG P13"
|
||||
BARRE_MOY = 10.0
|
||||
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
||||
|
@ -420,8 +420,8 @@ def module_edit(module_id=None):
|
||||
"input_type": "menu",
|
||||
"title": "Type",
|
||||
"explanation": "",
|
||||
"labels": ("Standard", "Malus"),
|
||||
"allowed_values": (str(scu.MODULE_STANDARD), str(scu.MODULE_MALUS)),
|
||||
"labels": [x.name.capitalize() for x in scu.ModuleType],
|
||||
"allowed_values": [str(int(x)) for x in scu.ModuleType],
|
||||
"enabled": unlocked,
|
||||
},
|
||||
),
|
||||
|
@ -775,8 +775,12 @@ def _ue_table_ues(
|
||||
)
|
||||
else:
|
||||
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(
|
||||
_ue_table_matieres(
|
||||
func_html_list(
|
||||
parcours,
|
||||
ue,
|
||||
editable,
|
||||
@ -837,6 +841,8 @@ def _ue_table_matieres(
|
||||
delete_disabled_icon,
|
||||
)
|
||||
)
|
||||
if not parcours.UE_IS_MODULE:
|
||||
H.append("</li>")
|
||||
if not matieres:
|
||||
H.append("<li>Aucune matière dans cette UE ! ")
|
||||
if editable:
|
||||
@ -855,6 +861,76 @@ def _ue_table_matieres(
|
||||
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(
|
||||
parcours,
|
||||
mat,
|
||||
@ -866,6 +942,10 @@ def _ue_table_modules(
|
||||
arrow_none,
|
||||
delete_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"""
|
||||
H = ['<ul class="notes_module_list">']
|
||||
@ -948,8 +1028,8 @@ def _ue_table_modules(
|
||||
)
|
||||
H.append("</li>")
|
||||
if not modules:
|
||||
H.append("<li>Aucun module dans cette matière ! ")
|
||||
if editable:
|
||||
H.append(f"<li>{empty_list_msg} ! ")
|
||||
if editable and add_suppress_link:
|
||||
H.append(
|
||||
f"""<a class="stdlink" href="{
|
||||
url_for("notes.matiere_delete",
|
||||
@ -963,11 +1043,10 @@ def _ue_table_modules(
|
||||
f"""<li> <a class="stdlink" href="{
|
||||
url_for("notes.module_create",
|
||||
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("</li>")
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
|
@ -32,13 +32,12 @@ import base64
|
||||
import bisect
|
||||
import copy
|
||||
import datetime
|
||||
from enum import IntEnum
|
||||
import json
|
||||
from hashlib import md5
|
||||
import numbers
|
||||
import os
|
||||
import pydot
|
||||
import re
|
||||
import requests
|
||||
import _thread
|
||||
import time
|
||||
import unicodedata
|
||||
@ -46,6 +45,8 @@ import urllib
|
||||
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
||||
|
||||
from PIL import Image as PILImage
|
||||
import pydot
|
||||
import requests
|
||||
|
||||
from flask import g, request
|
||||
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_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_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 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.0.61"
|
||||
SCOVERSION = "9.1.0"
|
||||
|
||||
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