859 lines
26 KiB
Python
859 lines
26 KiB
Python
# -*- mode: python -*-
|
||
# -*- coding: utf-8 -*-
|
||
|
||
##############################################################################
|
||
#
|
||
# Gestion scolarite IUT
|
||
#
|
||
# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved.
|
||
#
|
||
# This program is free software; you can redistribute it and/or modify
|
||
# it under the terms of the GNU General Public License as published by
|
||
# the Free Software Foundation; either version 2 of the License, or
|
||
# (at your option) any later version.
|
||
#
|
||
# This program is distributed in the hope that it will be useful,
|
||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
# GNU General Public License for more details.
|
||
#
|
||
# You should have received a copy of the GNU General Public License
|
||
# along with this program; if not, write to the Free Software
|
||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
#
|
||
# Emmanuel Viennet emmanuel.viennet@univ-paris13.fr
|
||
#
|
||
##############################################################################
|
||
|
||
"""Semestres: Codes gestion cursus (constantes)
|
||
Attention: ne pas confondre avec les "parcours" du BUT.
|
||
Renommage des anciens "parcours" -> "cursus" effectué en 9.4.41
|
||
"""
|
||
import enum
|
||
|
||
import numpy as np
|
||
|
||
from app import log
|
||
|
||
|
||
@enum.unique
|
||
class CodesCursus(enum.IntEnum):
|
||
"""Codes numériques des cursus (ex parcours), 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)
|
||
|
||
# Barre sur moyenne générale utilisée pour compensations semestres:
|
||
NOTES_BARRE_GEN = 10.0
|
||
NOTES_BARRE_GEN_COMPENSATION = NOTES_BARRE_GEN - NOTES_TOLERANCE
|
||
|
||
# ----------------------------------------------------------------
|
||
# Types d'UE:
|
||
UE_STANDARD = 0 # UE "fondamentale"
|
||
UE_SPORT = 1 # bonus "sport"
|
||
UE_STAGE_LP = 2 # ue "projet tuteuré et stage" dans les Lic. Pro.
|
||
UE_STAGE_10 = 3 # ue "stage" avec moyenne requise > 10
|
||
UE_ELECTIVE = 4 # UE "élective" dans certains cursus (UCAC?, ISCID)
|
||
UE_PROFESSIONNELLE = 5 # UE "professionnelle" (ISCID, ...)
|
||
UE_OPTIONNELLE = 6 # UE non fondamentales (ILEPS, ...)
|
||
|
||
|
||
def ue_is_fondamentale(ue_type):
|
||
return ue_type in (UE_STANDARD, UE_STAGE_LP, UE_PROFESSIONNELLE)
|
||
|
||
|
||
def ue_is_professionnelle(ue_type):
|
||
return (
|
||
ue_type == UE_PROFESSIONNELLE
|
||
) # NB: les UE_PROFESSIONNELLE sont à la fois fondamentales et pro
|
||
|
||
|
||
UE_TYPE_NAME = {
|
||
UE_STANDARD: "Standard",
|
||
UE_SPORT: "Sport/Culture (points bonus)",
|
||
UE_STAGE_LP: "Projet tuteuré et stage (Lic. Pro.)",
|
||
UE_STAGE_10: "Stage (moyenne min. 10/20)",
|
||
UE_ELECTIVE: "Elective (ISCID)",
|
||
UE_PROFESSIONNELLE: "Professionnelle (ISCID)",
|
||
UE_OPTIONNELLE: "Optionnelle",
|
||
# UE_FONDAMENTALE : '"Fondamentale" (eg UCAC)',
|
||
# UE_OPTIONNELLE : '"Optionnelle" (UCAC)'
|
||
}
|
||
|
||
# Couleurs RGB (dans [0.,1.]) des UE pour les bulletins:
|
||
UE_DEFAULT_COLOR = (150 / 255.0, 200 / 255.0, 180 / 255.0)
|
||
UE_COLORS = {
|
||
UE_STANDARD: UE_DEFAULT_COLOR,
|
||
UE_SPORT: (0.40, 0.90, 0.50),
|
||
UE_STAGE_LP: (0.80, 0.90, 0.90),
|
||
}
|
||
UE_SEM_DEFAULT = 1000000 # indice semestre des UE sans modules
|
||
|
||
# ------------------------------------------------------------------
|
||
# Codes proposés par ADIUT / Apogee
|
||
ABAN = "ABAN"
|
||
ABL = "ABL"
|
||
ADM = "ADM" # moyenne gen., barres UE, assiduité: sem. validé
|
||
ADC = "ADC" # admis par compensation (eg moy(S1, S2) > 10)
|
||
ADSUP = "ADSUP" # BUT: UE ou RCUE validé par niveau supérieur
|
||
ADJ = "ADJ" # admis par le jury
|
||
ADJR = "ADJR" # UE admise car son RCUE est ADJ
|
||
ATT = "ATT" #
|
||
ATJ = "ATJ" # pb assiduité: décision repoussée au semestre suivant
|
||
ATB = "ATB"
|
||
AJ = "AJ"
|
||
CMP = "CMP" # utile pour UE seulement (indique UE acquise car semestre acquis)
|
||
DEF = "DEF" # défaillance (n'est pas un code jury dans scodoc mais un état, comme inscrit ou demission)
|
||
DEM = "DEM"
|
||
EXCLU = "EXCLU"
|
||
JSD = "JSD" # jury tenu mais pas de code (Jury Sans Décision)
|
||
NAR = "NAR"
|
||
PASD = "PASD"
|
||
PAS1NCI = "PAS1NCI"
|
||
RAT = "RAT" # en attente rattrapage, sera ATT dans Apogée
|
||
RED = "RED"
|
||
UEBSL = "UEBSL" # UE blanchie
|
||
|
||
# codes actions
|
||
REDOANNEE = "REDOANNEE" # redouble annee (va en Sn-1)
|
||
REDOSEM = "REDOSEM" # redouble semestre (va en Sn)
|
||
RA_OR_NEXT = "RA_OR_NEXT" # redouble annee ou passe en Sn+1
|
||
RA_OR_RS = "RA_OR_RS" # redouble annee ou semestre
|
||
RS_OR_NEXT = "RS_OR_NEXT" # redouble semestre ou passe en Sn+1
|
||
NEXT_OR_NEXT2 = "NEXT_OR_NEXT2" # passe en suivant (Sn+1) ou sur-suivant (Sn+2)
|
||
NEXT = "NEXT"
|
||
NEXT2 = "NEXT2" # passe au sur-suivant (Sn+2)
|
||
REO = "REO"
|
||
BUG = "BUG"
|
||
|
||
ALL = "ALL"
|
||
|
||
# Explication des codes (de semestre ou d'UE)
|
||
CODES_EXPL = {
|
||
ABAN: "Non évalué pour manque d’assiduité: non présentation des notes de l'étudiant au jury",
|
||
ABL: "Année blanche",
|
||
ADC: "Validé par compensation",
|
||
ADJ: "Validé par le Jury",
|
||
ADJR: "UE validée car son RCUE est validé ADJ par le jury",
|
||
ADM: "Validé",
|
||
ADSUP: "UE ou RCUE validé car le niveau supérieur est validé",
|
||
AJ: "Ajourné (ou UE/BC de BUT en attente pour problème de moyenne)",
|
||
ATB: "Décision en attente d'un autre semestre (au moins une UE sous la barre)",
|
||
ATJ: "Décision en attente d'un autre semestre (assiduité insuffisante)",
|
||
ATT: "Décision en attente d'un autre semestre (faute d'atteindre la moyenne)",
|
||
CMP: """Code UE acquise car semestre acquis, ou, en BUT, acquise par
|
||
compensation UE avec l’UE de même compétence et de même année (ECTS acquis).
|
||
Utilisé aussi pour les blocs de compétences BUT (RCUE).
|
||
""",
|
||
DEF: "Défaillant, pas ou peu de notes par arrêt de la formation. Non évalué par manque assiduité.",
|
||
DEM: "Démission",
|
||
EXCLU: "Exclusion: décision réservée à des décisions disciplinaires",
|
||
NAR: "Non admis, réorientation, non autorisé à redoubler",
|
||
PASD: """Année BUT: non admis, mais passage de droit:
|
||
Passage en Année Supérieure de Droit (+ de 50% des UE VAL et RCUE Ajourné(s) >=8)
|
||
""",
|
||
PAS1NCI: """Année BUT: Non admis, mais passage par décision de jury:
|
||
Passage en Année Supérieure avec au moins 1 Niveau de Compétence Insuffisant (RCUE<8)
|
||
""",
|
||
RAT: "En attente d'un rattrapage",
|
||
RED: "Année: Ajourné, mais autorisé à redoubler",
|
||
UEBSL: "UE blanchie",
|
||
}
|
||
|
||
|
||
# Les codes de semestres:
|
||
CODES_JURY_SEM = {ADC, ADJ, ADM, AJ, ATB, ATJ, ATT, DEF, NAR, RAT}
|
||
CODES_SEM_VALIDES_DE_DROIT = {ADM, ADC}
|
||
CODES_SEM_VALIDES = CODES_SEM_VALIDES_DE_DROIT | {ADJ} # semestre validé
|
||
CODES_SEM_ATTENTES = {ATT, ATB, ATJ} # semestre en attente
|
||
|
||
CODES_SEM_REO = {NAR} # reorientation
|
||
|
||
# Les codes d'UEs
|
||
CODES_JURY_UE = {ADM, CMP, ADJ, ADJR, ADSUP, AJ, ATJ, RAT, DEF, ABAN, DEM, UEBSL}
|
||
CODES_UE_VALIDES_DE_DROIT = {ADM, CMP} # validation "de droit"
|
||
CODES_UE_VALIDES = CODES_UE_VALIDES_DE_DROIT | {ADJ, ADJR, ADSUP}
|
||
"UE validée"
|
||
CODES_UE_CAPITALISANTS = {ADM}
|
||
"UE capitalisée"
|
||
|
||
CODES_RCUE_VALIDES_DE_DROIT = {ADM, CMP}
|
||
CODES_RCUE_VALIDES = CODES_RCUE_VALIDES_DE_DROIT | {ADJ, ADSUP}
|
||
"Niveau RCUE validé"
|
||
|
||
# Pour le BUT:
|
||
CODES_ANNEE_BUT_VALIDES_DE_DROIT = {ADM, PASD} # PASD pour enregistrement auto
|
||
CODES_ANNEE_BUT_VALIDES = {ADM, ADSUP}
|
||
CODES_ANNEE_ARRET = {DEF, DEM, ABAN, ABL}
|
||
BUT_BARRE_UE8 = 8.0 - NOTES_TOLERANCE
|
||
BUT_BARRE_UE = BUT_BARRE_RCUE = 10.0 - NOTES_TOLERANCE
|
||
BUT_RCUE_SUFFISANT = 8.0 - NOTES_TOLERANCE
|
||
BUT_CODES_PASSAGE = {
|
||
ADM,
|
||
ADJ,
|
||
PASD,
|
||
PAS1NCI,
|
||
ATJ,
|
||
}
|
||
# les codes, du plus "défavorable" à l'étudiant au plus favorable:
|
||
# (valeur par défaut 0)
|
||
BUT_CODES_ORDER = {
|
||
ABAN: 0,
|
||
ABL: 0,
|
||
DEM: 0,
|
||
DEF: 0,
|
||
EXCLU: 0,
|
||
NAR: 0,
|
||
UEBSL: 0,
|
||
RAT: 5,
|
||
RED: 6,
|
||
AJ: 10,
|
||
ATJ: 20,
|
||
CMP: 50,
|
||
ADC: 50,
|
||
PAS1NCI: 50,
|
||
PASD: 60,
|
||
ADJR: 90,
|
||
ADSUP: 90,
|
||
ADJ: 90,
|
||
ADM: 100,
|
||
}
|
||
|
||
|
||
def code_semestre_validant(code: str) -> bool:
|
||
"Vrai si ce CODE entraine la validation du semestre"
|
||
return code in CODES_SEM_VALIDES
|
||
|
||
|
||
def code_semestre_attente(code: str) -> bool:
|
||
"Vrai si ce CODE est un code d'attente (semestre validable plus tard par jury ou compensation)"
|
||
return code in CODES_SEM_ATTENTES
|
||
|
||
|
||
def code_ue_validant(code: str) -> bool:
|
||
"Vrai si ce code d'UE est validant (ie attribue les ECTS)"
|
||
return code in CODES_UE_VALIDES
|
||
|
||
|
||
def code_rcue_validant(code: str) -> bool:
|
||
"Vrai si ce code d'RCUE est validant"
|
||
return code in CODES_RCUE_VALIDES
|
||
|
||
|
||
def code_annee_validant(code: str) -> bool:
|
||
"Vrai si code d'année BUT validant"
|
||
return code in CODES_ANNEE_BUT_VALIDES
|
||
|
||
|
||
DEVENIR_EXPL = {
|
||
NEXT: "Passage au semestre suivant",
|
||
REDOANNEE: "Redoublement année",
|
||
REDOSEM: "Redoublement semestre",
|
||
RA_OR_NEXT: "Passage, ou redoublement année",
|
||
RA_OR_RS: "Redoublement année, ou redoublement semestre", # slt si sems decales
|
||
RS_OR_NEXT: "Passage, ou redoublement semestre",
|
||
NEXT_OR_NEXT2: "Passage en semestre suivant ou à celui d'après",
|
||
NEXT2: "Passage au sur-suivant",
|
||
REO: "Réorientation",
|
||
}
|
||
|
||
# Devenirs autorises dans les cursus sans semestres décalés:
|
||
DEVENIRS_STD = {NEXT: 1, REDOANNEE: 1, RA_OR_NEXT: 1, REO: 1}
|
||
|
||
# Devenirs autorises dans les cursus en un seul semestre, semestre_id==-1 (licences ?)
|
||
DEVENIRS_MONO = {REDOANNEE: 1, REO: 1}
|
||
|
||
# Devenirs supplementaires (en mode manuel) pour les cursus avec semestres decales
|
||
DEVENIRS_DEC = {REDOSEM: 1, RS_OR_NEXT: 1}
|
||
|
||
# Devenirs en n+2 (sautant un semestre) (si semestres décalés et s'il ne manque qu'un semestre avant le n+2)
|
||
DEVENIRS_NEXT2 = {NEXT_OR_NEXT2: 1, NEXT2: 1}
|
||
|
||
NO_SEMESTRE_ID = -1 # code semestre si pas de semestres
|
||
|
||
|
||
# Règles gestion cursus
|
||
class DUTRule(object):
|
||
def __init__(self, rule_id, premise, conclusion):
|
||
self.rule_id = rule_id
|
||
self.premise = premise
|
||
self.conclusion = conclusion
|
||
# self.code, self.codes_ue, self.devenir, self.action, self.explication = conclusion
|
||
|
||
def match(self, state):
|
||
"True if state match rule premise"
|
||
assert len(state) == len(self.premise)
|
||
for i, stat in enumerate(state):
|
||
prem = self.premise[i]
|
||
if isinstance(prem, (list, tuple)):
|
||
if not stat in prem:
|
||
return False
|
||
else:
|
||
if prem not in (ALL, stat):
|
||
return False
|
||
return True
|
||
|
||
|
||
# Types de cursus
|
||
DEFAULT_TYPE_CURSUS = 700 # (BUT) pour le menu de creation nouvelle formation
|
||
|
||
|
||
class TypeCursus:
|
||
TYPE_CURSUS = None # id, utilisé par notes_formation.type_parcours
|
||
NAME = None # required
|
||
NB_SEM = 1 # Nombre de semestres
|
||
COMPENSATION_UE = True # inutilisé
|
||
BARRE_MOY = 10.0
|
||
BARRE_UE_DEFAULT = 8.0
|
||
BARRE_UE_DISPLAY_WARNING = 8.0
|
||
BARRE_UE = {}
|
||
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
||
NOTES_BARRE_VALID_UE = NOTES_BARRE_VALID_UE_TH - NOTES_TOLERANCE # barre sur UE
|
||
ALLOW_SEM_SKIP = False # Passage: autorise-t-on les sauts de semestres ?
|
||
SESSION_NAME = "semestre"
|
||
SESSION_NAME_A = "du "
|
||
SESSION_ABBRV = "S" # S1, S2, ...
|
||
UNUSED_CODES = set() # Ensemble des codes jury non autorisés dans ce cursus
|
||
UE_IS_MODULE = False # 1 seul module par UE (si plusieurs modules, etudiants censéments inscrits à un seul d'entre eux)
|
||
ECTS_ONLY = False # Cursus avec progression basée uniquement sur les ECTS
|
||
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
|
||
USE_REFERENTIEL_COMPETENCES = False # Lien avec ref. comp.
|
||
ECTS_FONDAMENTAUX_PER_YEAR = 0.0 # pour ISCID, deprecated
|
||
|
||
def check(self, formation=None):
|
||
return True, "" # status, diagnostic_message
|
||
|
||
def get_barre_ue(self, ue_type, tolerance=True):
|
||
"""Barre pour cette UE (la valeur peut dépendre du type d'UE).
|
||
Si tolerance, diminue de epsilon pour éviter les effets d'arrondis.
|
||
"""
|
||
if tolerance:
|
||
t = NOTES_TOLERANCE
|
||
else:
|
||
t = 0.0
|
||
return self.BARRE_UE.get(ue_type, self.BARRE_UE_DEFAULT) - t
|
||
|
||
def ues_sous_barre(self, ues_status):
|
||
"""Filtre les ues: liste celles ayant une moyenne sous la barre
|
||
|
||
ues_status est une liste de dict ayant des entrées 'moy' et 'coef_ue'
|
||
"""
|
||
return [
|
||
ue_status
|
||
for ue_status in ues_status
|
||
if ue_status["coef_ue"]
|
||
and isinstance(ue_status["moy"], float)
|
||
and ue_status["moy"] < self.get_barre_ue(ue_status["ue"]["type"])
|
||
]
|
||
|
||
def check_barre_ues(self, ues_status):
|
||
"""True si la ou les conditions sur les UE sont valides
|
||
Par defaut, vrai si les moyennes d'UE sont au dessus de la barre.
|
||
Le cas des LP2014 est plus compliqué.
|
||
"""
|
||
n = len(self.ues_sous_barre(ues_status))
|
||
if n == 0:
|
||
return True, "les UEs sont au dessus des barres"
|
||
else:
|
||
return False, """<b>%d UE sous la barre</b>""" % n
|
||
|
||
|
||
# Cursus définis (instances de sous-classes de TypeCursus):
|
||
SCO_CURSUS: dict[int, TypeCursus] = {} # type : Cursus
|
||
|
||
|
||
def register_cursus(cursus: TypeCursus):
|
||
SCO_CURSUS[int(cursus.TYPE_CURSUS)] = cursus
|
||
|
||
|
||
class CursusBUT(TypeCursus):
|
||
"""BUT Bachelor Universitaire de Technologie"""
|
||
|
||
TYPE_CURSUS = 700
|
||
NAME = "BUT"
|
||
NB_SEM = 6
|
||
COMPENSATION_UE = False
|
||
APC_SAE = True
|
||
USE_REFERENTIEL_COMPETENCES = True
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||
|
||
|
||
register_cursus(CursusBUT())
|
||
|
||
|
||
class CursusDUT(TypeCursus):
|
||
"""DUT selon l'arrêté d'août 2005"""
|
||
|
||
TYPE_CURSUS = 100
|
||
NAME = "DUT"
|
||
NB_SEM = 4
|
||
COMPENSATION_UE = True
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||
|
||
|
||
register_cursus(CursusDUT())
|
||
|
||
|
||
class CursusDUT4(CursusDUT):
|
||
"""DUT (en 4 semestres sans compensations)"""
|
||
|
||
TYPE_CURSUS = 110
|
||
NAME = "DUT4"
|
||
COMPENSATION_UE = False
|
||
|
||
|
||
register_cursus(CursusDUT4())
|
||
|
||
|
||
class CursusDUTMono(TypeCursus):
|
||
"""DUT en un an (FC, Années spéciales)"""
|
||
|
||
TYPE_CURSUS = 120
|
||
NAME = "DUT"
|
||
NB_SEM = 1
|
||
COMPENSATION_UE = False
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusDUTMono())
|
||
|
||
|
||
class CursusDUT2(CursusDUT):
|
||
"""DUT en deux semestres (par ex.: années spéciales semestrialisées)"""
|
||
|
||
TYPE_CURSUS = CodesCursus.DUT2
|
||
NAME = "DUT2"
|
||
NB_SEM = 2
|
||
|
||
|
||
register_cursus(CursusDUT2())
|
||
|
||
|
||
class CursusLP(TypeCursus):
|
||
"""Licence Pro (en un "semestre")
|
||
(pour anciennes LP. Après 2014, préférer CursusLP2014)
|
||
"""
|
||
|
||
TYPE_CURSUS = CodesCursus.LP
|
||
NAME = "LP"
|
||
NB_SEM = 1
|
||
COMPENSATION_UE = False
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
||
BARRE_UE_DEFAULT = 0.0 # pas de barre sur les UE "normales"
|
||
BARRE_UE = {UE_STAGE_LP: 10.0}
|
||
# pas de codes ATT en LP
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusLP())
|
||
|
||
|
||
class CursusLP2sem(CursusLP):
|
||
"""Licence Pro (en deux "semestres")"""
|
||
|
||
TYPE_CURSUS = CodesCursus.LP2sem
|
||
NAME = "LP2sem"
|
||
NB_SEM = 2
|
||
COMPENSATION_UE = True
|
||
UNUSED_CODES = set((ADC,)) # autorise les codes ATT et ATB, mais pas ADC.
|
||
|
||
|
||
register_cursus(CursusLP2sem())
|
||
|
||
|
||
class CursusLP2semEvry(CursusLP):
|
||
"""Licence Pro (en deux "semestres", U. Evry)"""
|
||
|
||
TYPE_CURSUS = CodesCursus.LP2semEvry
|
||
NAME = "LP2semEvry"
|
||
NB_SEM = 2
|
||
COMPENSATION_UE = True
|
||
|
||
|
||
register_cursus(CursusLP2semEvry())
|
||
|
||
|
||
class CursusLP2014(TypeCursus):
|
||
"""Licence Pro (en un "semestre"), selon arrêté du 22/01/2014"""
|
||
|
||
# Note: texte de référence
|
||
# https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000000397481
|
||
|
||
# Article 7: Le stage et le projet tutoré constituent chacun une unité d'enseignement.
|
||
# Article 10:
|
||
# La licence professionnelle est décernée aux étudiants qui ont obtenu à la fois une moyenne
|
||
# générale égale ou supérieure à 10 sur 20 à l'ensemble des unités d'enseignement, y compris le
|
||
# projet tutoré et le stage, et une moyenne égale ou supérieure à 10 sur 20 à l'ensemble constitué
|
||
# du projet tutoré et du stage.
|
||
|
||
# Actuellement, les points suivants de l'article 7 ("Les unités d'enseignement sont affectées par
|
||
# l'établissement d'un coefficient qui peut varier dans un rapport de 1 à 3. ", etc ne sont _pas_
|
||
# vérifiés par ScoDoc)
|
||
|
||
TYPE_CURSUS = CodesCursus.LP2014
|
||
NAME = "LP2014"
|
||
NB_SEM = 1
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_LP]
|
||
BARRE_UE_DEFAULT = 0.0 # pas de barre sur les UE "normales"
|
||
# pas de codes ATT en LP
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
# Specifique aux LP
|
||
BARRE_MOY_UE_STAGE_PROJET = 10.0
|
||
|
||
def check_barre_ues(self, ues_status):
|
||
"""True si la ou les conditions sur les UE sont valides
|
||
Article 10: "une moyenne égale ou supérieure à 10 sur 20 à l'ensemble constitué
|
||
du projet tutoré et du stage."
|
||
"""
|
||
# Les UE de type "projet ou stage" ayant des notes
|
||
mc_stages_proj = [
|
||
(ue_status["moy"], ue_status["coef_ue"])
|
||
for ue_status in ues_status
|
||
if ue_status["ue"]["type"] == UE_STAGE_LP
|
||
and np.issubdtype(type(ue_status["moy"]), np.floating)
|
||
]
|
||
# Moyenne des moyennes:
|
||
sum_coef = sum(x[1] for x in mc_stages_proj)
|
||
if sum_coef > 0.0:
|
||
moy = sum([x[0] * x[1] for x in mc_stages_proj]) / sum_coef
|
||
ok = moy > (self.BARRE_MOY_UE_STAGE_PROJET - NOTES_TOLERANCE)
|
||
if ok:
|
||
return True, "moyenne des UE de stages et projets au dessus de 10"
|
||
else:
|
||
return (
|
||
False,
|
||
"<b>moyenne des UE de stages et projets inférieure à 10</b>",
|
||
)
|
||
else:
|
||
return True, "" # pas de coef, condition ok
|
||
|
||
|
||
register_cursus(CursusLP2014())
|
||
|
||
|
||
class CursusLP2sem2014(CursusLP):
|
||
"""Licence Pro (en deux "semestres", selon arrêté du 22/01/2014)"""
|
||
|
||
TYPE_CURSUS = CodesCursus.LP2sem2014
|
||
NAME = "LP2014_2sem"
|
||
NB_SEM = 2
|
||
|
||
|
||
register_cursus(CursusLP2sem2014())
|
||
|
||
|
||
# Masters: M2 en deux semestres
|
||
class CursusM2(TypeCursus):
|
||
"""Master 2 (en deux "semestres")"""
|
||
|
||
TYPE_CURSUS = CodesCursus.M2
|
||
NAME = "M2sem"
|
||
NB_SEM = 2
|
||
COMPENSATION_UE = True
|
||
UNUSED_CODES = set((ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusM2())
|
||
|
||
|
||
class CursusM2noncomp(CursusM2):
|
||
"""Master 2 (en deux "semestres") sans compensation"""
|
||
|
||
TYPE_CURSUS = CodesCursus.M2noncomp
|
||
NAME = "M2noncomp"
|
||
COMPENSATION_UE = False
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusM2noncomp())
|
||
|
||
|
||
class CursusMono(TypeCursus):
|
||
"""Formation générique en une session"""
|
||
|
||
TYPE_CURSUS = CodesCursus.Mono
|
||
NAME = "Mono"
|
||
NB_SEM = 1
|
||
COMPENSATION_UE = False
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusMono())
|
||
|
||
|
||
class CursusLegacy(TypeCursus):
|
||
"""DUT (ancien ScoDoc, ne plus utiliser)"""
|
||
|
||
TYPE_CURSUS = CodesCursus.Legacy
|
||
NAME = "DUT"
|
||
NB_SEM = 4
|
||
COMPENSATION_UE = None # backward compat: defini dans formsemestre
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT]
|
||
|
||
|
||
register_cursus(CursusLegacy())
|
||
|
||
|
||
class CursusISCID(TypeCursus):
|
||
"""Superclasse pour les cursus de l'ISCID"""
|
||
|
||
# SESSION_NAME = "année"
|
||
# SESSION_NAME_A = "de l'"
|
||
# SESSION_ABBRV = 'A' # A1, A2, ...
|
||
COMPENSATION_UE = False
|
||
UNUSED_CODES = set((ADC, ATT, ATB, ATJ))
|
||
UE_IS_MODULE = True # pas de matieres et modules
|
||
ECTS_ONLY = True # jury basés sur les ECTS (pas moyenne generales, pas de barres, pas de compensations)
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_ELECTIVE, UE_PROFESSIONNELLE]
|
||
NOTES_BARRE_VALID_MODULE_TH = 10.0
|
||
NOTES_BARRE_VALID_MODULE = (
|
||
NOTES_BARRE_VALID_MODULE_TH - NOTES_TOLERANCE
|
||
) # barre sur module
|
||
ECTS_BARRE_VALID_YEAR = 60
|
||
ECTS_FONDAMENTAUX_PER_YEAR = 42 # mini pour valider l'annee
|
||
ECTS_PROF_DIPL = 0 # crédits professionnels requis pour obtenir le diplôme
|
||
|
||
|
||
class CursusBachelorISCID6(CursusISCID):
|
||
"""ISCID: Bachelor en 3 ans (6 sem.)"""
|
||
|
||
NAME = "CursusBachelorISCID6"
|
||
TYPE_CURSUS = CodesCursus.ISCID6
|
||
NAME = ""
|
||
NB_SEM = 6
|
||
ECTS_PROF_DIPL = 8 # crédits professionnels requis pour obtenir le diplôme
|
||
|
||
|
||
register_cursus(CursusBachelorISCID6())
|
||
|
||
|
||
class CursusMasterISCID4(CursusISCID):
|
||
"ISCID: Master en 2 ans (4 sem.)"
|
||
TYPE_CURSUS = CodesCursus.ISCID4
|
||
NAME = "CursusMasterISCID4"
|
||
NB_SEM = 4
|
||
ECTS_PROF_DIPL = 15 # crédits professionnels requis pour obtenir le diplôme
|
||
|
||
|
||
register_cursus(CursusMasterISCID4())
|
||
|
||
|
||
class CursusILEPS(TypeCursus):
|
||
"""Superclasse pour les cursus de l'ILEPS"""
|
||
|
||
# SESSION_NAME = "année"
|
||
# SESSION_NAME_A = "de l'"
|
||
# SESSION_ABBRV = 'A' # A1, A2, ...
|
||
COMPENSATION_UE = False
|
||
UNUSED_CODES = set((ADC, ATT, ATB, ATJ))
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_OPTIONNELLE, UE_SPORT]
|
||
# Barre moy gen. pour validation semestre:
|
||
BARRE_MOY = 10.0
|
||
# Barre pour UE ILEPS: 8/20 pour UE standards ("fondamentales")
|
||
# et pas de barre (-1.) pour UE élective.
|
||
BARRE_UE = {UE_STANDARD: 8.0, UE_OPTIONNELLE: 0.0}
|
||
BARRE_UE_DEFAULT = 0.0 # pas de barre sur les autres UE
|
||
|
||
|
||
class CursusLicenceILEPS6(CursusILEPS):
|
||
"""ILEPS: Licence 6 semestres"""
|
||
|
||
TYPE_CURSUS = 1010
|
||
NAME = "LicenceILEPS6"
|
||
NB_SEM = 6
|
||
|
||
|
||
register_cursus(CursusLicenceILEPS6())
|
||
|
||
|
||
class CursusUCAC(TypeCursus):
|
||
"""Règles de validation UCAC"""
|
||
|
||
SESSION_NAME = "année"
|
||
SESSION_NAME_A = "de l'"
|
||
COMPENSATION_UE = False
|
||
BARRE_MOY = 12.0
|
||
NOTES_BARRE_VALID_UE_TH = 12.0 # seuil pour valider UE
|
||
NOTES_BARRE_VALID_UE = NOTES_BARRE_VALID_UE_TH - NOTES_TOLERANCE # barre sur UE
|
||
BARRE_UE_DEFAULT = (
|
||
NOTES_BARRE_VALID_UE_TH # il faut valider tt les UE pour valider l'année
|
||
)
|
||
|
||
|
||
class CursusLicenceUCAC3(CursusUCAC):
|
||
"""UCAC: Licence en 3 sessions d'un an"""
|
||
|
||
TYPE_CURSUS = CodesCursus.LicenceUCAC3
|
||
NAME = "Licence UCAC en 3 sessions d'un an"
|
||
NB_SEM = 3
|
||
|
||
|
||
register_cursus(CursusLicenceUCAC3())
|
||
|
||
|
||
class CursusMasterUCAC2(CursusUCAC):
|
||
"""UCAC: Master en 2 sessions d'un an"""
|
||
|
||
TYPE_CURSUS = CodesCursus.MasterUCAC2
|
||
NAME = "Master UCAC en 2 sessions d'un an"
|
||
NB_SEM = 2
|
||
|
||
|
||
register_cursus(CursusMasterUCAC2())
|
||
|
||
|
||
class CursusMonoUCAC(CursusUCAC):
|
||
"""UCAC: Formation en 1 session de durée variable"""
|
||
|
||
TYPE_CURSUS = CodesCursus.MonoUCAC
|
||
NAME = "Formation UCAC en 1 session de durée variable"
|
||
NB_SEM = 1
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusMonoUCAC())
|
||
|
||
|
||
class Cursus6Sem(TypeCursus):
|
||
"""Cursus générique en 6 semestres"""
|
||
|
||
TYPE_CURSUS = CodesCursus.GEN_6_SEM
|
||
NAME = "Formation en 6 semestres"
|
||
NB_SEM = 6
|
||
COMPENSATION_UE = True
|
||
|
||
|
||
register_cursus(Cursus6Sem())
|
||
|
||
# # En cours d'implémentation:
|
||
# class CursusLicenceLMD(TypeCursus):
|
||
# """Licence standard en 6 semestres dans le LMD"""
|
||
# TYPE_CURSUS = 401
|
||
# NAME = "Licence LMD"
|
||
# NB_SEM = 6
|
||
# COMPENSATION_UE = True
|
||
|
||
# register_cursus(CursusLicenceLMD())
|
||
|
||
|
||
class CursusMasterLMD(TypeCursus):
|
||
"""Master générique en 4 semestres dans le LMD"""
|
||
|
||
TYPE_CURSUS = CodesCursus.MasterLMD
|
||
NAME = "Master LMD"
|
||
NB_SEM = 4
|
||
COMPENSATION_UE = True # variabale inutilisée
|
||
UNUSED_CODES = set((ADC, ATT, ATB))
|
||
|
||
|
||
register_cursus(CursusMasterLMD())
|
||
|
||
|
||
class CursusMasterIG(CursusMasterLMD):
|
||
"""Master de l'Institut Galilée (U. Paris 13) en 4 semestres (LMD)"""
|
||
|
||
TYPE_CURSUS = CodesCursus.MasterIG
|
||
NAME = "Master IG P13"
|
||
BARRE_MOY = 10.0
|
||
NOTES_BARRE_VALID_UE_TH = 10.0 # seuil pour valider UE
|
||
NOTES_BARRE_VALID_UE = NOTES_BARRE_VALID_UE_TH - NOTES_TOLERANCE # barre sur UE
|
||
BARRE_UE_DEFAULT = 7.0 # Les UE normales avec moins de 7/20 sont éliminatoires
|
||
# Specifique à l'UE de stage
|
||
BARRE_MOY_UE_STAGE = 10.0
|
||
ALLOWED_UE_TYPES = [UE_STANDARD, UE_SPORT, UE_STAGE_10]
|
||
|
||
def check_barre_ues(self, ues_status): # inspire de la fonction de CursusLP2014
|
||
"""True si la ou les conditions sur les UE sont valides
|
||
moyenne d'UE > 7, ou > 10 si UE de stage
|
||
"""
|
||
# Il y a-t-il une UE standard sous la barre ?
|
||
ue_sb = [
|
||
ue_status
|
||
for ue_status in ues_status
|
||
if ue_status["ue"]["type"] == UE_STANDARD
|
||
and ue_status["coef_ue"] > 0
|
||
and type(ue_status["moy"]) == float
|
||
and ue_status["moy"] < self.get_barre_ue(ue_status["ue"]["type"])
|
||
]
|
||
if len(ue_sb):
|
||
return (
|
||
False,
|
||
"<b>%d UE sous la barre (%s/20)</b>"
|
||
% (len(ue_sb), self.BARRE_UE_DEFAULT),
|
||
)
|
||
# Les UE de type "stage" ayant des notes
|
||
mc_stages = [
|
||
(ue_status["moy"], ue_status["coef_ue"])
|
||
for ue_status in ues_status
|
||
if ue_status["ue"]["type"] == UE_STAGE_10
|
||
and type(ue_status["moy"]) == float
|
||
]
|
||
# Moyenne des moyennes:
|
||
sum_coef = sum(x[1] for x in mc_stages)
|
||
if sum_coef > 0.0:
|
||
moy = sum([x[0] * x[1] for x in mc_stages]) / sum_coef
|
||
ok = moy > (self.BARRE_MOY_UE_STAGE - NOTES_TOLERANCE)
|
||
if ok:
|
||
return True, "moyenne des UE de stages au dessus de 10"
|
||
else:
|
||
return False, "<b>moyenne des UE de stages inférieure à 10</b>"
|
||
else:
|
||
return True, "" # pas de coef, condition ok
|
||
|
||
|
||
register_cursus(CursusMasterIG())
|
||
|
||
|
||
# Ajouter ici vos cursus, le TYPE_CURSUS devant être unique au monde
|
||
# (avisez sur la liste de diffusion)
|
||
|
||
|
||
# ...
|
||
|
||
|
||
# -------------------------
|
||
_tp = list(SCO_CURSUS.items())
|
||
_tp.sort(key=lambda x: x[1].__doc__) # sort by intitulé
|
||
FORMATION_CURSUS_DESCRS = [p[1].__doc__ for p in _tp] # intitulés (eg pour menu)
|
||
FORMATION_CURSUS_TYPES = [p[0] for p in _tp] # codes numeriques (TYPE_CURSUS)
|
||
|
||
|
||
def get_cursus_from_code(code_cursus: int) -> TypeCursus:
|
||
"renvoie le cursus de code indiqué"
|
||
cursus = SCO_CURSUS.get(code_cursus)
|
||
if cursus is None:
|
||
log(f"Warning: invalid code_cursus: {code_cursus}")
|
||
# default to legacy
|
||
cursus = SCO_CURSUS.get(0)
|
||
return cursus
|