forked from ScoDoc/ScoDoc
WIP: validation du DUT120 (#577). Manque PVs.
This commit is contained in:
parent
48e1207fd8
commit
b14c3938b7
@ -32,6 +32,7 @@ from app.models import (
|
||||
ScolarNews,
|
||||
Scolog,
|
||||
UniteEns,
|
||||
ValidationDUT120,
|
||||
)
|
||||
from app.scodoc import codes_cursus
|
||||
from app.scodoc import sco_cache
|
||||
@ -62,7 +63,7 @@ def decisions_jury(formsemestre_id: int):
|
||||
raise ScoException("non implemente")
|
||||
|
||||
|
||||
def _news_delete_jury_etud(etud: Identite):
|
||||
def _news_delete_jury_etud(etud: Identite, detail: str = ""):
|
||||
"génère news sur effacement décision"
|
||||
# n'utilise pas g.scodoc_dept, pas toujours dispo en mode API
|
||||
url = url_for(
|
||||
@ -71,7 +72,7 @@ def _news_delete_jury_etud(etud: Identite):
|
||||
ScolarNews.add(
|
||||
typ=ScolarNews.NEWS_JURY,
|
||||
obj=etud.id,
|
||||
text=f"""Suppression décision jury pour <a href="{url}">{etud.nomprenom}</a>""",
|
||||
text=f"""Suppression décision jury {detail} pour <a href="{url}">{etud.nomprenom}</a>""",
|
||||
url=url,
|
||||
)
|
||||
|
||||
@ -320,11 +321,11 @@ def validation_rcue_delete(etudid: int, validation_id: int):
|
||||
validation = ApcValidationRCUE.query.filter_by(
|
||||
id=validation_id, etudid=etudid
|
||||
).first_or_404()
|
||||
log(f"validation_ue_delete: etuid={etudid} {validation}")
|
||||
log(f"delete validation_ue_delete: etuid={etudid} {validation}")
|
||||
db.session.delete(validation)
|
||||
sco_cache.invalidate_formsemestre_etud(etud)
|
||||
db.session.commit()
|
||||
_news_delete_jury_etud(etud)
|
||||
_news_delete_jury_etud(etud, detail="UE")
|
||||
return "ok"
|
||||
|
||||
|
||||
@ -348,9 +349,38 @@ def validation_annee_but_delete(etudid: int, validation_id: int):
|
||||
validation = ApcValidationAnnee.query.filter_by(
|
||||
id=validation_id, etudid=etudid
|
||||
).first_or_404()
|
||||
log(f"validation_annee_but: etuid={etudid} {validation}")
|
||||
ordre = validation.ordre
|
||||
log(f"delete validation_annee_but: etuid={etudid} {validation}")
|
||||
db.session.delete(validation)
|
||||
sco_cache.invalidate_formsemestre_etud(etud)
|
||||
db.session.commit()
|
||||
_news_delete_jury_etud(etud)
|
||||
_news_delete_jury_etud(etud, detail=f"année BUT{ordre}")
|
||||
return "ok"
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/etudiant/<int:etudid>/jury/validation_dut120/<int:validation_id>/delete",
|
||||
methods=["POST"],
|
||||
)
|
||||
@api_web_bp.route(
|
||||
"/etudiant/<int:etudid>/jury/validation_dut120/<int:validation_id>/delete",
|
||||
methods=["POST"],
|
||||
)
|
||||
@login_required
|
||||
@scodoc
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
@as_json
|
||||
def validation_dut120_delete(etudid: int, validation_id: int):
|
||||
"Efface cette validation"
|
||||
etud = tools.get_etud(etudid)
|
||||
if etud is None:
|
||||
return "étudiant inconnu", 404
|
||||
validation = ValidationDUT120.query.filter_by(
|
||||
id=validation_id, etudid=etudid
|
||||
).first_or_404()
|
||||
log(f"delete validation_dut120: etuid={etudid} {validation}")
|
||||
db.session.delete(validation)
|
||||
sco_cache.invalidate_formsemestre_etud(etud)
|
||||
db.session.commit()
|
||||
_news_delete_jury_etud(etud, detail="diplôme DUT120")
|
||||
return "ok"
|
||||
|
@ -14,6 +14,7 @@ Classe raccordant avec ScoDoc 7:
|
||||
|
||||
"""
|
||||
import collections
|
||||
from collections.abc import Iterable
|
||||
from operator import attrgetter
|
||||
|
||||
from flask import g, url_for
|
||||
@ -382,18 +383,28 @@ class FormSemestreCursusBUT:
|
||||
# "cache { competence_id : competence }"
|
||||
|
||||
|
||||
def but_ects_valides(etud: Identite, referentiel_competence_id: int) -> int:
|
||||
def but_ects_valides(
|
||||
etud: Identite,
|
||||
referentiel_competence_id: int,
|
||||
annees_but: None | Iterable[str] = None,
|
||||
) -> int:
|
||||
"""Nombre d'ECTS validés par etud dans le BUT de référentiel indiqué.
|
||||
Ne prend que les UE associées à des niveaux de compétences,
|
||||
et ne les compte qu'une fois même en cas de redoublement avec re-validation.
|
||||
Si annees_but est spécifié, un iterable "BUT1, "BUT2" par exemple, ne prend que ces années.
|
||||
"""
|
||||
validations = (
|
||||
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
||||
.filter(ScolarFormSemestreValidation.ue_id != None)
|
||||
.join(UniteEns)
|
||||
.join(ApcNiveau)
|
||||
.join(ApcCompetence)
|
||||
.filter_by(referentiel_id=referentiel_competence_id)
|
||||
)
|
||||
# restreint à certaines années (utile pour les ECTS du DUT120)
|
||||
if annees_but:
|
||||
validations = validations.filter(ApcNiveau.annee.in_(annees_but))
|
||||
# Et restreint au référentiel de compétence:
|
||||
validations = validations.join(ApcCompetence).filter_by(
|
||||
referentiel_id=referentiel_competence_id
|
||||
)
|
||||
|
||||
ects_dict = {}
|
||||
|
109
app/but/jury_dut120.py
Normal file
109
app/but/jury_dut120.py
Normal file
@ -0,0 +1,109 @@
|
||||
##############################################################################
|
||||
# ScoDoc
|
||||
# Copyright (c) 1999 - 2024 Emmanuel Viennet. All rights reserved.
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""Jury DUT120: gestion et vues
|
||||
|
||||
Ce diplôme est attribué sur demande aux étudiants de BUT ayant acquis les 120 ECTS
|
||||
de BUT 1 et BUT 2.
|
||||
|
||||
"""
|
||||
import time
|
||||
from flask import flash, g, redirect, render_template, request, url_for
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField
|
||||
|
||||
from app import db, log
|
||||
from app.but import cursus_but
|
||||
from app.decorators import scodoc, permission_required
|
||||
from app.models.but_validations import ValidationDUT120
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.formsemestre import FormSemestre
|
||||
from app.scodoc.sco_exceptions import ScoPermissionDenied, ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.views import notes_bp as bp
|
||||
from app.views import ScoData
|
||||
|
||||
|
||||
def etud_valide_dut120(etud: Identite, referentiel_competence_id: int) -> bool:
|
||||
"""Vrai si l'étudiant satisfait les conditions pour valider le DUT120"""
|
||||
ects_but1_but2 = cursus_but.but_ects_valides(
|
||||
etud, referentiel_competence_id, annees_but=("BUT1", "BUT2")
|
||||
)
|
||||
return ects_but1_but2 >= 120
|
||||
|
||||
|
||||
class ValidationDUT120Form(FlaskForm):
|
||||
"Formulaire validation DUT120"
|
||||
submit = SubmitField("Enregistrer le diplôme DUT 120")
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/validate_dut120/etudid/<int:etudid>/formsemestre/<int:formsemestre_id>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def validate_dut120_etud(etudid: int, formsemestre_id: int):
|
||||
"""Formulaire validation individuelle du DUT120"""
|
||||
# Check arguments
|
||||
etud = Identite.get_etud(etudid)
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
refcomp = formsemestre.formation.referentiel_competence
|
||||
if not refcomp:
|
||||
raise ScoValueError("formation non associée à un référentiel de compétences")
|
||||
# Permission
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
ects_but1_but2 = cursus_but.but_ects_valides(
|
||||
etud, refcomp.id, annees_but=("BUT1", "BUT2")
|
||||
)
|
||||
|
||||
form = ValidationDUT120Form()
|
||||
# Check if ValidationDUT120 instance already exists
|
||||
existing_validation = ValidationDUT120.query.filter_by(
|
||||
etudid=etudid, referentiel_competence_id=refcomp.id
|
||||
).first()
|
||||
if existing_validation:
|
||||
flash("DUT120 déjà validé", "info")
|
||||
etud_can_validate_dut = False
|
||||
# Check if the student meets the criteria
|
||||
elif ects_but1_but2 < 120:
|
||||
flash("L'étudiant ne remplit pas les conditions", "warning")
|
||||
etud_can_validate_dut = False # here existing_validation is None
|
||||
else:
|
||||
etud_can_validate_dut = True
|
||||
|
||||
if etud_can_validate_dut and request.method == "POST" and form.validate_on_submit():
|
||||
new_validation = ValidationDUT120(
|
||||
etudid=etudid,
|
||||
referentiel_competence_id=refcomp.id,
|
||||
formsemestre_id=formsemestre.id, # Replace with appropriate value
|
||||
)
|
||||
db.session.add(new_validation)
|
||||
db.session.commit()
|
||||
log(f"ValidationDUT120 enregistrée pour {etud} depuis {formsemestre}")
|
||||
flash("Validation DUT120 enregistrée", "success")
|
||||
return redirect(etud.url_fiche())
|
||||
|
||||
return render_template(
|
||||
"but/validate_dut120.j2",
|
||||
ects_but1_but2=ects_but1_but2,
|
||||
etud=etud,
|
||||
etud_can_validate_dut=etud_can_validate_dut,
|
||||
form=form,
|
||||
formsemestre=formsemestre,
|
||||
sco=ScoData(formsemestre=formsemestre, etud=etud),
|
||||
time=time,
|
||||
title="Délivrance du DUT",
|
||||
validation=existing_validation,
|
||||
)
|
@ -16,9 +16,10 @@ from app.models import (
|
||||
ApcValidationAnnee,
|
||||
ApcValidationRCUE,
|
||||
Identite,
|
||||
UniteEns,
|
||||
ScolarAutorisationInscription,
|
||||
ScolarFormSemestreValidation,
|
||||
UniteEns,
|
||||
ValidationDUT120,
|
||||
)
|
||||
from app.views import ScoData
|
||||
|
||||
@ -60,6 +61,9 @@ def jury_delete_manual(etud: Identite):
|
||||
sem_vals=sem_vals,
|
||||
ue_vals=ue_vals,
|
||||
autorisations=autorisations,
|
||||
dut120_vals=ValidationDUT120.query.filter_by(etudid=etud.id).order_by(
|
||||
ValidationDUT120.date
|
||||
),
|
||||
rcue_vals=rcue_vals,
|
||||
annee_but_vals=annee_but_vals,
|
||||
sco=ScoData(),
|
||||
|
@ -3,6 +3,7 @@
|
||||
"""Modèles base de données ScoDoc
|
||||
"""
|
||||
|
||||
from flask import abort, g
|
||||
import sqlalchemy
|
||||
from app import db
|
||||
|
||||
@ -116,6 +117,31 @@ class ScoDocModel(db.Model):
|
||||
args = {field.name: field.data for field in form}
|
||||
return self.from_dict(args)
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, oid: int, accept_none=False):
|
||||
"""Instance du modèle ou ou 404 (ou None si accept_none),
|
||||
cherche uniquement dans le département courant.
|
||||
Ne fonctionne que si le modèle a un attribut dept_id.
|
||||
Si accept_none, return None si l'id est invalide ou ne correspond
|
||||
pas à une validation.
|
||||
"""
|
||||
if not isinstance(oid, int):
|
||||
try:
|
||||
oid = int(oid)
|
||||
except (TypeError, ValueError):
|
||||
if accept_none:
|
||||
return None
|
||||
abort(404, "oid invalide")
|
||||
|
||||
query = (
|
||||
cls.query.filter_by(id=oid, dept_id=g.scodoc_dept_id)
|
||||
if g.scodoc_dept
|
||||
else cls.query.filter_by(id=oid)
|
||||
)
|
||||
if accept_none:
|
||||
return query.first()
|
||||
return query.first_or_404()
|
||||
|
||||
|
||||
from app.models.absences import Absence, AbsenceNotification, BilletAbsence
|
||||
from app.models.departements import Departement
|
||||
@ -173,7 +199,11 @@ from app.models.but_refcomp import (
|
||||
ApcReferentielCompetences,
|
||||
ApcSituationPro,
|
||||
)
|
||||
from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
||||
from app.models.but_validations import (
|
||||
ApcValidationAnnee,
|
||||
ApcValidationRCUE,
|
||||
ValidationDUT120,
|
||||
)
|
||||
|
||||
from app.models.config import ScoDocSiteConfig
|
||||
|
||||
|
@ -4,8 +4,10 @@
|
||||
"""
|
||||
from collections import defaultdict
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
from app import db
|
||||
from app.models import CODE_STR_LEN
|
||||
from app.models import CODE_STR_LEN, ScoDocModel
|
||||
from app.models.but_refcomp import ApcNiveau
|
||||
from app.models.etudiants import Identite
|
||||
from app.models.formsemestre import FormSemestre
|
||||
@ -14,7 +16,7 @@ from app.scodoc import sco_preferences
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
|
||||
class ApcValidationRCUE(db.Model):
|
||||
class ApcValidationRCUE(ScoDocModel):
|
||||
"""Validation des niveaux de compétences
|
||||
|
||||
aka "regroupements cohérents d'UE" dans le jargon BUT.
|
||||
@ -121,7 +123,7 @@ class ApcValidationRCUE(db.Model):
|
||||
return self.ue1.get_codes_apogee_rcue() | self.ue2.get_codes_apogee_rcue()
|
||||
|
||||
|
||||
class ApcValidationAnnee(db.Model):
|
||||
class ApcValidationAnnee(ScoDocModel):
|
||||
"""Validation des années du BUT"""
|
||||
|
||||
__tablename__ = "apc_validation_annee"
|
||||
@ -277,3 +279,64 @@ def _build_decisions_rcue_list(decisions_rcue: dict) -> list[str]:
|
||||
)
|
||||
)
|
||||
return titres_rcues
|
||||
|
||||
|
||||
class ValidationDUT120(ScoDocModel):
|
||||
"""Validations du DUT 120
|
||||
Ce diplôme est attribué sur demande aux étudiants de BUT ayant acquis les 120 ECTS
|
||||
de BUT 1 et BUT 2.
|
||||
"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
etudid = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("identite.id", ondelete="CASCADE"),
|
||||
index=True,
|
||||
nullable=False,
|
||||
)
|
||||
formsemestre_id = db.Column(
|
||||
db.Integer,
|
||||
db.ForeignKey("notes_formsemestre.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
"""le semestre origine, dans la plupart des cas le S4 (le diplôme DUT120
|
||||
apparaîtra sur les PV de ce formsemestre)"""
|
||||
referentiel_competence_id = db.Column(
|
||||
db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False
|
||||
) # pas de cascade, on ne doit pas supprimer un référentiel utilisé
|
||||
"""Identifie la spécialité de DUT décernée"""
|
||||
date = db.Column(
|
||||
db.DateTime(timezone=True), server_default=db.func.now(), nullable=False
|
||||
)
|
||||
"""Date de délivrance"""
|
||||
|
||||
etud = db.relationship("Identite", backref="validations_dut120")
|
||||
formsemestre = db.relationship("FormSemestre", backref="validations_dut120")
|
||||
|
||||
def __repr__(self):
|
||||
return f"""<ValidationDUT120 {self.etud}>"""
|
||||
|
||||
def html(self) -> str:
|
||||
"Affichage html"
|
||||
date_str = (
|
||||
f"""le {self.date.strftime(scu.DATEATIME_FMT)}"""
|
||||
if self.date
|
||||
else "(sans date)"
|
||||
)
|
||||
link = (
|
||||
self.formsemestre.html_link_status(
|
||||
label=f"{self.formsemestre.titre_formation(with_sem_idx=1)}",
|
||||
title=self.formsemestre.titre_annee(),
|
||||
)
|
||||
if self.formsemestre
|
||||
else "externe/antérieure"
|
||||
)
|
||||
specialite = (
|
||||
self.formsemestre.formation.referentiel_competence.get_title()
|
||||
if self.formsemestre.formation.referentiel_competence
|
||||
else "(désassociée!)"
|
||||
)
|
||||
return f"""Diplôme de <b>DUT en 120 ECTS du {specialite}</b> émis par
|
||||
{link}
|
||||
{date_str}
|
||||
"""
|
||||
|
@ -179,7 +179,9 @@ class Identite(models.ScoDocModel):
|
||||
|
||||
def html_link_fiche(self) -> str:
|
||||
"lien vers la fiche"
|
||||
return f"""<a class="etudlink" href="{self.url_fiche()}">{self.nomprenom}</a>"""
|
||||
return (
|
||||
f"""<a class="etudlink" href="{self.url_fiche()}">{self.nom_prenom()}</a>"""
|
||||
)
|
||||
|
||||
def url_fiche(self) -> str:
|
||||
"url de la fiche étudiant"
|
||||
@ -314,7 +316,7 @@ class Identite(models.ScoDocModel):
|
||||
|
||||
@property
|
||||
def nomprenom(self, reverse=False) -> str:
|
||||
"""DEPRECATED
|
||||
"""DEPRECATED: préférer nom_prenom()
|
||||
Civilité/prénom/nom pour affichages: "M. Pierre Dupont"
|
||||
Si reverse, "Dupont Pierre", sans civilité.
|
||||
Prend l'identité courante et non celle de l'état civil si elles diffèrent.
|
||||
|
@ -37,7 +37,14 @@ import sqlalchemy as sa
|
||||
from app import log
|
||||
from app.auth.models import User
|
||||
from app.but import cursus_but, validations_view
|
||||
from app.models import Adresse, EtudAnnotation, FormSemestre, Identite, ScoDocSiteConfig
|
||||
from app.models import (
|
||||
Adresse,
|
||||
EtudAnnotation,
|
||||
FormSemestre,
|
||||
Identite,
|
||||
ScoDocSiteConfig,
|
||||
ValidationDUT120,
|
||||
)
|
||||
from app.scodoc import (
|
||||
codes_cursus,
|
||||
html_sco_header,
|
||||
@ -455,6 +462,20 @@ def fiche_etud(etudid=None):
|
||||
ects_total = sum((v.ects() for v in ue_validation_by_niveau.values()))
|
||||
else:
|
||||
ects_total = ""
|
||||
|
||||
validation_dut120 = ValidationDUT120.query.filter_by(etudid=etudid).first()
|
||||
validation_dut120_html = (
|
||||
f"""Diplôme DUT décerné
|
||||
en <a class="stdlink" href="{
|
||||
url_for("notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=validation_dut120.formsemestre.id)
|
||||
}">S{validation_dut120.formsemestre.semestre_id}</a>
|
||||
"""
|
||||
if validation_dut120
|
||||
else ""
|
||||
)
|
||||
|
||||
info[
|
||||
"but_cursus_mkup"
|
||||
] = f"""
|
||||
@ -463,6 +484,7 @@ def fiche_etud(etudid=None):
|
||||
"but/cursus_etud.j2",
|
||||
cursus=but_cursus,
|
||||
scu=scu,
|
||||
validation_dut120_html=validation_dut120_html,
|
||||
)}
|
||||
<div class="fiche_but_col2">
|
||||
<div class="link_validation_rcues">
|
||||
@ -471,7 +493,7 @@ def fiche_etud(etudid=None):
|
||||
formsemestre_id=last_formsemestre.id)}"
|
||||
title="Visualiser les compétences BUT"
|
||||
>
|
||||
<img src="/ScoDoc/static/icons/parcours-but.png" alt="validation_rcues" height="100px"/>
|
||||
<img src="/ScoDoc/static/icons/parcours-but.png" alt="validation_rcues" height="132px"/>
|
||||
<div style="text-align: center;">Compétences BUT</div>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -40,6 +40,25 @@ div.code_rcue {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div.cursus_but .validation_dut120 {
|
||||
grid-column: span 3;
|
||||
justify-self: end;
|
||||
/* align on right of BUT2 */
|
||||
width: auto;
|
||||
/* fit its content */
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
font-weight: bold;
|
||||
color: rgb(0, 0, 192);
|
||||
background-color: #eee;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgb(0, 0, 192);
|
||||
}
|
||||
|
||||
div.validation_dut120 a.stdlink {
|
||||
color: rgb(0, 0, 192);
|
||||
}
|
||||
|
||||
div.no_niveau {
|
||||
background-color: rgb(245, 237, 200);
|
||||
}
|
||||
|
@ -959,7 +959,7 @@ td.fichetitre2 .fl {
|
||||
div.section_but {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
@ -974,7 +974,8 @@ div.fiche_total_etcs {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
div.section_but>div.link_validation_rcues {
|
||||
div.section_but div.link_validation_rcues,
|
||||
div.section_but div.link_validation_rcues img {
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
}
|
||||
@ -4590,6 +4591,10 @@ table.table_recap tr td.jury_code_sem {
|
||||
border-left: 1px solid blue;
|
||||
}
|
||||
|
||||
table.table_recap tr td.col_jury_link {
|
||||
border-left: 1px solid blue;
|
||||
}
|
||||
|
||||
table.table_recap .admission {
|
||||
white-space: nowrap;
|
||||
color: rgb(6, 73, 6);
|
||||
|
@ -18,7 +18,7 @@ from app.but import jury_but
|
||||
from app.but.jury_but import DecisionsProposeesRCUE
|
||||
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import ApcNiveau, UniteEns
|
||||
from app.models import ApcNiveau, UniteEns, ValidationDUT120
|
||||
from app.models.etudiants import Identite
|
||||
from app.scodoc.codes_cursus import (
|
||||
BUT_BARRE_RCUE,
|
||||
@ -149,7 +149,7 @@ class TableJury(TableRecap):
|
||||
"ects_acquis",
|
||||
"ECTS",
|
||||
# res.get_etud_ects_valides(etud.id),
|
||||
# cette recherche augmente de 10% le temps de contsruction de la table
|
||||
# cette recherche augmente de 10% le temps de construction de la table
|
||||
cursus_but.but_ects_valides(
|
||||
etud, res.formsemestre.formation.referentiel_competence_id
|
||||
),
|
||||
@ -157,6 +157,16 @@ class TableJury(TableRecap):
|
||||
classes=["recorded_code"],
|
||||
target_attrs={"title": "crédits validés en BUT"},
|
||||
)
|
||||
# Diplôme DUT120
|
||||
validation_dut120 = ValidationDUT120.query.filter_by(etudid=etud.id).first()
|
||||
if validation_dut120:
|
||||
row.add_cell(
|
||||
"dut120",
|
||||
"DUT",
|
||||
"DUT",
|
||||
group="jury_code_sem",
|
||||
classes=["recorded_code"],
|
||||
)
|
||||
# Lien saisie ou visu jury
|
||||
a_saisir = (not res.validations) or (not res.validations.has_decision(etud))
|
||||
row.add_cell(
|
||||
|
@ -30,4 +30,10 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% if validation_dut120_html %}
|
||||
<div class="validation_dut120"
|
||||
title="DUT en 120 ECTS dans un BUT">
|
||||
{{validation_dut120_html | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
68
app/templates/but/validate_dut120.j2
Normal file
68
app/templates/but/validate_dut120.j2
Normal file
@ -0,0 +1,68 @@
|
||||
{% extends "sco_page.j2" %}
|
||||
|
||||
{% block styles %}
|
||||
{{super()}}
|
||||
<link href="{{scu.STATIC_DIR}}/css/jury_but.css" rel="stylesheet" type="text/css" />
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block app_content %}
|
||||
|
||||
<div class="bull_head jury_but">
|
||||
<div>
|
||||
<div class="titre_parcours">Validation du DUT en 120 ECTS dans un parcours BUT</div>
|
||||
<div class="nom_etud">{{etud.html_link_fiche()|safe}}</div>
|
||||
</div>
|
||||
<div class="bull_photo">
|
||||
<a href="{{etud.url_fiche()|safe}}">
|
||||
{{etud.photo_html(title="fiche de " + etud.nomprenom)|safe}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help">
|
||||
<p>Les étudiants de BUT peuvent demander l’attribution du <em>diplôme universitaire de technologie</em>
|
||||
(DUT) au terme de l’acquisition des 120 premiers crédits européens du cursus.
|
||||
</p>
|
||||
<p>Dans ScoDoc, c'est le référentiel de compétence qui détermine la spécialité (si un étudiant redouble dans
|
||||
une formation utilisant une autre version de référentiel, pensez à revalider ses UEs).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 16px;">
|
||||
{{etud.html_link_fiche()|safe}} a acquis <b>{{ects_but1_but2}} ECTS</b> en BUT1 et BUT2.
|
||||
{% if not validation %}
|
||||
Son DUT n'est pas encore enregistré dans cette spécialité.
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if etud_can_validate_dut %}
|
||||
<form method="POST" action="">
|
||||
{{ form.hidden_tag() }}
|
||||
<div style="margin-top: 24px;">
|
||||
{{ form.submit() }}
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<div class="warning">
|
||||
{% if validation %}
|
||||
DUT déjà validé dans cette spécialité
|
||||
<b>{{formsemestre.formation.referentiel_competence.get_title()}}</b>
|
||||
pour l'étudiant{{etud.ne}} {{etud.html_link_fiche()|safe}}
|
||||
<ul>
|
||||
<li>DUT 120 spécialité {{formsemestre.formation.referentiel_competence.specialite_long}}
|
||||
enregistré le {{time.strftime("%d/%m/%Y à %Hh%M")}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% else %}
|
||||
L'étudiant ne satisfait pas les conditions :
|
||||
vérifiez qu'il a validé et enregistré ses UEs de BUT1 et BUT2
|
||||
et ainsi acquis 120 crédits ECTS.
|
||||
Voir la page <a class="stdlink" href="{{url_for('notes.validation_rcues',
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id, etudid=etud.id)
|
||||
}}">compétences BUT</a>.
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -84,6 +84,22 @@ pages de saisie de jury habituelles).
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if dut120_vals.count() %}
|
||||
<div class="jury_decisions_list jury_decisions_dut120">
|
||||
<div>Diplôme de DUT en 120 ECTS (dans un parcours BUT)</div>
|
||||
<ul>
|
||||
{% for v in dut120_vals %}
|
||||
<li>{{v.html()|safe}}
|
||||
<form>
|
||||
<button data-v_id="{{v.id}}" data-type="validation_dut120" data-etudid="{{etud.id}}"
|
||||
>effacer</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if autorisations.first() %}
|
||||
<div class="jury_decisions_list jury_decisions_autorisation_inscription">
|
||||
<div>Autorisations d'inscriptions (passages)</div>
|
||||
@ -140,6 +156,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (response.ok) {
|
||||
location.reload();
|
||||
} else {
|
||||
console.log(`Error: ${SCO_URL}../api/etudiant/${etudid}/jury/${validation_type}/${v_id}/delete`);
|
||||
throw new Error('Request failed');
|
||||
}
|
||||
});
|
||||
|
@ -53,7 +53,7 @@ from app.but import (
|
||||
jury_but_view,
|
||||
)
|
||||
from app.but import bulletin_but_court # ne pas enlever: ajoute des routes !
|
||||
|
||||
from app.but import jury_dut120 # ne pas enlever: ajoute des routes !
|
||||
from app.but.forms import jury_but_forms
|
||||
|
||||
|
||||
@ -2568,6 +2568,13 @@ def formsemestre_validation_but(
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
|
||||
>enregistrer des UEs antérieures</a>
|
||||
|
||||
<a style="margin-left: 16px;" class="stdlink"
|
||||
href="{
|
||||
url_for("notes.validate_dut120_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
|
||||
>décerner le DUT "120ECTS"</a>
|
||||
"""
|
||||
H.append(
|
||||
f"""<div class="but_settings">
|
||||
|
58
migrations/versions/9794534db935_validationdut120.py
Normal file
58
migrations/versions/9794534db935_validationdut120.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""ValidationDUT120
|
||||
|
||||
Revision ID: 9794534db935
|
||||
Revises: 60119446aab6
|
||||
Create Date: 2024-07-06 17:36:41.576748
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = "9794534db935"
|
||||
down_revision = "60119446aab6"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
"validation_dut120",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("etudid", sa.Integer(), nullable=False),
|
||||
sa.Column("formsemestre_id", sa.Integer(), nullable=False),
|
||||
sa.Column("referentiel_competence_id", sa.Integer(), nullable=False),
|
||||
sa.Column(
|
||||
"date",
|
||||
sa.DateTime(timezone=True),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=False,
|
||||
),
|
||||
sa.ForeignKeyConstraint(["etudid"], ["identite.id"], ondelete="CASCADE"),
|
||||
sa.ForeignKeyConstraint(
|
||||
["formsemestre_id"],
|
||||
["notes_formsemestre.id"],
|
||||
ondelete="CASCADE",
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["referentiel_competence_id"],
|
||||
["apc_referentiel_competences.id"],
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
)
|
||||
with op.batch_alter_table("validation_dut120", schema=None) as batch_op:
|
||||
batch_op.create_index(
|
||||
batch_op.f("ix_validation_dut120_etudid"), ["etudid"], unique=False
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table("validation_dut120", schema=None) as batch_op:
|
||||
batch_op.drop_index(batch_op.f("ix_validation_dut120_etudid"))
|
||||
|
||||
op.drop_table("validation_dut120")
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue
Block a user