forked from ScoDoc/ScoDoc
BUT: ECTS par UE dépendant du parcours.
This commit is contained in:
parent
488e4b1c85
commit
5f719442f0
@ -36,6 +36,7 @@ from jinja2 import select_autoescape
|
|||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from flask_cas import CAS
|
from flask_cas import CAS
|
||||||
|
import werkzeug.debug
|
||||||
|
|
||||||
from app.scodoc.sco_exceptions import (
|
from app.scodoc.sco_exceptions import (
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
@ -273,6 +274,14 @@ def create_app(config_class=DevConfig):
|
|||||||
# flask_sqlalchemy/query (pb deprecation du model.get())
|
# flask_sqlalchemy/query (pb deprecation du model.get())
|
||||||
warnings.filterwarnings("error", module="flask_sqlalchemy/query")
|
warnings.filterwarnings("error", module="flask_sqlalchemy/query")
|
||||||
# warnings.filterwarnings("ignore", module="json/provider.py") xxx sans effet en test
|
# warnings.filterwarnings("ignore", module="json/provider.py") xxx sans effet en test
|
||||||
|
if app.config["DEBUG"]:
|
||||||
|
# comme on a désactivé ci-dessus les logs de werkzeug,
|
||||||
|
# on affiche nous même le PIN en mode debug:
|
||||||
|
print(
|
||||||
|
f""" * Debugger is active!
|
||||||
|
* Debugger PIN: {werkzeug.debug.get_pin_and_cookie_name(app)[0]}
|
||||||
|
"""
|
||||||
|
)
|
||||||
# Vérifie/crée lien sym pour les URL statiques
|
# Vérifie/crée lien sym pour les URL statiques
|
||||||
link_filename = f"{app.root_path}/static/links/{sco_version.SCOVERSION}"
|
link_filename = f"{app.root_path}/static/links/{sco_version.SCOVERSION}"
|
||||||
if not os.path.exists(link_filename):
|
if not os.path.exists(link_filename):
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
"""
|
"""
|
||||||
Edition associations UE <-> Ref. Compétence
|
Edition associations UE <-> Ref. Compétence
|
||||||
"""
|
"""
|
||||||
from flask import g, render_template, url_for
|
from flask import g, url_for
|
||||||
|
|
||||||
from app.models import ApcReferentielCompetences, UniteEns
|
from app.models import ApcReferentielCompetences, UniteEns
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.forms.formation.ue_parcours_niveau import UEParcoursNiveauForm
|
|
||||||
|
|
||||||
|
|
||||||
def form_ue_choix_niveau(ue: UniteEns) -> str:
|
def form_ue_choix_niveau(ue: UniteEns) -> str:
|
||||||
@ -28,69 +28,17 @@ def form_ue_choix_niveau(ue: UniteEns) -> str:
|
|||||||
}">associer un référentiel de compétence</a>
|
}">associer un référentiel de compétence</a>
|
||||||
</div>
|
</div>
|
||||||
</div>"""
|
</div>"""
|
||||||
# Les parcours:
|
|
||||||
parcours_options = []
|
|
||||||
for parcour in ref_comp.parcours:
|
|
||||||
parcours_options.append(
|
|
||||||
f"""<option value="{parcour.id}" {
|
|
||||||
'selected' if parcour in ue.parcours else ''}
|
|
||||||
>{parcour.libelle} ({parcour.code})
|
|
||||||
</option>"""
|
|
||||||
)
|
|
||||||
|
|
||||||
newline = "\n"
|
|
||||||
return f"""
|
return f"""
|
||||||
<div class="ue_choix_niveau">
|
<div class="ue_advanced">
|
||||||
<form class="form_ue_choix_niveau">
|
<ul>
|
||||||
<div class="cont_ue_choix_niveau">
|
<li>
|
||||||
<div>
|
<a class="stdlink" href="{
|
||||||
<b>Parcours :</b>
|
url_for("notes.ue_parcours_ects",
|
||||||
<select class="select_parcour multiselect"
|
scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
onchange="set_ue_parcour(this);"
|
}">définir des ECTS différents dans chaque parcours</a>
|
||||||
data-ue_id="{ue.id}"
|
</li>
|
||||||
data-setter="{
|
</ul>
|
||||||
url_for( "apiweb.set_ue_parcours", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
|
||||||
}">
|
|
||||||
<option value="" {
|
|
||||||
'selected' if not ue.parcours else ''
|
|
||||||
}>Tous</option>
|
|
||||||
{newline.join(parcours_options)}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Niveau de compétence :</b>
|
|
||||||
<select class="select_niveau_ue"
|
|
||||||
onchange="set_ue_niveau_competence(this);"
|
|
||||||
data-ue_id="{ue.id}"
|
|
||||||
data-setter="{
|
|
||||||
url_for( "notes.set_ue_niveau_competence", scodoc_dept=g.scodoc_dept)
|
|
||||||
}">
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# Nouvelle version XXX WIP
|
|
||||||
def form_ue_choix_parcours_niveau(ue: UniteEns):
|
|
||||||
"""formulaire (div) pour choix association des parcours et du niveau de compétence d'une UE"""
|
|
||||||
if ue.type != codes_cursus.UE_STANDARD:
|
|
||||||
return ""
|
|
||||||
ref_comp = ue.formation.referentiel_competence
|
|
||||||
if ref_comp is None:
|
|
||||||
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>"""
|
|
||||||
parcours = ue.formation.referentiel_competence.parcours
|
|
||||||
form = UEParcoursNiveauForm(ue, parcours)
|
|
||||||
return f"""<div class="ue_choix_niveau">
|
|
||||||
{ render_template( "pn/ue_choix_parcours_niveau.j2", form_ue_parcours_niveau=form ) }
|
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
35
app/forms/formation/ue_parcours_ects.py
Normal file
35
app/forms/formation/ue_parcours_ects.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from flask import g, url_for
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import FieldList, Form, DecimalField, validators
|
||||||
|
|
||||||
|
from app.models import ApcParcours, ApcReferentielCompetences, UniteEns
|
||||||
|
|
||||||
|
|
||||||
|
class _UEParcoursECTSForm(FlaskForm):
|
||||||
|
"Formulaire association ECTS par parcours à une UE"
|
||||||
|
# construit dynamiquement ci-dessous
|
||||||
|
|
||||||
|
|
||||||
|
def UEParcoursECTSForm(ue: UniteEns) -> FlaskForm:
|
||||||
|
"Génère formulaire association ECTS par parcours à une UE"
|
||||||
|
|
||||||
|
class F(_UEParcoursECTSForm):
|
||||||
|
pass
|
||||||
|
|
||||||
|
parcours: list[ApcParcours] = ue.formation.referentiel_competence.parcours
|
||||||
|
# Initialise un champs de saisie par parcours
|
||||||
|
for parcour in parcours:
|
||||||
|
ects = ue.get_ects(parcour, only_parcours=True)
|
||||||
|
setattr(
|
||||||
|
F,
|
||||||
|
f"ects_parcour_{parcour.id}",
|
||||||
|
DecimalField(
|
||||||
|
f"Parcours {parcour.code}",
|
||||||
|
validators=[
|
||||||
|
validators.Optional(),
|
||||||
|
validators.NumberRange(min=0, max=30),
|
||||||
|
],
|
||||||
|
default=ects,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return F()
|
@ -1,39 +0,0 @@
|
|||||||
from flask import g, url_for
|
|
||||||
from flask_wtf import FlaskForm
|
|
||||||
from wtforms.fields import SelectField, SelectMultipleField
|
|
||||||
|
|
||||||
from app.models import ApcParcours, ApcReferentielCompetences, UniteEns
|
|
||||||
|
|
||||||
|
|
||||||
class UEParcoursNiveauForm(FlaskForm):
|
|
||||||
"Formulaire association parcours et niveau de compétence à une UE"
|
|
||||||
niveau_select = SelectField(
|
|
||||||
"Niveau de compétence:", render_kw={"class": "niveau_select"}
|
|
||||||
)
|
|
||||||
parcours_multiselect = SelectMultipleField(
|
|
||||||
"Parcours :",
|
|
||||||
coerce=int,
|
|
||||||
option_widget={"class": "form-check-input"},
|
|
||||||
# widget_attrs={"class": "form-check"},
|
|
||||||
render_kw={"class": "multiselect select_ue_parcours", "multiple": "multiple"},
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, ue: UniteEns, parcours: list[ApcParcours], *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
# Initialise le menu des niveaux:
|
|
||||||
self.niveau_select.render_kw["data-ue_id"] = ue.id
|
|
||||||
self.niveau_select.choices = [
|
|
||||||
(r.id, f"{r.type_titre} {r.specialite_long} ({r.get_version()})")
|
|
||||||
for r in ApcReferentielCompetences.query.filter_by(dept_id=g.scodoc_dept_id)
|
|
||||||
]
|
|
||||||
# Initialise le menu des parcours
|
|
||||||
self.parcours_multiselect.render_kw["data-set_ue_parcours"] = url_for(
|
|
||||||
"apiweb.set_ue_parcours", ue_id=ue.id, scodoc_dept=g.scodoc_dept
|
|
||||||
)
|
|
||||||
parcours_options = [(str(p.id), f"{p.libelle} ({p.code})") for p in parcours]
|
|
||||||
self.parcours_multiselect.choices = parcours_options
|
|
||||||
|
|
||||||
# initialize checked items based on u instance
|
|
||||||
parcours_selected = [str(p.id) for p in ue.parcours]
|
|
||||||
self.parcours_multiselect.process_data(parcours_selected)
|
|
@ -38,7 +38,7 @@ class UniteEns(db.Model):
|
|||||||
server_default=db.text("notes_newid_ucod()"),
|
server_default=db.text("notes_newid_ucod()"),
|
||||||
nullable=False,
|
nullable=False,
|
||||||
)
|
)
|
||||||
ects = db.Column(db.Float) # nombre de credits ECTS
|
ects = db.Column(db.Float) # nombre de credits ECTS (sauf si parcours spécifié)
|
||||||
is_external = db.Column(db.Boolean(), default=False, server_default="false")
|
is_external = db.Column(db.Boolean(), default=False, server_default="false")
|
||||||
# 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))
|
||||||
@ -100,8 +100,7 @@ class UniteEns(db.Model):
|
|||||||
return ue
|
return ue
|
||||||
|
|
||||||
def to_dict(self, convert_objects=False, with_module_ue_coefs=True):
|
def to_dict(self, convert_objects=False, with_module_ue_coefs=True):
|
||||||
"""as a dict, with the same conversions as in ScoDoc7
|
"""as a dict, with the same conversions as in ScoDoc7.
|
||||||
(except ECTS: keep None)
|
|
||||||
If convert_objects, convert all attributes to native types
|
If convert_objects, convert all attributes to native types
|
||||||
(suitable for json encoding).
|
(suitable for json encoding).
|
||||||
"""
|
"""
|
||||||
@ -111,7 +110,12 @@ class UniteEns(db.Model):
|
|||||||
# ScoDoc7 output_formators
|
# ScoDoc7 output_formators
|
||||||
e["ue_id"] = self.id
|
e["ue_id"] = self.id
|
||||||
e["numero"] = e["numero"] if e["numero"] else 0
|
e["numero"] = e["numero"] if e["numero"] else 0
|
||||||
e["ects"] = e["ects"]
|
e["ects"] = e["ects"] # legacy
|
||||||
|
e["ects_by_parcours"] = {}
|
||||||
|
for up in UEParcours.query.filter_by(ue_id=self.id):
|
||||||
|
p = ApcParcours.query.get(up.parcours_id)
|
||||||
|
e["ects_by_parcours"][p.code] = self.get_ects(p)
|
||||||
|
|
||||||
e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0
|
e["coefficient"] = e["coefficient"] if e["coefficient"] else 0.0
|
||||||
e["code_apogee"] = e["code_apogee"] or "" # pas de None
|
e["code_apogee"] = e["code_apogee"] or "" # pas de None
|
||||||
e["parcours"] = [
|
e["parcours"] = [
|
||||||
@ -164,6 +168,44 @@ class UniteEns(db.Model):
|
|||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
def get_ects(self, parcour: ApcParcours = None, only_parcours=False) -> float:
|
||||||
|
"""Crédits ECTS associés à cette UE.
|
||||||
|
En BUT, cela peut quelquefois dépendre du parcours.
|
||||||
|
Si only_parcours, renvoie None si pas de valeur spéciquement définie dans
|
||||||
|
le parcours indiqué.
|
||||||
|
"""
|
||||||
|
if parcour is not None:
|
||||||
|
ue_parcour = UEParcours.query.filter_by(
|
||||||
|
ue_id=self.id, parcours_id=parcour.id
|
||||||
|
).first()
|
||||||
|
if ue_parcour is not None and ue_parcour.ects is not None:
|
||||||
|
return ue_parcour.ects
|
||||||
|
if only_parcours:
|
||||||
|
return None
|
||||||
|
return self.ects
|
||||||
|
|
||||||
|
def set_ects(self, ects: float, parcour: ApcParcours = None):
|
||||||
|
"""Fixe les crédits. Do not commit.
|
||||||
|
Si le parcours n'est pas spécifié, affecte les ECTS par défaut de l'UE.
|
||||||
|
Si ects est None et parcours indiqué, efface l'association.
|
||||||
|
"""
|
||||||
|
if parcour is not None:
|
||||||
|
ue_parcour = UEParcours.query.filter_by(
|
||||||
|
ue_id=self.id, parcours_id=parcour.id
|
||||||
|
).first()
|
||||||
|
if ects is None:
|
||||||
|
if ue_parcour:
|
||||||
|
db.session.delete(ue_parcour)
|
||||||
|
else:
|
||||||
|
if ue_parcour is None:
|
||||||
|
ue_parcour = UEParcours(parcours_id=parcour.id, ue_id=self.id)
|
||||||
|
ue_parcour.ects = float(ects)
|
||||||
|
db.session.add(ue_parcour)
|
||||||
|
else:
|
||||||
|
self.ects = ects
|
||||||
|
log(f"ue.set_ects( ue_id={self.id}, acronyme={self.acronyme}, ects={ects} )")
|
||||||
|
db.session.add(self)
|
||||||
|
|
||||||
def get_ressources(self):
|
def get_ressources(self):
|
||||||
"Liste des modules ressources rattachés à cette UE"
|
"Liste des modules ressources rattachés à cette UE"
|
||||||
return self.modules.filter_by(module_type=scu.ModuleType.RESSOURCE).all()
|
return self.modules.filter_by(module_type=scu.ModuleType.RESSOURCE).all()
|
||||||
@ -334,12 +376,21 @@ class UEParcours(db.Model):
|
|||||||
"""Association ue <-> parcours, indiquant les ECTS"""
|
"""Association ue <-> parcours, indiquant les ECTS"""
|
||||||
|
|
||||||
__tablename__ = "ue_parcours"
|
__tablename__ = "ue_parcours"
|
||||||
ue_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), primary_key=True)
|
ue_id = db.Column(
|
||||||
|
db.Integer,
|
||||||
|
db.ForeignKey("notes_ue.id", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
|
)
|
||||||
parcours_id = db.Column(
|
parcours_id = db.Column(
|
||||||
db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True
|
db.Integer,
|
||||||
|
db.ForeignKey("apc_parcours.id", ondelete="CASCADE"),
|
||||||
|
primary_key=True,
|
||||||
)
|
)
|
||||||
ects = db.Column(db.Float, nullable=True) # si NULL, on prendra les ECTS de l'UE
|
ects = db.Column(db.Float, nullable=True) # si NULL, on prendra les ECTS de l'UE
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<UEParcours( ue_id={self.ue_id}, parcours_id={self.parcours_id}, ects={self.ects})>"
|
||||||
|
|
||||||
|
|
||||||
class DispenseUE(db.Model):
|
class DispenseUE(db.Model):
|
||||||
"""Dispense d'UE
|
"""Dispense d'UE
|
||||||
|
@ -111,7 +111,6 @@ def html_edit_formation_apc(
|
|||||||
icons=icons,
|
icons=icons,
|
||||||
ues_by_sem=ues_by_sem,
|
ues_by_sem=ues_by_sem,
|
||||||
ects_by_sem=ects_by_sem,
|
ects_by_sem=ects_by_sem,
|
||||||
form_ue_choix_parcours_niveau=apc_edit_ue.form_ue_choix_parcours_niveau,
|
|
||||||
scu=scu,
|
scu=scu,
|
||||||
codes_cursus=codes_cursus,
|
codes_cursus=codes_cursus,
|
||||||
),
|
),
|
||||||
|
@ -369,7 +369,12 @@ def ue_edit(ue_id=None, create=False, formation_id=None, default_semestre_idx=No
|
|||||||
"min_value": 0,
|
"min_value": 0,
|
||||||
"max_value": 1000,
|
"max_value": 1000,
|
||||||
"title": "ECTS",
|
"title": "ECTS",
|
||||||
"explanation": "nombre de crédits ECTS (indiquer 0 si UE bonus)",
|
"explanation": "nombre de crédits ECTS (indiquer 0 si UE bonus)"
|
||||||
|
+ (
|
||||||
|
". (si les ECTS dépendent du parcours, voir plus bas.)"
|
||||||
|
if is_apc
|
||||||
|
else ""
|
||||||
|
),
|
||||||
"allow_null": not is_apc, # ects requis en APC
|
"allow_null": not is_apc, # ects requis en APC
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -103,12 +103,12 @@ div.competence {
|
|||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titre_niveau span.parcs {
|
span.parcs {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titre_niveau span.parc {
|
span.parc {
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
/* color: rgb(92, 87, 255); */
|
/* color: rgb(92, 87, 255); */
|
||||||
|
@ -2530,6 +2530,15 @@ div.cont_ue_choix_niveau select.select_niveau_ue {
|
|||||||
width: 490px;
|
width: 490px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.ue_advanced {
|
||||||
|
background-color: rgb(244, 253, 255);
|
||||||
|
border: 1px solid blue;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
div#ue_list_modules {
|
div#ue_list_modules {
|
||||||
background-color: rgb(251, 225, 165);
|
background-color: rgb(251, 225, 165);
|
||||||
border: 1px solid blue;
|
border: 1px solid blue;
|
||||||
|
@ -116,11 +116,32 @@ Choisissez un parcours...
|
|||||||
</div>
|
</div>
|
||||||
<div><a class="stdlink" href="{{
|
<div><a class="stdlink" href="{{
|
||||||
url_for('notes.refcomp_show',
|
url_for('notes.refcomp_show',
|
||||||
scodoc_dept=g.scodoc_dept, refcomp_id=parcour.referentiel.id )
|
scodoc_dept=g.scodoc_dept, refcomp_id=formation.referentiel_competence.id )
|
||||||
}}">Référentiel de compétences</a>
|
}}">Référentiel de compétences</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if parcour %}
|
||||||
|
<div class="help">
|
||||||
|
|
||||||
|
<p> Cette page représente le parcours <span class="parc">{{parcour.code}}</span>
|
||||||
|
du référentiel de compétence {{formation.referentiel_competence.specialite}}, et permet
|
||||||
|
d'associer à chaque semestre d'un niveau de compétence une UE de la formation
|
||||||
|
<a class="stdlink"
|
||||||
|
href="{{url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, formation_id=formation.id )
|
||||||
|
}}">{{formation.to_html()}}
|
||||||
|
</a>.</p>
|
||||||
|
|
||||||
|
<p>Le symbole <span class="parc">TC</span> désigne un niveau du tronc commun
|
||||||
|
(c'est à dire présent dans tous les parcours de la spécialité). </p>
|
||||||
|
|
||||||
|
<p>Ce formulaire ne vérifie pas si l'UE est bien conçue pour ce parcours.</p>
|
||||||
|
|
||||||
|
<p>Les modifications sont enregistrées au fur et à mesure.</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function assoc_ue_niveau(event, niveau_id) {
|
function assoc_ue_niveau(event, niveau_id) {
|
||||||
let ue_id = event.target.value;
|
let ue_id = event.target.value;
|
||||||
|
40
app/templates/formation/ue_assoc_parcours_ects.j2
Normal file
40
app/templates/formation/ue_assoc_parcours_ects.j2
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{# Association d'ECTS à une UE par parcours #}
|
||||||
|
{% extends "sco_page.j2" %}
|
||||||
|
{% import 'bootstrap/wtf.html' as wtf %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{super()}}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
<h2>ECTS par parcours pour l'UE {{ue.acronyme}}</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="help">
|
||||||
|
<p>
|
||||||
|
Utilisez ce formulaire dans le cas (assez rare) où l'UE {{ue.acronyme}}
|
||||||
|
doit avoir un nombre d'ECTS qui dépend du parcours de l'étudiant.
|
||||||
|
<p>
|
||||||
|
Si un champ est laissé sont vide, la valeur par défaut spécifiée pour l'UE
|
||||||
|
(actuellement {{ue.ects or 0}} ECTS) sera utilisée pour ce parcours.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
{% for field in form %}
|
||||||
|
{% if field.name != 'csrf_token' %}
|
||||||
|
<div>
|
||||||
|
<label for="{{ field.id }}">{{ field.label }}</label>
|
||||||
|
{{ field }}
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<div class="error-message">{{ error }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ form.csrf_token }}
|
||||||
|
<input type="submit" name="submit" value="Enregistrer">
|
||||||
|
<input type="submit" name="cancel" value="Annuler">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -65,7 +65,6 @@
|
|||||||
}}">modifier</a>
|
}}">modifier</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# form_ue_choix_parcours_niveau(ue)|safe #}
|
|
||||||
{% if ue.type != codes_cursus.UE_SPORT %}
|
{% if ue.type != codes_cursus.UE_SPORT %}
|
||||||
<div class="ue_choix_niveau">
|
<div class="ue_choix_niveau">
|
||||||
{% if ue.niveau_competence %}
|
{% if ue.niveau_competence %}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
{# inclu par form_ues.j2 #}
|
|
||||||
|
|
||||||
<form method="POST" action="">
|
|
||||||
{{ form_ue_parcours_niveau.csrf_token }}
|
|
||||||
<div class="form-group">
|
|
||||||
{{ form_ue_parcours_niveau.niveau_select.label }}
|
|
||||||
{{ form_ue_parcours_niveau.niveau_select }}
|
|
||||||
|
|
||||||
{{ form_ue_parcours_niveau.parcours_multiselect.label }}
|
|
||||||
{{ form_ue_parcours_niveau.parcours_multiselect }}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
@ -28,20 +28,23 @@ Vues sur les formations BUT
|
|||||||
Emmanuel Viennet, 2023
|
Emmanuel Viennet, 2023
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import g, render_template
|
from flask import flash, g, redirect, render_template, request, url_for
|
||||||
|
|
||||||
from app import log
|
from app import db, log
|
||||||
from app.decorators import (
|
from app.decorators import (
|
||||||
scodoc,
|
scodoc,
|
||||||
permission_required,
|
permission_required,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from app.forms.formation.ue_parcours_ects import UEParcoursECTSForm
|
||||||
|
|
||||||
from app.models import (
|
from app.models import (
|
||||||
ApcCompetence,
|
ApcCompetence,
|
||||||
ApcNiveau,
|
ApcNiveau,
|
||||||
ApcParcours,
|
ApcParcours,
|
||||||
ApcReferentielCompetences,
|
ApcReferentielCompetences,
|
||||||
Formation,
|
Formation,
|
||||||
|
UniteEns,
|
||||||
)
|
)
|
||||||
from app.scodoc.codes_cursus import UE_STANDARD
|
from app.scodoc.codes_cursus import UE_STANDARD
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
@ -168,3 +171,40 @@ def parcour_formation_competences(parcour: ApcParcours, formation: Formation) ->
|
|||||||
for competence in parcour.query_competences()
|
for competence in parcour.query_competences()
|
||||||
]
|
]
|
||||||
return competences
|
return competences
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/ue_parcours_ects/<int:ue_id>", methods=["GET", "POST"])
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoChangeFormation)
|
||||||
|
def ue_parcours_ects(ue_id: int):
|
||||||
|
"""formulaire (div) pour associer des ECTS par parcours d'une UE"""
|
||||||
|
ue: UniteEns = (
|
||||||
|
UniteEns.query.filter_by(id=ue_id)
|
||||||
|
.join(Formation)
|
||||||
|
.filter_by(dept_id=g.scodoc_dept_id)
|
||||||
|
.first_or_404()
|
||||||
|
)
|
||||||
|
if ue.type != UE_STANDARD:
|
||||||
|
raise ScoValueError("Pas d'ECTS / Parcours pour ce type d'UE")
|
||||||
|
ref_comp = ue.formation.referentiel_competence
|
||||||
|
if ref_comp is None:
|
||||||
|
raise ScoValueError("Pas référentiel de compétence pour cette UE !")
|
||||||
|
form = UEParcoursECTSForm(ue)
|
||||||
|
|
||||||
|
edit_url = url_for("notes.ue_edit", scodoc_dept=g.scodoc_dept, ue_id=ue.id)
|
||||||
|
if request.method == "POST":
|
||||||
|
if request.form.get("submit"):
|
||||||
|
if form.validate():
|
||||||
|
for parcour in ue.formation.referentiel_competence.parcours:
|
||||||
|
field = getattr(form, f"ects_parcour_{parcour.id}")
|
||||||
|
if field:
|
||||||
|
ue.set_ects(field.data, parcour=parcour)
|
||||||
|
db.session.commit()
|
||||||
|
flash("ECTS enregistrés")
|
||||||
|
return redirect(edit_url)
|
||||||
|
elif request.form.get("cancel"):
|
||||||
|
return redirect(edit_url)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"formation/ue_assoc_parcours_ects.j2", form=form, sco=ScoData(), ue=ue
|
||||||
|
)
|
||||||
|
@ -30,15 +30,12 @@ def upgrade():
|
|||||||
sa.Column("parcours_id", sa.Integer(), nullable=False),
|
sa.Column("parcours_id", sa.Integer(), nullable=False),
|
||||||
sa.Column("ects", sa.Float(), nullable=True),
|
sa.Column("ects", sa.Float(), nullable=True),
|
||||||
sa.ForeignKeyConstraint(
|
sa.ForeignKeyConstraint(
|
||||||
["parcours_id"],
|
["parcours_id"], ["apc_parcours.id"], ondelete="CASCADE"
|
||||||
["apc_parcours.id"],
|
|
||||||
),
|
|
||||||
sa.ForeignKeyConstraint(
|
|
||||||
["ue_id"],
|
|
||||||
["notes_ue.id"],
|
|
||||||
),
|
),
|
||||||
|
sa.ForeignKeyConstraint(["ue_id"], ["notes_ue.id"], ondelete="CASCADE"),
|
||||||
sa.PrimaryKeyConstraint("ue_id", "parcours_id"),
|
sa.PrimaryKeyConstraint("ue_id", "parcours_id"),
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
bind = op.get_bind()
|
bind = op.get_bind()
|
||||||
session = Session(bind=bind)
|
session = Session(bind=bind)
|
||||||
|
Loading…
Reference in New Issue
Block a user