forked from ScoDoc/ScoDoc
Visualisation d'un parcours et ses UEs (WIP)
This commit is contained in:
parent
6e86f7a9c4
commit
d307fcb1e9
@ -290,7 +290,7 @@ class ResultatsSemestreBUT(NotesTableCompat):
|
|||||||
ues_parcour = self.formsemestre.formation.query_ues_parcour(parcour)
|
ues_parcour = self.formsemestre.formation.query_ues_parcour(parcour)
|
||||||
ues_ids = set()
|
ues_ids = set()
|
||||||
for niveau in niveaux:
|
for niveau in niveaux:
|
||||||
ue = ues_parcour.filter_by(UniteEns.niveau_competence == niveau).first()
|
ue = ues_parcour.filter(UniteEns.niveau_competence == niveau).first()
|
||||||
if ue:
|
if ue:
|
||||||
ues_ids.add(ue.id)
|
ues_ids.add(ue.id)
|
||||||
|
|
||||||
|
@ -383,9 +383,12 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
parcour: "ApcParcours",
|
parcour: "ApcParcours",
|
||||||
annee: int,
|
annee: int,
|
||||||
referentiel_competence: ApcReferentielCompetences = None,
|
referentiel_competence: ApcReferentielCompetences = None,
|
||||||
|
competence: ApcCompetence = None,
|
||||||
) -> list["ApcNiveau"]:
|
) -> list["ApcNiveau"]:
|
||||||
"""Les niveaux de l'année du parcours
|
"""Les niveaux de l'année du parcours
|
||||||
Si le parcour est None, tous les niveaux de l'année
|
Si le parcour est None, tous les niveaux de l'année
|
||||||
|
(dans ce cas, spécifier referentiel_competence)
|
||||||
|
Si competence est indiquée, filtre les niveaux de cette compétence.
|
||||||
"""
|
"""
|
||||||
if annee not in {1, 2, 3}:
|
if annee not in {1, 2, 3}:
|
||||||
raise ValueError("annee invalide pour un parcours BUT")
|
raise ValueError("annee invalide pour un parcours BUT")
|
||||||
@ -396,15 +399,20 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
raise ScoNoReferentielCompetences()
|
raise ScoNoReferentielCompetences()
|
||||||
if not parcour:
|
if not parcour:
|
||||||
annee_formation = f"BUT{annee}"
|
annee_formation = f"BUT{annee}"
|
||||||
return ApcNiveau.query.filter(
|
query = ApcNiveau.query.filter(
|
||||||
ApcNiveau.annee == annee_formation,
|
ApcNiveau.annee == annee_formation,
|
||||||
ApcCompetence.id == ApcNiveau.competence_id,
|
ApcCompetence.id == ApcNiveau.competence_id,
|
||||||
ApcCompetence.referentiel_id == referentiel_competence.id,
|
ApcCompetence.referentiel_id == referentiel_competence.id,
|
||||||
)
|
)
|
||||||
annee_parcour = parcour.annees.filter_by(ordre=annee).first()
|
if competence is not None:
|
||||||
|
query = query.filter(ApcCompetence.id == competence.id)
|
||||||
|
return query.all()
|
||||||
|
|
||||||
|
annee_parcour: ApcAnneeParcours = parcour.annees.filter_by(ordre=annee).first()
|
||||||
if not annee_parcour:
|
if not annee_parcour:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
if competence is None:
|
||||||
parcour_niveaux: list[
|
parcour_niveaux: list[
|
||||||
ApcParcoursNiveauCompetence
|
ApcParcoursNiveauCompetence
|
||||||
] = annee_parcour.niveaux_competences
|
] = annee_parcour.niveaux_competences
|
||||||
@ -412,6 +420,10 @@ class ApcNiveau(db.Model, XMLModel):
|
|||||||
pn.competence.niveaux.filter_by(ordre=pn.niveau).first()
|
pn.competence.niveaux.filter_by(ordre=pn.niveau).first()
|
||||||
for pn in parcour_niveaux
|
for pn in parcour_niveaux
|
||||||
]
|
]
|
||||||
|
else:
|
||||||
|
niveaux: list[ApcNiveau] = competence.niveaux.filter_by(
|
||||||
|
annee=f"BUT{int(annee)}"
|
||||||
|
).all()
|
||||||
return niveaux
|
return niveaux
|
||||||
|
|
||||||
|
|
||||||
@ -558,6 +570,16 @@ class ApcParcours(db.Model, XMLModel):
|
|||||||
.order_by(ApcCompetence.numero)
|
.order_by(ApcCompetence.numero)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_competence_by_titre(self, titre: str) -> ApcCompetence:
|
||||||
|
"La compétence de titre donné dans ce parcours, ou None"
|
||||||
|
return (
|
||||||
|
ApcCompetence.query.filter_by(titre=titre)
|
||||||
|
.join(ApcParcoursNiveauCompetence, ApcAnneeParcours)
|
||||||
|
.filter_by(parcours_id=self.id)
|
||||||
|
.order_by(ApcCompetence.numero)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ApcAnneeParcours(db.Model, XMLModel):
|
class ApcAnneeParcours(db.Model, XMLModel):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
68
app/static/css/parcour_formation.css
Normal file
68
app/static/css/parcour_formation.css
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
.parcour_formation {
|
||||||
|
margin-left: 24px;
|
||||||
|
width: 990px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titre_parcours {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 120%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.competence {
|
||||||
|
/* display: grid; */
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titre_competence {
|
||||||
|
/* grid-column-start: 1;
|
||||||
|
grid-column-end: span -1;
|
||||||
|
grid-row-start: 1;
|
||||||
|
grid-row-start: 2; */
|
||||||
|
border-bottom: 6px solid white;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 110%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.niveaux {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.niveau {
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
grid-template-rows: auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.niveau>div {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titre_niveau {
|
||||||
|
grid-column: 1 / span 2;
|
||||||
|
grid-row: 1 / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ue {
|
||||||
|
grid-row-start: 2;
|
||||||
|
/* border: 1px dashed blue; */
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ue.impair {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ue.pair {
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.niveau-1 {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.niveau-2 {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
@ -56,26 +56,90 @@ table.table_niveaux_parcours tr.annee_but td.empty {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Les couleurs des niveaux de compétences du BO */
|
||||||
|
.comp-c1-1 {
|
||||||
|
background: rgb(224, 201, 201);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c1-2 {
|
||||||
|
background: rgb(231, 127, 130);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c1-3,
|
||||||
.comp-c1 {
|
.comp-c1 {
|
||||||
background: #a44
|
background: rgb(167, 0, 9);
|
||||||
|
color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comp-c2-1 {
|
||||||
|
background: rgb(240, 218, 198);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c2-2 {
|
||||||
|
background: rgb(231, 142, 95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c2-3,
|
||||||
.comp-c2 {
|
.comp-c2 {
|
||||||
background: #84a
|
background: rgb(231, 119, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comp-c3-1 {
|
||||||
|
background: rgb(241, 227, 167);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c3-2 {
|
||||||
|
background: rgb(238, 208, 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c3-3,
|
||||||
.comp-c3 {
|
.comp-c3 {
|
||||||
background: #a84
|
background: rgb(233, 174, 17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comp-c4-1 {
|
||||||
|
background: rgb(218, 225, 205);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c4-2 {
|
||||||
|
background: rgb(159, 207, 111);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c4-3,
|
||||||
.comp-c4 {
|
.comp-c4 {
|
||||||
background: #8a4
|
background: rgb(124, 192, 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comp-c5-1 {
|
||||||
|
background: rgb(191, 206, 230);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c5-2 {
|
||||||
|
background: rgb(119, 156, 208);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c5-3,
|
||||||
.comp-c5 {
|
.comp-c5 {
|
||||||
background: #4a8
|
background: rgb(10, 22, 75);
|
||||||
|
color: #eee;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.comp-c6-1,
|
||||||
.comp-c6 {
|
.comp-c6 {
|
||||||
background: #48a
|
background: rgb(203, 199, 176);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c6-2 {
|
||||||
|
background: rgb(152, 143, 97);
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comp-c6-3 {
|
||||||
|
background: rgb(13, 13, 13);
|
||||||
|
color: #eee;
|
||||||
}
|
}
|
34
app/templates/but/parcour_formation.j2
Normal file
34
app/templates/but/parcour_formation.j2
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{% extends "sco_page.j2" %}
|
||||||
|
|
||||||
|
{% block styles %}
|
||||||
|
{{super()}}
|
||||||
|
<link href="{{sco.scu.STATIC_DIR}}/css/refcomp_parcours_niveaux.css" rel="stylesheet" type="text/css" />
|
||||||
|
<link href="{{sco.scu.STATIC_DIR}}/css/parcour_formation.css" rel="stylesheet" type="text/css" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
<div class="parcour_formation">
|
||||||
|
|
||||||
|
<div class="titre_parcours">Parcours {{parcour.code}} « {{parcour.libelle}} »</div>
|
||||||
|
|
||||||
|
{% for comp in competences_parcour %}
|
||||||
|
{% set color_idx = 1 + loop.index0 % 6 %}
|
||||||
|
<div class="competence comp-c{{color_idx}}">
|
||||||
|
<div class="titre_competence tc">
|
||||||
|
Compétence {{comp['competence'].numero}} : {{comp['competence'].titre}}
|
||||||
|
</div>
|
||||||
|
<div class="niveaux">
|
||||||
|
{% for annee, niv in comp['niveaux'].items() %}
|
||||||
|
<div class="niveau comp-c{{color_idx}}-{{annee}}">
|
||||||
|
<div class="titre_niveau n{{annee}}">{{niv['niveau'].libelle if niv['niveau'] else '-'}}</div>
|
||||||
|
<div class="ue impair u{{annee}}1">{{niv['ue_impair'].acronyme if niv['ue_impair'] else 'UE1'}}</div>
|
||||||
|
<div class="ue pair u{{annee}}1">{{niv['ue_pair'].acronyme if niv['ue_pair'] else 'UE2'}}</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -107,6 +107,7 @@ class ScoData:
|
|||||||
|
|
||||||
from app.views import (
|
from app.views import (
|
||||||
absences,
|
absences,
|
||||||
|
but_formation,
|
||||||
notes_formsemestre,
|
notes_formsemestre,
|
||||||
notes,
|
notes,
|
||||||
pn_modules,
|
pn_modules,
|
||||||
|
145
app/views/but_formation.py
Normal file
145
app/views/but_formation.py
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# ScoDoc
|
||||||
|
#
|
||||||
|
# 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@viennet.net
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Vues sur les formations BUT
|
||||||
|
|
||||||
|
Emmanuel Viennet, 2023
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import g, render_template
|
||||||
|
|
||||||
|
from app import log
|
||||||
|
from app.decorators import (
|
||||||
|
scodoc,
|
||||||
|
permission_required,
|
||||||
|
)
|
||||||
|
|
||||||
|
from app.models import (
|
||||||
|
ApcCompetence,
|
||||||
|
ApcNiveau,
|
||||||
|
ApcParcours,
|
||||||
|
ApcReferentielCompetences,
|
||||||
|
Formation,
|
||||||
|
)
|
||||||
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc.sco_exceptions import ScoValueError
|
||||||
|
from app.views import notes_bp as bp
|
||||||
|
from app.views import ScoData
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/parcour_formation/<int:parcour_id>/<int:formation_id>")
|
||||||
|
@scodoc
|
||||||
|
@permission_required(Permission.ScoView)
|
||||||
|
def parcour_formation(parcour_id: int, formation_id: int) -> str:
|
||||||
|
"""visu HTML d'un parcours dans une formation,
|
||||||
|
avec les compétences, niveaux et UEs associées."""
|
||||||
|
formation: Formation = Formation.query.filter_by(
|
||||||
|
id=formation_id, dept_id=g.scodoc_dept_id
|
||||||
|
).first_or_404()
|
||||||
|
ref_comp: ApcReferentielCompetences = formation.referentiel_competence
|
||||||
|
if ref_comp is None:
|
||||||
|
return "pas de référentiel de compétences"
|
||||||
|
parcour: ApcParcours = ref_comp.parcours.filter_by(id=parcour_id).first()
|
||||||
|
if parcour is None:
|
||||||
|
raise ScoValueError("parcours invalide ou hors référentiel de formation")
|
||||||
|
|
||||||
|
competences_parcour = parcour_formation_competences(parcour, formation)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"but/parcour_formation.j2",
|
||||||
|
formation=formation,
|
||||||
|
parcour=parcour,
|
||||||
|
competences_parcour=competences_parcour,
|
||||||
|
sco=ScoData(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def parcour_formation_competences(parcour: ApcParcours, formation: Formation) -> list:
|
||||||
|
"""
|
||||||
|
[
|
||||||
|
{
|
||||||
|
'competence' : ApcCompetence,
|
||||||
|
'niveaux' : {
|
||||||
|
1 : { ... },
|
||||||
|
2 : { ... },
|
||||||
|
3 : {
|
||||||
|
'niveau' : ApcNiveau,
|
||||||
|
'ue_impair' : UniteEns,
|
||||||
|
'ue_pair' : UniteEns
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def _niveau_ues(competence: ApcCompetence, annee: int) -> dict:
|
||||||
|
niveaux = ApcNiveau.niveaux_annee_de_parcours(
|
||||||
|
parcour, annee, competence=competence
|
||||||
|
)
|
||||||
|
if len(niveaux) > 0:
|
||||||
|
if len(niveaux) > 1:
|
||||||
|
log(f"_niveau_ues: plus d'un niveau pour {competence} annee {annee}")
|
||||||
|
niveau = niveaux[0]
|
||||||
|
elif len(niveaux) == 0:
|
||||||
|
return {"niveau": None, "ue_pair": None, "ue_impair": None}
|
||||||
|
|
||||||
|
ues = [
|
||||||
|
ue
|
||||||
|
for ue in niveau.ues
|
||||||
|
if ue.formation.id == formation.id
|
||||||
|
and parcour.id in (p.id for p in ue.parcours)
|
||||||
|
]
|
||||||
|
ues_pair = [ue for ue in ues if ue.semestre_idx == 2 * annee]
|
||||||
|
if len(ues_pair) > 0:
|
||||||
|
ue_pair = ues_pair[0]
|
||||||
|
if len(ues_pair) > 1:
|
||||||
|
log(
|
||||||
|
f"_niveau_ues: {len(ues)} associées au niveau {niveau} / S{2*annee}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ue_pair = None
|
||||||
|
ues_impair = [ue for ue in ues if ue.semestre_idx == (2 * annee - 1)]
|
||||||
|
if len(ues_impair) > 0:
|
||||||
|
ue_impair = ues_impair[0]
|
||||||
|
if len(ues_impair) > 1:
|
||||||
|
log(
|
||||||
|
f"_niveau_ues: {len(ues)} associées au niveau {niveau} / S{2*annee-1}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ue_impair = None
|
||||||
|
return {
|
||||||
|
"niveau": niveau,
|
||||||
|
"ue_pair": ue_pair,
|
||||||
|
"ue_impair": ue_impair,
|
||||||
|
}
|
||||||
|
|
||||||
|
competences = [
|
||||||
|
{
|
||||||
|
"competence": competence,
|
||||||
|
"niveaux": {annee: _niveau_ues(competence, annee) for annee in (1, 2, 3)},
|
||||||
|
}
|
||||||
|
for competence in parcour.query_competences()
|
||||||
|
]
|
||||||
|
return competences
|
@ -263,16 +263,12 @@ def refcomp_load(formation_id=None):
|
|||||||
category="info",
|
category="info",
|
||||||
)
|
)
|
||||||
|
|
||||||
if formation is not None:
|
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for(
|
url_for(
|
||||||
"notes.refcomp_assoc_formation",
|
"notes.refcomp_table",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=formation.formation_id,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
return redirect(url_for("notes.index_html", scodoc_dept=g.scodoc_dept))
|
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"but/refcomp_load.j2",
|
"but/refcomp_load.j2",
|
||||||
|
Loading…
Reference in New Issue
Block a user