Parcours BUT / Ref. Compétences

+ association UE -> ApcNiveau
    + choix sur la page ue_edit
 + association Module <-> ensemble de ApcParcours
    + choix sur la page module_edit
 + association Module - ApcAppCritique
    ~ choix sur la page module_edit
    TODO: revoir pour présenter les AC du semestre et parcours sélectionnés (JS)

 + association FormSemestre <-> ApcParcours
    + choix sur la page formsemestre_editwithmodules
This commit is contained in:
Emmanuel Viennet 2022-05-22 03:26:39 +02:00
parent 539041fd0d
commit fd8116a772
17 changed files with 278 additions and 132 deletions

View File

@ -17,7 +17,13 @@ def form_ue_choix_niveau(ue: UniteEns) -> str:
"""Form. HTML pour associer une UE à un niveau de compétence"""
ref_comp = ue.formation.referentiel_competence
if ref_comp is None:
return """<div class="ue_choix_niveau">pas de référentiel de compétence</div>"""
return f"""<div class="ue_choix_niveau">
<div class="warning">Pas de référentiel de compétence associé à cette formation !</div>
<div><a class="stdlink" href="{ url_for('notes.refcomp_assoc_formation',
scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)
}">associer un référentiel de compétence</a>
</div>
</div>"""
annee = (ue.semestre_idx + 1) // 2 # 1, 2, 3
niveaux_by_parcours = ref_comp.get_niveaux_by_parcours(annee)
@ -39,7 +45,7 @@ def form_ue_choix_niveau(ue: UniteEns) -> str:
options.append("""</optgroup>""")
options_str = "\n".join(options)
return f"""
<div id="ue_choix_niveau">
<div class="ue_choix_niveau">
<form id="form_ue_choix_niveau">
<b>Niveau de compétence associé:</b>
<select onchange="set_ue_niveau_competence();" data-setter="{

View File

@ -13,7 +13,9 @@ from wtforms import SelectField, SubmitField
class FormationRefCompForm(FlaskForm):
referentiel_competence = SelectField("Référentiels déjà chargés")
referentiel_competence = SelectField(
"Choisir parmi les référentiels déjà chargés :"
)
submit = SubmitField("Valider")
cancel = SubmitField("Annuler")

View File

@ -1,12 +1,9 @@
"""ScoDoc 9 models : Formation BUT 2021
XXX inutilisé
"""
from enum import unique
from typing import Any
from app import db
from app.scodoc.sco_utils import ModuleType
class APCFormation(db.Model):
"""Formation par compétence"""

View File

@ -83,7 +83,7 @@ class ApcReferentielCompetences(db.Model, XMLModel):
formations = db.relationship("Formation", backref="referentiel_competence")
def __repr__(self):
return f"<ApcReferentielCompetences {self.id} {self.specialite}>"
return f"<ApcReferentielCompetences {self.id} {self.specialite!r}>"
def to_dict(self):
"""Représentation complète du ref. de comp.
@ -191,7 +191,7 @@ class ApcCompetence(db.Model, XMLModel):
)
def __repr__(self):
return f"<ApcCompetence {self.id} {self.titre}>"
return f"<ApcCompetence {self.id} {self.titre!r}>"
def to_dict(self):
return {
@ -257,7 +257,8 @@ class ApcNiveau(db.Model, XMLModel):
ues = db.relationship("UniteEns", back_populates="niveau_competence")
def __repr__(self):
return f"<{self.__class__.__name__} ordre={self.ordre} annee={self.annee} {self.competence}>"
return f"""<{self.__class__.__name__} ordre={self.ordre!r} annee={
self.annee!r} {self.competence!r}>"""
def to_dict(self):
return {
@ -341,7 +342,7 @@ class ApcAppCritique(db.Model, XMLModel):
return self.code + " - " + self.titre
def __repr__(self):
return f"<{self.__class__.__name__} {self.code}>"
return f"<{self.__class__.__name__} {self.code!r}>"
def get_saes(self):
"""Liste des SAE associées"""
@ -362,6 +363,20 @@ parcours_modules = db.Table(
)
"""Association parcours <-> modules (many-to-many)"""
parcours_formsemestre = db.Table(
"parcours_formsemestre",
db.Column(
"parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True
),
db.Column(
"formsemestre_id",
db.Integer,
db.ForeignKey("notes_formsemestre.id", ondelete="CASCADE"),
primary_key=True,
),
)
"""Association parcours <-> formsemestre (many-to-many)"""
class ApcParcours(db.Model, XMLModel):
id = db.Column(db.Integer, primary_key=True)
@ -379,7 +394,7 @@ class ApcParcours(db.Model, XMLModel):
)
def __repr__(self):
return f"<{self.__class__.__name__} {self.code}>"
return f"<{self.__class__.__name__} {self.code!r}>"
def to_dict(self):
return {
@ -399,7 +414,7 @@ class ApcAnneeParcours(db.Model, XMLModel):
"numéro de l'année: 1, 2, 3"
def __repr__(self):
return f"<{self.__class__.__name__} ordre={self.ordre} parcours={self.parcours.code}>"
return f"<{self.__class__.__name__} ordre={self.ordre!r} parcours={self.parcours.code!r}>"
def to_dict(self):
return {
@ -450,4 +465,4 @@ class ApcParcoursNiveauCompetence(db.Model):
)
def __repr__(self):
return f"<{self.__class__.__name__} {self.competence}<->{self.annee_parcours} niveau={self.niveau}>"
return f"<{self.__class__.__name__} {self.competence!r}<->{self.annee_parcours!r} niveau={self.niveau!r}>"

View File

@ -515,21 +515,21 @@ class Admission(db.Model):
def to_dict(self, no_nulls=False):
"""Représentation dictionnaire,"""
e = dict(self.__dict__)
e.pop("_sa_instance_state", None)
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
if no_nulls:
for k in e:
if e[k] is None:
for k in d.keys():
if d[k] is None:
col_type = getattr(
sqlalchemy.inspect(models.Admission).columns, "apb_groupe"
).expression.type
if isinstance(col_type, sqlalchemy.Text):
e[k] = ""
d[k] = ""
elif isinstance(col_type, sqlalchemy.Integer):
e[k] = 0
d[k] = 0
elif isinstance(col_type, sqlalchemy.Boolean):
e[k] = False
return e
d[k] = False
return d
# Suivi scolarité / débouchés

View File

@ -45,7 +45,7 @@ class Formation(db.Model):
modules = db.relationship("Module", lazy="dynamic", backref="formation")
def __repr__(self):
return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>"
return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme!r}')>"
def to_dict(self):
e = dict(self.__dict__)
@ -168,7 +168,7 @@ class Matiere(db.Model):
def __repr__(self):
return f"""<{self.__class__.__name__}(id={self.id}, ue_id={
self.ue_id}, titre='{self.titre}')>"""
self.ue_id}, titre='{self.titre!r}')>"""
def to_dict(self):
"""as a dict, with the same conversions as in ScoDoc7"""

View File

@ -14,10 +14,12 @@ from app.models import SHORT_STR_LEN
from app.models import CODE_STR_LEN
import app.scodoc.sco_utils as scu
from app.models.ues import UniteEns
from app.models.but_refcomp import parcours_formsemestre
from app.models.etudiants import Identite
from app.models.modules import Module
from app.models.moduleimpls import ModuleImpl
from app.models.etudiants import Identite
from app.models.ues import UniteEns
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_preferences
from app.scodoc.sco_vdi import ApoEtapeVDI
@ -113,6 +115,14 @@ class FormSemestre(db.Model):
# ne pas utiliser après migrate_scodoc7_dept_archives
scodoc7_id = db.Column(db.Text(), nullable=True)
# BUT
parcours = db.relationship(
"ApcParcours",
secondary=parcours_formsemestre,
lazy="subquery",
backref=db.backref("formsemestres", lazy=True),
)
def __init__(self, **kwargs):
super(FormSemestre, self).__init__(**kwargs)
if self.modalite is None:

View File

@ -65,7 +65,7 @@ class Module(db.Model):
super(Module, self).__init__(**kwargs)
def __repr__(self):
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code}>"
return f"<Module{ModuleType(self.module_type or ModuleType.STANDARD).name} id={self.id} code={self.code!r}>"
def to_dict(self):
e = dict(self.__dict__)

View File

@ -207,12 +207,16 @@ class TF(object):
else:
self.values[field] = 1
if field not in self.values:
if "default" in descr: # first: default in form description
self.values[field] = descr["default"]
else: # then: use initvalues dict
self.values[field] = self.initvalues.get(field, "")
if self.values[field] == None:
self.values[field] = ""
if (descr.get("input_type", None) == "checkbox") and self.submitted():
# aucune case cochée
self.values[field] = []
else:
if "default" in descr: # first: default in form description
self.values[field] = descr["default"]
else: # then: use initvalues dict
self.values[field] = self.initvalues.get(field, "")
if self.values[field] is None:
self.values[field] = ""
# convert numbers, except ids
if field.endswith("id") and self.values[field]:
@ -392,9 +396,7 @@ class TF(object):
if self.top_buttons:
R.append(buttons_markup + "<p></p>")
R.append('<table class="tf">')
idx = 0
for idx in range(len(self.formdescription)):
(field, descr) = self.formdescription[idx]
for field, descr in self.formdescription:
if descr.get("readonly", False):
R.append(self._ReadOnlyElement(field, descr))
continue
@ -408,7 +410,7 @@ class TF(object):
input_type = descr.get("input_type", "text")
item_dom_id = descr.get("dom_id", "")
if item_dom_id:
item_dom_attr = ' id="%s"' % item_dom_id
item_dom_attr = f' id="{item_dom_id}"'
else:
item_dom_attr = ""
# choix du template
@ -523,7 +525,6 @@ class TF(object):
else:
checked = ""
else: # boolcheckbox
# open('/tmp/toto','a').write('GenForm: values[%s] = %s (%s)\n' % (field, values[field], type(values[field])))
if values[field] == "True":
v = True
elif values[field] == "False":

View File

@ -365,6 +365,7 @@ def module_edit(
"libjs/jQuery-tagEditor/jquery.tag-editor.min.js",
"libjs/jQuery-tagEditor/jquery.caret.min.js",
"js/module_tag_editor.js",
"js/module_edit.js",
],
),
f"""<h2>{title}</h2>""",
@ -605,8 +606,7 @@ def module_edit(
"input_type": "menu",
"type": "int",
"title": parcours.SESSION_NAME.capitalize(),
"explanation": "%s de début du module dans la formation standard"
% parcours.SESSION_NAME,
"explanation": f"{parcours.SESSION_NAME} de début du module dans la formation standard",
"labels": [str(x) for x in semestres_indices],
"allowed_values": semestres_indices,
"enabled": unlocked,
@ -619,7 +619,7 @@ def module_edit(
{
"title": "Code Apogée",
"size": 25,
"explanation": """(optionnel) code élément pédagogique Apogée ou liste de codes ELP
"explanation": """(optionnel) code élément pédagogique Apogée ou liste de codes ELP
séparés par des virgules (ce code est propre à chaque établissement, se rapprocher
du référent Apogée).
""",
@ -648,11 +648,15 @@ def module_edit(
"input_type": "checkbox",
"vertical": True,
"dom_id": "tf_module_parcours",
"labels": [parcour.libelle for parcour in ref_comp.parcours],
"labels": [parcour.libelle for parcour in ref_comp.parcours]
+ ["Tous (tronc commun)"],
"allowed_values": [
str(parcour.id) for parcour in ref_comp.parcours
],
"explanation": "parcours dans lesquels est utilisé ce module.",
]
+ ["-1"],
"explanation": """Parcours dans lesquels est utilisé ce module.<br>
Attention: si le module ne doit pas avoir les mêmes coefficients suivant le parcours,
il faut en créer plusieurs versions, car dans ScoDoc chaque module a ses coefficients.""",
},
)
]
@ -677,12 +681,17 @@ def module_edit(
"vertical": True,
"dom_id": "tf_module_app_critiques",
"labels": [
app_crit.libelle for app_crit in app_critiques
f"{app_crit.code}&nbsp; {app_crit.libelle}"
for app_crit in app_critiques
],
"allowed_values": [
str(app_crit.id) for app_crit in app_critiques
],
"explanation": "apprentissages critiques liés à ce module.",
"html_data": [],
"explanation": """Apprentissages Critiques liés à ce module.
(si vous changez le semestre, revenez ensuite sur cette page
pour associer les AC.)
""",
},
)
]
@ -693,7 +702,9 @@ def module_edit(
{
"input_type": "separator",
"title": f"""<span class="fontred">{scu.EMO_WARNING }
L'UE {ue.acronyme} {ue.titre}
L'UE <a class="stdlink" href="{
url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
}">{ue.acronyme} {ue.titre}</a>
n'est pas associée à un niveau de compétences
</span>""",
},
@ -727,9 +738,10 @@ def module_edit(
request.base_url,
scu.get_request_args(),
descr,
html_foot_markup="""<div class="sco_tag_module_edit"><span class="sco_tag_edit"><textarea data-module_id="{}" class="module_tag_editor">{}</textarea></span></div>""".format(
module_id, ",".join(sco_tag_module.module_tag_list(module_id))
)
html_foot_markup=f"""<div class="sco_tag_module_edit"><span
class="sco_tag_edit"><textarea data-module_id="{module_id}" class="module_tag_editor"
>{','.join(sco_tag_module.module_tag_list(module_id))}</textarea></span></div>
"""
if not create
else "",
initvalues=module_dict if module else {},
@ -793,11 +805,14 @@ def module_edit(
#
do_module_edit(tf[2])
# Modifie les parcours
if "parcours" in tf[2]:
module.parcours = [
ApcParcours.query.get(int(parcour_id_str))
for parcour_id_str in tf[2]["parcours"]
]
if ("parcours" in tf[2]) and formation.referentiel_competence:
if "-1" in tf[2]["parcours"]: # "tous"
module.parcours = formation.referentiel_competence.parcours.all()
else:
module.parcours = [
ApcParcours.query.get(int(parcour_id_str))
for parcour_id_str in tf[2]["parcours"]
]
# Modifie les AC
if "app_critiques" in tf[2]:
module.app_critiques = [
@ -811,7 +826,7 @@ def module_edit(
"notes.ue_table",
scodoc_dept=g.scodoc_dept,
formation_id=formation.id,
semestre_idx=tf[2]["semestre_id"],
semestre_idx=tf[2]["semestre_id"] if is_apc else 1,
)
)

View File

@ -39,23 +39,21 @@ from app.models import Module, ModuleImpl, Evaluation, EvaluationUEPoids, UniteE
from app.models import ScolarNews
from app.models.formations import Formation
from app.models.formsemestre import FormSemestre
from app.models.but_refcomp import ApcParcours
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc import sco_cache
from app.scodoc import sco_groups
from app import log
from app.scodoc.TrivialFormulator import TrivialFormulator, TF
from app.scodoc.TrivialFormulator import TrivialFormulator
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
from app.scodoc.sco_permissions import Permission
from app.scodoc.sco_vdi import ApoEtapeVDI
from app.scodoc import html_sco_header
from app.scodoc import sco_codes_parcours
from app.scodoc import sco_compute_moy
from app.scodoc import sco_edit_matiere
from app.scodoc import sco_edit_module
from app.scodoc import sco_edit_ue
from app.scodoc import sco_etud
from app.scodoc import sco_evaluations
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre
@ -119,12 +117,12 @@ def formsemestre_editwithmodules(formsemestre_id):
vals = scu.get_request_args()
if not vals.get("tf_submitted", False):
H.append(
"""<p class="help">Seuls les modules cochés font partie de ce semestre.
"""<p class="help">Seuls les modules cochés font partie de ce semestre.
Pour les retirer, les décocher et appuyer sur le bouton "modifier".
</p>
<p class="help">Attention : s'il y a déjà des évaluations dans un module,
<p class="help">Attention : s'il y a déjà des évaluations dans un module,
il ne peut pas être supprimé !</p>
<p class="help">Les modules ont toujours un responsable.
<p class="help">Les modules ont toujours un responsable.
Par défaut, c'est le directeur des études.</p>
<p class="help">Un semestre ne peut comporter qu'une seule UE "bonus
sport/culture"</p>
@ -153,7 +151,7 @@ def do_formsemestre_createwithmodules(edit=False):
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
if not current_user.has_permission(Permission.ScoImplement):
if not edit:
# il faut ScoImplement pour creer un semestre
# il faut ScoImplement pour créer un semestre
raise AccessDenied("vous n'avez pas le droit d'effectuer cette opération")
else:
if not sem["resp_can_edit"] or current_user.id not in sem["responsables"]:
@ -175,6 +173,7 @@ def do_formsemestre_createwithmodules(edit=False):
formation = Formation.query.get(formation_id)
if formation is None:
raise ScoValueError("Formation inexistante !")
is_apc = formation.is_apc()
if not edit:
initvalues = {"titre": _default_sem_title(formation)}
semestre_id = int(vals["semestre_id"])
@ -210,12 +209,12 @@ def do_formsemestre_createwithmodules(edit=False):
if NB_SEM == 1:
semestre_id_list = [-1]
else:
if edit and formation.is_apc():
if edit and is_apc:
# en APC, ne permet pas de changer de semestre
semestre_id_list = [formsemestre.semestre_id]
else:
semestre_id_list = list(range(1, NB_SEM + 1))
if not formation.is_apc():
if not is_apc:
# propose "pas de semestre" seulement en classique
semestre_id_list.insert(0, -1)
@ -226,7 +225,7 @@ def do_formsemestre_createwithmodules(edit=False):
else:
semestre_id_labels.append(f"S{sid}")
# Liste des modules dans cette formation
if formation.is_apc():
if is_apc:
modules = formation.modules.order_by(Module.module_type, Module.numero)
else:
modules = (
@ -318,10 +317,10 @@ def do_formsemestre_createwithmodules(edit=False):
{
"size": 40,
"title": "Nom de ce semestre",
"explanation": """n'indiquez pas les dates, ni le semestre, ni la modalité dans
"explanation": f"""n'indiquez pas les dates, ni le semestre, ni la modalité dans
le titre: ils seront automatiquement ajoutés <input type="button"
value="remettre titre par défaut" onClick="document.tf.titre.value='%s';"/>"""
% _default_sem_title(formation),
value="remettre titre par défaut" onClick="document.tf.titre.value='{
_default_sem_title(formation)}';"/>""",
},
),
(
@ -343,11 +342,9 @@ def do_formsemestre_createwithmodules(edit=False):
"allowed_values": semestre_id_list,
"labels": semestre_id_labels,
"explanation": "en BUT, on ne peut pas modifier le semestre après création"
if formation.is_apc()
else "",
"attributes": ['onchange="change_semestre_id();"']
if formation.is_apc()
if is_apc
else "",
"attributes": ['onchange="change_semestre_id();"'] if is_apc else "",
},
),
)
@ -386,7 +383,7 @@ def do_formsemestre_createwithmodules(edit=False):
mf = mf_manual
for n in range(1, scu.EDIT_NB_ETAPES + 1):
mf["title"] = "Etape Apogée (%d)" % n
mf["title"] = f"Etape Apogée ({n})"
modform.append(("etape_apo" + str(n), mf.copy()))
modform.append(
(
@ -443,15 +440,19 @@ def do_formsemestre_createwithmodules(edit=False):
)
)
if edit:
formtit = (
"""
<p><a href="formsemestre_edit_uecoefs?formsemestre_id=%s">Modifier les coefficients des UE capitalisées</a></p>
<h3>Sélectionner les modules, leurs responsables et les étudiants à inscrire:</h3>
formtit = f"""
<p><a href="formsemestre_edit_uecoefs?formsemestre_id={formsemestre_id}"
>Modifier les coefficients des UE capitalisées</a></p>
<h3>Sélectionner les modules, leurs responsables et les étudiants
à inscrire:</h3>
"""
% formsemestre_id
)
else:
formtit = """<h3>Sélectionner les modules et leurs responsables</h3><p class="help">Si vous avez des parcours (options), ne sélectionnez que les modules du tronc commun.</p>"""
formtit = """<h3>Sélectionner les modules et leurs responsables</h3>
<p class="help">Si vous avez des parcours (options), dans un premier
ne sélectionnez que les modules du tronc commun, puis après inscriptions,
revenez ajouter les modules de parcours en sélectionnant les groupes d'étudiants
à y inscrire.
</p>"""
modform += [
(
@ -531,12 +532,52 @@ def do_formsemestre_createwithmodules(edit=False):
"explanation": "empêcher le calcul des moyennes d'UE et générale.",
},
),
]
# Choix des parcours
if is_apc:
ref_comp = formation.referentiel_competence
if ref_comp:
modform += [
(
"parcours",
{
"input_type": "checkbox",
"vertical": True,
"dom_id": "tf_module_parcours",
"labels": [parcour.libelle for parcour in ref_comp.parcours],
"allowed_values": [
str(parcour.id) for parcour in ref_comp.parcours
],
"explanation": "Parcours proposés dans ce semestre.",
},
)
]
if edit:
sem["parcours"] = [str(parcour.id) for parcour in formsemestre.parcours]
else:
modform += [
(
"parcours",
{
"input_type": "separator",
"title": f"""<span class="fontred">{scu.EMO_WARNING }
Pas de parcours:
<a class="stdlink" href="{ url_for('notes.ue_table',
scodoc_dept=g.scodoc_dept, formation_id=formation.id)
}">vérifier la formation</a>
</span>""",
},
)
]
# Choix des modules
modform += [
(
"sep",
{
"input_type": "separator",
"title": "",
"template": "</table>%s<table>" % formtit,
"template": f"</table>{formtit}<table>",
},
),
]
@ -544,8 +585,8 @@ def do_formsemestre_createwithmodules(edit=False):
nbmod = 0
for semestre_id in semestre_ids:
if formation.is_apc():
# pour restreindre l'édition aux module du semestre sélectionné
if is_apc:
# pour restreindre l'édition aux modules du semestre sélectionné
tr_class = f'class="sem{semestre_id}"'
else:
tr_class = ""
@ -560,7 +601,7 @@ def do_formsemestre_createwithmodules(edit=False):
"sep",
{
"input_type": "separator",
"title": "<b>Semestre %s</b>" % semestre_id,
"title": f"<b>Semestre {semestre_id}</b>",
"template": templ_sep,
},
)
@ -568,13 +609,13 @@ def do_formsemestre_createwithmodules(edit=False):
for mod in mods:
if mod["semestre_id"] == semestre_id and (
(not edit) # creation => tous modules
or (not formation.is_apc()) # pas BUT, on peut mixer les semestres
or (not is_apc) # pas BUT, on peut mixer les semestres
or (semestre_id == formsemestre.semestre_id) # module du semestre
or (mod["module_id"] in module_ids_set) # module déjà présent
):
nbmod += 1
if edit:
select_name = "%s!group_id" % mod["module_id"]
select_name = f"{mod['module_id']}!group_id"
def opt_selected(gid):
if gid == vals.get(select_name):
@ -603,13 +644,16 @@ def do_formsemestre_createwithmodules(edit=False):
group["group_name"],
)
fcg += "</select>"
itemtemplate = (
f"""<tr {tr_class}><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td><td>"""
+ fcg
+ "</td></tr>"
)
itemtemplate = f"""<tr {tr_class}>
<td class="tf-fieldlabel">%(label)s</td>
<td class="tf-field">%(elem)s</td>
<td>{fcg}</td>
</tr>"""
else:
itemtemplate = f"""<tr {tr_class}><td class="tf-fieldlabel">%(label)s</td><td class="tf-field">%(elem)s</td></tr>"""
itemtemplate = f"""<tr {tr_class}>
<td class="tf-fieldlabel">%(label)s</td>
<td class="tf-field">%(elem)s</td>
</tr>"""
modform.append(
(
"MI" + str(mod["module_id"]),
@ -742,7 +786,8 @@ def do_formsemestre_createwithmodules(edit=False):
for module_id in tf[2]["tf-checked"]:
mod_resp_id = User.get_user_id_from_nomplogin(tf[2][module_id])
if mod_resp_id is None:
# Si un module n'a pas de responsable (ou inconnu), l'affecte au 1er directeur des etudes:
# Si un module n'a pas de responsable (ou inconnu),
# l'affecte au 1er directeur des etudes:
mod_resp_id = tf[2]["responsable_id"]
tf[2][module_id] = mod_resp_id
@ -763,7 +808,7 @@ def do_formsemestre_createwithmodules(edit=False):
module_ids_checked = [int(x[2:]) for x in tf[2]["tf-checked"]]
_formsemestre_check_ue_bonus_unicity(module_ids_checked)
if not edit:
if formation.is_apc():
if is_apc:
_formsemestre_check_module_list(
module_ids_checked, tf[2]["semestre_id"]
)
@ -777,14 +822,6 @@ def do_formsemestre_createwithmodules(edit=False):
"responsable_id": tf[2][f"MI{module_id}"],
}
_ = sco_moduleimpl.do_moduleimpl_create(modargs)
flash("Nouveau semestre créé")
return flask.redirect(
url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
)
else:
# Modification du semestre:
# on doit creer les modules nouvellement selectionnés
@ -794,7 +831,7 @@ def do_formsemestre_createwithmodules(edit=False):
module_ids_tocreate = [
x for x in module_ids_checked if not x in module_ids_existing
]
if formation.is_apc():
if is_apc:
_formsemestre_check_module_list(
module_ids_tocreate, tf[2]["semestre_id"]
)
@ -868,27 +905,46 @@ def do_formsemestre_createwithmodules(edit=False):
modargs, formsemestre_id=formsemestre_id
)
mod = sco_edit_module.module_list({"module_id": module_id})[0]
if msg:
msg_html = (
'<div class="ue_warning"><span>Attention !<ul><li>'
+ "</li><li>".join(msg)
+ "</li></ul></span></div>"
)
if ok:
msg_html += "<p>Modification effectuée</p>"
else:
msg_html += "<p>Modification effectuée (<b>mais modules cités non supprimés</b>)</p>"
msg_html += (
'<a href="formsemestre_status?formsemestre_id=%s">retour au tableau de bord</a>'
% formsemestre_id
)
return msg_html
# --- Assocation des parcours
formsemestre = FormSemestre.query.get(formsemestre_id)
if "parcours" in tf[2]:
formsemestre.parcours = [
ApcParcours.query.get(int(parcour_id_str))
for parcour_id_str in tf[2]["parcours"]
]
db.session.add(formsemestre)
db.session.commit()
# --- Fin
if edit:
if msg:
msg_html = (
'<div class="ue_warning"><span>Attention !<ul><li>'
+ "</li><li>".join(msg)
+ "</li></ul></span></div>"
)
if ok:
msg_html += "<p>Modification effectuée</p>"
else:
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Semestre modifié"
% formsemestre_id
)
msg_html += "<p>Modification effectuée (<b>mais modules cités non supprimés</b>)</p>"
msg_html += (
'<a href="formsemestre_status?formsemestre_id=%s">retour au tableau de bord</a>'
% formsemestre_id
)
return msg_html
else:
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Semestre modifié"
% formsemestre_id
)
else:
flash("Nouveau semestre créé")
return flask.redirect(
url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
)
def _formsemestre_check_module_list(module_ids, semestre_idx):

View File

@ -929,10 +929,18 @@ def formsemestre_status_head(formsemestre_id=None, page_title=None):
}</tt></b>)"""
)
H.append("</td></tr>")
if sem.parcours:
H.append(
f"""
<tr><td class="fichetitre2">Parcours: </td>
<td style="color: blue;">{', '.join(parcours.code for parcours in sem.parcours)}</td>
</tr>
"""
)
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
H.append(
'<tr><td class="fichetitre2">Evaluations: </td><td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides'
'<tr><td class="fichetitre2">Évaluations: </td><td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides'
% evals
)
if evals["last_modif"]:

