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,
|
ScolarNews,
|
||||||
Scolog,
|
Scolog,
|
||||||
UniteEns,
|
UniteEns,
|
||||||
|
ValidationDUT120,
|
||||||
)
|
)
|
||||||
from app.scodoc import codes_cursus
|
from app.scodoc import codes_cursus
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
@ -62,7 +63,7 @@ def decisions_jury(formsemestre_id: int):
|
|||||||
raise ScoException("non implemente")
|
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"
|
"génère news sur effacement décision"
|
||||||
# n'utilise pas g.scodoc_dept, pas toujours dispo en mode API
|
# n'utilise pas g.scodoc_dept, pas toujours dispo en mode API
|
||||||
url = url_for(
|
url = url_for(
|
||||||
@ -71,7 +72,7 @@ def _news_delete_jury_etud(etud: Identite):
|
|||||||
ScolarNews.add(
|
ScolarNews.add(
|
||||||
typ=ScolarNews.NEWS_JURY,
|
typ=ScolarNews.NEWS_JURY,
|
||||||
obj=etud.id,
|
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,
|
url=url,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -320,11 +321,11 @@ def validation_rcue_delete(etudid: int, validation_id: int):
|
|||||||
validation = ApcValidationRCUE.query.filter_by(
|
validation = ApcValidationRCUE.query.filter_by(
|
||||||
id=validation_id, etudid=etudid
|
id=validation_id, etudid=etudid
|
||||||
).first_or_404()
|
).first_or_404()
|
||||||
log(f"validation_ue_delete: etuid={etudid} {validation}")
|
log(f"delete validation_ue_delete: etuid={etudid} {validation}")
|
||||||
db.session.delete(validation)
|
db.session.delete(validation)
|
||||||
sco_cache.invalidate_formsemestre_etud(etud)
|
sco_cache.invalidate_formsemestre_etud(etud)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
_news_delete_jury_etud(etud)
|
_news_delete_jury_etud(etud, detail="UE")
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
@ -348,9 +349,38 @@ def validation_annee_but_delete(etudid: int, validation_id: int):
|
|||||||
validation = ApcValidationAnnee.query.filter_by(
|
validation = ApcValidationAnnee.query.filter_by(
|
||||||
id=validation_id, etudid=etudid
|
id=validation_id, etudid=etudid
|
||||||
).first_or_404()
|
).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)
|
db.session.delete(validation)
|
||||||
sco_cache.invalidate_formsemestre_etud(etud)
|
sco_cache.invalidate_formsemestre_etud(etud)
|
||||||
db.session.commit()
|
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"
|
return "ok"
|
||||||
|
@ -14,6 +14,7 @@ Classe raccordant avec ScoDoc 7:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
import collections
|
import collections
|
||||||
|
from collections.abc import Iterable
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
from flask import g, url_for
|
from flask import g, url_for
|
||||||
@ -382,18 +383,28 @@ class FormSemestreCursusBUT:
|
|||||||
# "cache { competence_id : competence }"
|
# "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é.
|
"""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,
|
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.
|
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 = (
|
validations = (
|
||||||
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
ScolarFormSemestreValidation.query.filter_by(etudid=etud.id)
|
||||||
.filter(ScolarFormSemestreValidation.ue_id != None)
|
.filter(ScolarFormSemestreValidation.ue_id != None)
|
||||||
.join(UniteEns)
|
.join(UniteEns)
|
||||||
.join(ApcNiveau)
|
.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 = {}
|
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,
|
ApcValidationAnnee,
|
||||||
ApcValidationRCUE,
|
ApcValidationRCUE,
|
||||||
Identite,
|
Identite,
|
||||||
UniteEns,
|
|
||||||
ScolarAutorisationInscription,
|
ScolarAutorisationInscription,
|
||||||
ScolarFormSemestreValidation,
|
ScolarFormSemestreValidation,
|
||||||
|
UniteEns,
|
||||||
|
ValidationDUT120,
|
||||||
)
|
)
|
||||||
from app.views import ScoData
|
from app.views import ScoData
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ def jury_delete_manual(etud: Identite):
|
|||||||
sem_vals=sem_vals,
|
sem_vals=sem_vals,
|
||||||
ue_vals=ue_vals,
|
ue_vals=ue_vals,
|
||||||
autorisations=autorisations,
|
autorisations=autorisations,
|
||||||
|
dut120_vals=ValidationDUT120.query.filter_by(etudid=etud.id).order_by(
|
||||||
|
ValidationDUT120.date
|
||||||
|
),
|
||||||
rcue_vals=rcue_vals,
|
rcue_vals=rcue_vals,
|
||||||
annee_but_vals=annee_but_vals,
|
annee_but_vals=annee_but_vals,
|
||||||
sco=ScoData(),
|
sco=ScoData(),
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"""Modèles base de données ScoDoc
|
"""Modèles base de données ScoDoc
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from flask import abort, g
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
@ -116,6 +117,31 @@ class ScoDocModel(db.Model):
|
|||||||
args = {field.name: field.data for field in form}
|
args = {field.name: field.data for field in form}
|
||||||
return self.from_dict(args)
|
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.absences import Absence, AbsenceNotification, BilletAbsence
|
||||||
from app.models.departements import Departement
|
from app.models.departements import Departement
|
||||||
@ -173,7 +199,11 @@ from app.models.but_refcomp import (
|
|||||||
ApcReferentielCompetences,
|
ApcReferentielCompetences,
|
||||||
ApcSituationPro,
|
ApcSituationPro,
|
||||||
)
|
)
|
||||||
from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE
|
from app.models.but_validations import (
|
||||||
|
ApcValidationAnnee,
|
||||||
|
ApcValidationRCUE,
|
||||||
|
ValidationDUT120,
|
||||||
|
)
|
||||||
|
|
||||||
from app.models.config import ScoDocSiteConfig
|
from app.models.config import ScoDocSiteConfig
|
||||||
|
|
||||||
|
@ -4,8 +4,10 @@
|
|||||||
"""
|
"""
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
from app import db
|
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.but_refcomp import ApcNiveau
|
||||||
from app.models.etudiants import Identite
|
from app.models.etudiants import Identite
|
||||||
from app.models.formsemestre import FormSemestre
|
from app.models.formsemestre import FormSemestre
|
||||||
@ -14,7 +16,7 @@ from app.scodoc import sco_preferences
|
|||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
|
||||||
|
|
||||||
class ApcValidationRCUE(db.Model):
|
class ApcValidationRCUE(ScoDocModel):
|
||||||
"""Validation des niveaux de compétences
|
"""Validation des niveaux de compétences
|
||||||
|
|
||||||
aka "regroupements cohérents d'UE" dans le jargon BUT.
|
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()
|
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"""
|
"""Validation des années du BUT"""
|
||||||
|
|
||||||
__tablename__ = "apc_validation_annee"
|
__tablename__ = "apc_validation_annee"
|
||||||
@ -277,3 +279,64 @@ def _build_decisions_rcue_list(decisions_rcue: dict) -> list[str]:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
return titres_rcues
|
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:
|
def html_link_fiche(self) -> str:
|
||||||
"lien vers la fiche"
|
"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:
|
def url_fiche(self) -> str:
|
||||||
"url de la fiche étudiant"
|
"url de la fiche étudiant"
|
||||||
@ -314,7 +316,7 @@ class Identite(models.ScoDocModel):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def nomprenom(self, reverse=False) -> str:
|
def nomprenom(self, reverse=False) -> str:
|
||||||
"""DEPRECATED
|
"""DEPRECATED: préférer nom_prenom()
|
||||||
Civilité/prénom/nom pour affichages: "M. Pierre Dupont"
|
Civilité/prénom/nom pour affichages: "M. Pierre Dupont"
|
||||||
Si reverse, "Dupont Pierre", sans civilité.
|
Si reverse, "Dupont Pierre", sans civilité.
|
||||||
Prend l'identité courante et non celle de l'état civil si elles diffèrent.
|
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 import log
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.but import cursus_but, validations_view
|
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 (
|
from app.scodoc import (
|
||||||
codes_cursus,
|
codes_cursus,
|
||||||
html_sco_header,
|
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()))
|
ects_total = sum((v.ects() for v in ue_validation_by_niveau.values()))
|
||||||
else:
|
else:
|
||||||
ects_total = ""
|
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[
|
info[
|
||||||
"but_cursus_mkup"
|
"but_cursus_mkup"
|
||||||
] = f"""
|
] = f"""
|
||||||
@ -463,6 +484,7 @@ def fiche_etud(etudid=None):
|
|||||||
"but/cursus_etud.j2",
|
"but/cursus_etud.j2",
|
||||||
cursus=but_cursus,
|
cursus=but_cursus,
|
||||||
scu=scu,
|
scu=scu,
|
||||||
|
validation_dut120_html=validation_dut120_html,
|
||||||
)}
|
)}
|
||||||
<div class="fiche_but_col2">
|
<div class="fiche_but_col2">
|
||||||
<div class="link_validation_rcues">
|
<div class="link_validation_rcues">
|
||||||
@ -471,7 +493,7 @@ def fiche_etud(etudid=None):
|
|||||||
formsemestre_id=last_formsemestre.id)}"
|
formsemestre_id=last_formsemestre.id)}"
|
||||||
title="Visualiser les compétences BUT"
|
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>
|
<div style="text-align: center;">Compétences BUT</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,6 +40,25 @@ div.code_rcue {
|
|||||||
position: relative;
|
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 {
|
div.no_niveau {
|
||||||
background-color: rgb(245, 237, 200);
|
background-color: rgb(245, 237, 200);
|
||||||
}
|
}
|
||||||
|
@ -959,7 +959,7 @@ td.fichetitre2 .fl {
|
|||||||
div.section_but {
|
div.section_but {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
justify-content: space-evenly;
|
justify-content: space-evenly;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -974,7 +974,8 @@ div.fiche_total_etcs {
|
|||||||
margin-top: 16px;
|
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;
|
align-self: center;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -4590,6 +4591,10 @@ table.table_recap tr td.jury_code_sem {
|
|||||||
border-left: 1px solid blue;
|
border-left: 1px solid blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.table_recap tr td.col_jury_link {
|
||||||
|
border-left: 1px solid blue;
|
||||||
|
}
|
||||||
|
|
||||||
table.table_recap .admission {
|
table.table_recap .admission {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
color: rgb(6, 73, 6);
|
color: rgb(6, 73, 6);
|
||||||
|
@ -18,7 +18,7 @@ from app.but import jury_but
|
|||||||
from app.but.jury_but import DecisionsProposeesRCUE
|
from app.but.jury_but import DecisionsProposeesRCUE
|
||||||
|
|
||||||
from app.comp.res_compat import NotesTableCompat
|
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.models.etudiants import Identite
|
||||||
from app.scodoc.codes_cursus import (
|
from app.scodoc.codes_cursus import (
|
||||||
BUT_BARRE_RCUE,
|
BUT_BARRE_RCUE,
|
||||||
@ -149,7 +149,7 @@ class TableJury(TableRecap):
|
|||||||
"ects_acquis",
|
"ects_acquis",
|
||||||
"ECTS",
|
"ECTS",
|
||||||
# res.get_etud_ects_valides(etud.id),
|
# 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(
|
cursus_but.but_ects_valides(
|
||||||
etud, res.formsemestre.formation.referentiel_competence_id
|
etud, res.formsemestre.formation.referentiel_competence_id
|
||||||
),
|
),
|
||||||
@ -157,6 +157,16 @@ class TableJury(TableRecap):
|
|||||||
classes=["recorded_code"],
|
classes=["recorded_code"],
|
||||||
target_attrs={"title": "crédits validés en BUT"},
|
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
|
# Lien saisie ou visu jury
|
||||||
a_saisir = (not res.validations) or (not res.validations.has_decision(etud))
|
a_saisir = (not res.validations) or (not res.validations.has_decision(etud))
|
||||||
row.add_cell(
|
row.add_cell(
|
||||||
|
@ -30,4 +30,10 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% 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>
|
</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>
|
</div>
|
||||||
{% endif %}
|
{% 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() %}
|
{% if autorisations.first() %}
|
||||||
<div class="jury_decisions_list jury_decisions_autorisation_inscription">
|
<div class="jury_decisions_list jury_decisions_autorisation_inscription">
|
||||||
<div>Autorisations d'inscriptions (passages)</div>
|
<div>Autorisations d'inscriptions (passages)</div>
|
||||||
@ -140,6 +156,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
location.reload();
|
location.reload();
|
||||||
} else {
|
} else {
|
||||||
|
console.log(`Error: ${SCO_URL}../api/etudiant/${etudid}/jury/${validation_type}/${v_id}/delete`);
|
||||||
throw new Error('Request failed');
|
throw new Error('Request failed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -53,7 +53,7 @@ from app.but import (
|
|||||||
jury_but_view,
|
jury_but_view,
|
||||||
)
|
)
|
||||||
from app.but import bulletin_but_court # ne pas enlever: ajoute des routes !
|
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
|
from app.but.forms import jury_but_forms
|
||||||
|
|
||||||
|
|
||||||
@ -2568,6 +2568,13 @@ def formsemestre_validation_but(
|
|||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
|
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
|
||||||
>enregistrer des UEs antérieures</a>
|
>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(
|
H.append(
|
||||||
f"""<div class="but_settings">
|
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