View File

@ -2206,7 +2206,7 @@ ul.notes_module_list {
list-style-type: none;
}
div#ue_choix_niveau {
div.ue_choix_niveau {
background-color: rgb(191, 242, 255);
border: 1px solid blue;
border-radius: 10px;

View File

@ -0,0 +1,8 @@
/* Page édition module */
$(document).ready(function () {
});

View File

@ -6,11 +6,25 @@
<h1>Associer un référentiel de compétences</h1>
<div class="help">
Association d'un référentiel de compétence à la formation
{{formation.titre}} ({{formation.acronyme}})
<a href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id)
}}">{{formation.titre}} ({{formation.acronyme}})</a>
</div>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
<div style="margin-top: 20px; margin-bottom: 20px;">
Référentiel actuellement associé:
{% if formation.referentiel_competence is not none %}
<b>{{ formation.referentiel_competence.specialite_long }}</b>
<a href="{{
url_for('notes.refcomp_desassoc_formation', scodoc_dept=g.scodoc_dept, formation_id=formation.id)
}}" class="stdlink">supprimer</a>
{% else %}
<b>aucun</b>
{% endif %}
<div class="row" style="margin-top: 20px;">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
</div>

View File

@ -160,6 +160,20 @@ def refcomp_assoc_formation(formation_id: int):
)
@bp.route("/referentiel/comp/desassoc_formation/<int:formation_id>", methods=["GET"])
@scodoc
@permission_required(Permission.ScoChangeFormation)
def refcomp_desassoc_formation(formation_id: int):
"""Désassocie la formation de son ref. de compétence"""
formation = Formation.query.get_or_404(formation_id)
formation.referentiel_competence = None
db.session.add(formation)
db.session.commit()
return redirect(
url_for("notes.ue_table", scodoc_dept=g.scodoc_dept, formation_id=formation.id)
)
@bp.route(
"/referentiel/comp/load", defaults={"formation_id": None}, methods=["GET", "POST"]
)

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.2.22"
SCOVERSION = "9.3a"
SCONAME = "ScoDoc"