forked from ScoDoc/ScoDoc
Séparation vues notes/jurys et validations
This commit is contained in:
parent
9deae8cd6a
commit
d4fd6527e5
@ -4,13 +4,10 @@
|
||||
# See LICENSE
|
||||
##############################################################################
|
||||
|
||||
"""Jury édition manuelle des décisions (correction d'erreurs, parcours hors normes)
|
||||
|
||||
Non spécifique au BUT.
|
||||
"""Jury édition manuelle des décisions RCUE antérieures
|
||||
"""
|
||||
|
||||
from flask import render_template
|
||||
import sqlalchemy as sa
|
||||
|
||||
from app import log
|
||||
from app.but import cursus_but
|
||||
|
@ -45,16 +45,15 @@ import app.scodoc.sco_utils as scu
|
||||
|
||||
|
||||
# ---- Table recap formation
|
||||
def formation_table_recap(formation_id, fmt="html") -> Response:
|
||||
def formation_table_recap(formation: Formation, fmt="html") -> Response:
|
||||
"""Table recapitulant formation."""
|
||||
T = []
|
||||
formation = Formation.query.get_or_404(formation_id)
|
||||
rows = []
|
||||
ues = formation.ues.order_by(UniteEns.semestre_idx, UniteEns.numero)
|
||||
can_edit = current_user.has_permission(Permission.EditFormation)
|
||||
li = 0
|
||||
for ue in ues:
|
||||
# L'UE
|
||||
T.append(
|
||||
rows.append(
|
||||
{
|
||||
"sem": f"S{ue.semestre_idx}" if ue.semestre_idx is not None else "-",
|
||||
"_sem_order": f"{li:04d}",
|
||||
@ -83,7 +82,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
||||
for mod in modules:
|
||||
nb_moduleimpls = mod.modimpls.count()
|
||||
# le module (ou ressource ou sae)
|
||||
T.append(
|
||||
rows.append(
|
||||
{
|
||||
"sem": (
|
||||
f"S{mod.semestre_id}"
|
||||
@ -152,7 +151,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
||||
|
||||
tab = GenTable(
|
||||
columns_ids=columns_ids,
|
||||
rows=T,
|
||||
rows=rows,
|
||||
titles=titles,
|
||||
origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
|
||||
caption=title,
|
||||
@ -168,7 +167,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
||||
}"
|
||||
""",
|
||||
html_with_td_classes=True,
|
||||
base_url=f"{request.base_url}?formation_id={formation_id}",
|
||||
base_url=f"{request.base_url}",
|
||||
page_title=title,
|
||||
html_title=f"<h2>{title}</h2>",
|
||||
pdf_title=title,
|
||||
@ -192,7 +191,7 @@ def export_recap_formations_annee_scolaire(annee_scolaire):
|
||||
formation_ids = {formsemestre.formation.id for formsemestre in formsemestres}
|
||||
for formation_id in formation_ids:
|
||||
formation = db.session.get(Formation, formation_id)
|
||||
xls = formation_table_recap(formation_id, fmt="xlsx").data
|
||||
xls = formation_table_recap(formation, fmt="xlsx").data
|
||||
filename = (
|
||||
scu.sanitize_filename(formation.get_titre_version()) + scu.XLSX_SUFFIX
|
||||
)
|
||||
|
@ -140,10 +140,15 @@ class ScoData:
|
||||
return sco_formsemestre_status.formsemestre_status_menubar(self.formsemestre)
|
||||
|
||||
|
||||
# Ajout 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.pe import pe_view # ne pas enlever, ajoute des routes !
|
||||
from app.views import (
|
||||
absences,
|
||||
assiduites,
|
||||
but_formation,
|
||||
jury_validations,
|
||||
notes_formsemestre,
|
||||
notes,
|
||||
pn_modules,
|
||||
|
904
app/views/jury_validations.py
Normal file
904
app/views/jury_validations.py
Normal file
@ -0,0 +1,904 @@
|
||||
##############################################################################
|
||||
#
|
||||
# ScoDoc
|
||||
#
|
||||
# Copyright (c) 1999 - 2024 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 jurys et validations
|
||||
|
||||
Emmanuel Viennet, 2024
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import flask
|
||||
from flask import flash, g, redirect, render_template, request, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
from app import log
|
||||
from app.but import (
|
||||
cursus_but,
|
||||
jury_edit_manual,
|
||||
jury_but,
|
||||
jury_but_validation_auto,
|
||||
jury_but_view,
|
||||
)
|
||||
from app.but.forms import jury_but_forms
|
||||
from app.comp import jury
|
||||
from app.decorators import (
|
||||
scodoc,
|
||||
scodoc7func,
|
||||
permission_required,
|
||||
)
|
||||
from app.models import (
|
||||
Evaluation,
|
||||
Formation,
|
||||
FormSemestre,
|
||||
FormSemestreInscription,
|
||||
Identite,
|
||||
ScolarAutorisationInscription,
|
||||
ScolarNews,
|
||||
ScoDocSiteConfig,
|
||||
)
|
||||
from app.scodoc import (
|
||||
html_sco_header,
|
||||
sco_bulletins_json,
|
||||
sco_cache,
|
||||
sco_formsemestre_exterieurs,
|
||||
sco_formsemestre_validation,
|
||||
sco_preferences,
|
||||
)
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import (
|
||||
ScoPermissionDenied,
|
||||
ScoValueError,
|
||||
)
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_pv_dict import descr_autorisations
|
||||
|
||||
# from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.views import notes_bp as bp
|
||||
from app.views import ScoData
|
||||
|
||||
|
||||
# --- FORMULAIRE POUR VALIDATION DES UE ET SEMESTRES
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud_form")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud_form(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
etud_index=None,
|
||||
check=0,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Formulaire choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
read_only = not formsemestre.can_edit_jury()
|
||||
if formsemestre.formation.is_apc():
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud_form(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
etud_index=etud_index,
|
||||
check=check,
|
||||
read_only=read_only,
|
||||
dest_url=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
codechoice=None,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Enregistre choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
codechoice=codechoice,
|
||||
desturl=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud_manu")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud_manu(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
code_etat="",
|
||||
new_code_prev="",
|
||||
devenir="",
|
||||
assidu=False,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Enregistre choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud_manu(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
code_etat=code_etat,
|
||||
new_code_prev=new_code_prev,
|
||||
devenir=devenir,
|
||||
assidu=assidu,
|
||||
desturl=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
# --- Jurys BUT
|
||||
@bp.route(
|
||||
"/formsemestre_validation_but/<int:formsemestre_id>/<etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validation_but(
|
||||
formsemestre_id: int,
|
||||
etudid: int,
|
||||
):
|
||||
"Form. saisie décision jury semestre BUT"
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
# la route ne donne pas le type d'etudid pour pouvoir construire des URLs
|
||||
# provisoires avec NEXT et PREV
|
||||
try:
|
||||
etudid = int(etudid)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("adresse invalide") from exc
|
||||
etud = Identite.get_etud(etudid)
|
||||
nb_etuds = formsemestre.etuds.count()
|
||||
read_only = not formsemestre.can_edit_jury()
|
||||
can_erase = current_user.has_permission(Permission.EtudInscrit)
|
||||
# --- Navigation
|
||||
prev_lnk = (
|
||||
f"""{scu.EMO_PREV_ARROW} <a href="{url_for(
|
||||
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id, etudid="PREV"
|
||||
)}" class="stdlink"">précédent</a>
|
||||
"""
|
||||
if nb_etuds > 1
|
||||
else ""
|
||||
)
|
||||
next_lnk = (
|
||||
f"""<a href="{url_for(
|
||||
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id, etudid="NEXT"
|
||||
)}" class="stdlink"">suivant</a> {scu.EMO_NEXT_ARROW}
|
||||
"""
|
||||
if nb_etuds > 1
|
||||
else ""
|
||||
)
|
||||
navigation_div = f"""
|
||||
<div class="but_navigation">
|
||||
<div class="prev">
|
||||
{prev_lnk}
|
||||
</div>
|
||||
<div class="back_list">
|
||||
<a href="{
|
||||
url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
selected_etudid=etud.id
|
||||
)}" class="stdlink">retour à la liste</a>
|
||||
</div>
|
||||
<div class="next">
|
||||
{next_lnk}
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
page_title=f"Validation BUT S{formsemestre.semestre_id}",
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
cssstyles=[
|
||||
"css/jury_but.css",
|
||||
"css/cursus_but.css",
|
||||
],
|
||||
javascripts=("js/jury_but.js",),
|
||||
),
|
||||
"""<div class="jury_but">
|
||||
""",
|
||||
]
|
||||
|
||||
if formsemestre.etuds_inscriptions[etudid].etat != scu.INSCRIT:
|
||||
return (
|
||||
"\n".join(H)
|
||||
+ f"""
|
||||
<div>
|
||||
<div class="bull_head">
|
||||
<div>
|
||||
<div class="titre_parcours">Jury BUT</div>
|
||||
<div class="nom_etud">{etud.html_link_fiche()}</div>
|
||||
</div>
|
||||
<div class="bull_photo"><a href="{
|
||||
etud.url_fiche()
|
||||
}">{etud.photo_html(title="fiche de " + etud.nomprenom)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning">Impossible de statuer sur cet étudiant:
|
||||
il est démissionnaire ou défaillant (voir <a class="stdlink" href="{
|
||||
etud.url_fiche()}">sa fiche</a>)
|
||||
</div>
|
||||
</div>
|
||||
{navigation_div}
|
||||
</div>
|
||||
"""
|
||||
+ html_sco_header.sco_footer()
|
||||
)
|
||||
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
has_notes_en_attente = deca.has_notes_en_attente()
|
||||
evaluations_a_debloquer = Evaluation.get_evaluations_blocked_for_etud(
|
||||
formsemestre, etud
|
||||
)
|
||||
if has_notes_en_attente or evaluations_a_debloquer:
|
||||
read_only = True
|
||||
if request.method == "POST":
|
||||
if not read_only:
|
||||
deca.record_form(request.form)
|
||||
ScolarNews.add(
|
||||
typ=ScolarNews.NEWS_JURY,
|
||||
obj=formsemestre.id,
|
||||
text=f"""Saisie décision jury dans {formsemestre.html_link_status()}""",
|
||||
url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
),
|
||||
)
|
||||
flash("codes enregistrés")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
)
|
||||
|
||||
warning = ""
|
||||
if len(deca.niveaux_competences) != len(deca.decisions_rcue_by_niveau):
|
||||
warning += f"""<div class="warning">Attention: {len(deca.niveaux_competences)}
|
||||
niveaux mais {len(deca.decisions_rcue_by_niveau)} regroupements RCUE.</div>"""
|
||||
if (deca.parcour is None) and len(formsemestre.parcours) > 0:
|
||||
warning += (
|
||||
"""<div class="warning">L'étudiant n'est pas inscrit à un parcours.</div>"""
|
||||
)
|
||||
if formsemestre.date_fin - datetime.date.today() > datetime.timedelta(days=12):
|
||||
# encore loin de la fin du semestre de départ de ce jury ?
|
||||
warning += f"""<div class="warning">Le semestre S{formsemestre.semestre_id}
|
||||
terminera le {formsemestre.date_fin.strftime(scu.DATE_FMT)} :
|
||||
êtes-vous certain de vouloir enregistrer une décision de jury ?
|
||||
</div>"""
|
||||
|
||||
if deca.formsemestre_impair:
|
||||
inscription = deca.formsemestre_impair.etuds_inscriptions.get(etud.id)
|
||||
if (not inscription) or inscription.etat != scu.INSCRIT:
|
||||
etat_ins = scu.ETATS_INSCRIPTION.get(inscription.etat, "inconnu?")
|
||||
warning += f"""<div class="warning">{etat_ins}
|
||||
en S{deca.formsemestre_impair.semestre_id}</div>"""
|
||||
|
||||
if deca.formsemestre_pair:
|
||||
inscription = deca.formsemestre_pair.etuds_inscriptions.get(etud.id)
|
||||
if (not inscription) or inscription.etat != scu.INSCRIT:
|
||||
etat_ins = scu.ETATS_INSCRIPTION.get(inscription.etat, "inconnu?")
|
||||
warning += f"""<div class="warning">{etat_ins}
|
||||
en S{deca.formsemestre_pair.semestre_id}</div>"""
|
||||
|
||||
if has_notes_en_attente:
|
||||
warning += f"""<div class="warning-bloquant">{etud.html_link_fiche()
|
||||
} a des notes en ATTente dans les modules suivants.
|
||||
Vous devez régler cela avant de statuer en jury !
|
||||
<ul class="modimpls_att">
|
||||
"""
|
||||
for modimpl in deca.get_modimpls_attente():
|
||||
warning += f"""<li><a href="{
|
||||
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
||||
}" class="stdlink">{modimpl.module.code} {modimpl.module.titre_str()}</a></li>"""
|
||||
warning += "</ul></div>"
|
||||
if evaluations_a_debloquer:
|
||||
links_evals = [
|
||||
f"""<a class="stdlink" href="{url_for(
|
||||
'notes.evaluation_listenotes', scodoc_dept=g.scodoc_dept, evaluation_id=e.id
|
||||
)}">{e.description} en {e.moduleimpl.module.code}</a>"""
|
||||
for e in evaluations_a_debloquer
|
||||
]
|
||||
warning += f"""<div class="warning-bloquant">Impossible de statuer sur cet étudiant:
|
||||
il a des notes dans des évaluations qui seront débloquées plus tard:
|
||||
voir {", ".join(links_evals)}
|
||||
"""
|
||||
|
||||
if warning:
|
||||
warning = f"""<div class="jury_but_warning jury_but_box">{warning}</div>"""
|
||||
H.append(
|
||||
f"""
|
||||
<div>
|
||||
<div class="bull_head">
|
||||
<div>
|
||||
<div class="titre_parcours">Jury BUT{deca.annee_but}
|
||||
- Parcours {(deca.parcour.libelle if deca.parcour else False) or "non spécifié"}
|
||||
- {deca.annee_scolaire_str()}</div>
|
||||
<div class="nom_etud">{etud.html_link_fiche()}</div>
|
||||
</div>
|
||||
<div class="bull_photo"><a href="{
|
||||
etud.url_fiche()}">{etud.photo_html(title="fiche de " + etud.nomprenom)}</a>
|
||||
</div>
|
||||
</div>
|
||||
{warning}
|
||||
</div>
|
||||
|
||||
<form method="post" class="jury_but_box" id="jury_but">
|
||||
"""
|
||||
)
|
||||
|
||||
H.append(jury_but_view.show_etud(deca, read_only=read_only))
|
||||
|
||||
autorisations_idx = deca.get_autorisations_passage()
|
||||
div_autorisations_passage = (
|
||||
f"""
|
||||
<div class="but_autorisations_passage">
|
||||
<span>Autorisé à passer en :</span>
|
||||
{ ", ".join( ["S" + str(i) for i in autorisations_idx ] )}
|
||||
</div>
|
||||
"""
|
||||
if autorisations_idx
|
||||
else """<div class="but_autorisations_passage but_explanation">
|
||||
pas d'autorisations de passage enregistrées.
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
H.append(div_autorisations_passage)
|
||||
|
||||
if read_only:
|
||||
H.append(
|
||||
f"""
|
||||
<div class="but_explanation">
|
||||
{"Vous n'avez pas la permission de modifier ces décisions."
|
||||
if formsemestre.etat
|
||||
else "Semestre verrouillé."}
|
||||
Les champs entourés en vert sont enregistrés.
|
||||
</div>"""
|
||||
)
|
||||
else:
|
||||
erase_span = f"""
|
||||
<a style="margin-left: 16px;" class="stdlink {'' if can_erase else 'link_unauthorized'}"
|
||||
title="{'' if can_erase else 'réservé au responsable'}"
|
||||
href="{
|
||||
url_for("notes.erase_decisions_annee_formation",
|
||||
scodoc_dept=g.scodoc_dept, formation_id=deca.formsemestre.formation.id,
|
||||
etudid=deca.etud.id, annee=deca.annee_but, formsemestre_id=formsemestre_id)
|
||||
if can_erase else ''
|
||||
}"
|
||||
>effacer des décisions de jury</a>
|
||||
|
||||
<a style="margin-left: 16px;" class="stdlink"
|
||||
href="{
|
||||
url_for("notes.formsemestre_validate_previous_ue",
|
||||
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">
|
||||
<input type="checkbox" onchange="enable_manual_codes(this)">
|
||||
<em>permettre la saisie manuelles des codes
|
||||
{"d'année et " if deca.jury_annuel else ""}
|
||||
de niveaux.
|
||||
Dans ce cas, assurez-vous de la cohérence entre les codes d'UE/RCUE/Année !
|
||||
</em>
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="but_buttons">
|
||||
<span><input type="submit" value="Enregistrer ces décisions"></span>
|
||||
<span>{erase_span}</span>
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
H.append(navigation_div)
|
||||
H.append("</form>")
|
||||
|
||||
# Affichage cursus BUT
|
||||
but_cursus = cursus_but.EtudCursusBUT(etud, deca.formsemestre.formation)
|
||||
H += [
|
||||
"""<div class="jury_but_box">
|
||||
<div class="jury_but_box_title"><b>Niveaux de compétences enregistrés :</b></div>
|
||||
""",
|
||||
render_template(
|
||||
"but/cursus_etud.j2",
|
||||
cursus=but_cursus,
|
||||
scu=scu,
|
||||
),
|
||||
"</div>",
|
||||
]
|
||||
H.append(
|
||||
render_template(
|
||||
"but/documentation_codes_jury.j2",
|
||||
nom_univ=f"""Export {sco_preferences.get_preference("InstituteName")
|
||||
or sco_preferences.get_preference("UnivName")
|
||||
or "Apogée"}""",
|
||||
codes=ScoDocSiteConfig.get_codes_apo_dict(),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
f"""<div class="but_doc_codes but_warning_rcue_cap">
|
||||
{scu.EMO_WARNING} Rappel: pour les redoublants, seules les UE <b>capitalisées</b> (note > 10)
|
||||
lors d'une année précédente peuvent être prise en compte pour former
|
||||
un RCUE (associé à un niveau de compétence du BUT).
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_validation_auto_but/<int:formsemestre_id>", methods=["GET", "POST"]
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validation_auto_but(formsemestre_id: int = None):
|
||||
"Saisie automatique des décisions de jury BUT"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
if not formsemestre.formation.is_apc():
|
||||
raise ScoValueError(
|
||||
"formsemestre_validation_auto_but est réservé aux formations APC"
|
||||
)
|
||||
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
form = jury_but_forms.FormSemestreValidationAutoBUTForm()
|
||||
if request.method == "POST":
|
||||
if not form.cancel.data:
|
||||
nb_etud_modif, _ = (
|
||||
jury_but_validation_auto.formsemestre_validation_auto_but(formsemestre)
|
||||
)
|
||||
flash(f"Décisions enregistrées ({nb_etud_modif} étudiants modifiés)")
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
)
|
||||
)
|
||||
# Avertissement si formsemestre impair
|
||||
formsemestres_suspects = {}
|
||||
if formsemestre.semestre_id % 2:
|
||||
_, decas = jury_but_validation_auto.formsemestre_validation_auto_but(
|
||||
formsemestre, dry_run=True
|
||||
)
|
||||
# regarde si il y a des semestres pairs postérieurs qui ne soient pas bloqués
|
||||
formsemestres_suspects = {
|
||||
deca.formsemestre_pair.id: deca.formsemestre_pair
|
||||
for deca in decas
|
||||
if deca.formsemestre_pair
|
||||
and deca.formsemestre_pair.date_debut > formsemestre.date_debut
|
||||
and not deca.formsemestre_pair.block_moyennes
|
||||
}
|
||||
|
||||
return render_template(
|
||||
"but/formsemestre_validation_auto_but.j2",
|
||||
form=form,
|
||||
formsemestres_suspects=formsemestres_suspects,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
title="Calcul automatique jury BUT",
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_validate_previous_ue/<int:formsemestre_id>/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
|
||||
"Form. saisie UE validée hors ScoDoc"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
etud: Identite = (
|
||||
Identite.query.filter_by(id=etudid)
|
||||
.join(FormSemestreInscription)
|
||||
.filter_by(formsemestre_id=formsemestre_id)
|
||||
.first_or_404()
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validate_previous_ue(
|
||||
formsemestre, etud
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_ext_edit_ue_validations", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid=None):
|
||||
"Form. edition UE semestre extérieur"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
|
||||
formsemestre_id, etudid
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_auto")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_auto(formsemestre_id):
|
||||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
if formsemestre.formation.is_apc():
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_auto_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validation_auto(formsemestre_id)
|
||||
|
||||
|
||||
@bp.route("/do_formsemestre_validation_auto")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def do_formsemestre_validation_auto(formsemestre_id):
|
||||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.do_formsemestre_validation_auto(formsemestre_id)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_suppress_etud", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_suppress_etud(
|
||||
formsemestre_id, etudid, dialog_confirmed=False
|
||||
):
|
||||
"""Suppression des décisions de jury pour un étudiant."""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
etud = Identite.get_etud(etudid)
|
||||
if formsemestre.formation.is_apc():
|
||||
next_url = url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etudid,
|
||||
)
|
||||
else:
|
||||
next_url = url_for(
|
||||
"notes.formsemestre_validation_etud_form",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
if not dialog_confirmed:
|
||||
d = sco_bulletins_json.dict_decision_jury(
|
||||
etud, formsemestre, with_decisions=True
|
||||
)
|
||||
|
||||
descr_ues = [f"{u['acronyme']}: {u['code']}" for u in d.get("decision_ue", [])]
|
||||
dec_annee = d.get("decision_annee")
|
||||
if dec_annee:
|
||||
descr_annee = dec_annee.get("code", "-")
|
||||
else:
|
||||
descr_annee = "-"
|
||||
|
||||
existing = f"""
|
||||
<ul>
|
||||
<li>Semestre : {d.get("decision", {"code":"-"})['code'] or "-"}</li>
|
||||
<li>Année BUT: {descr_annee}</li>
|
||||
<li>UEs : {", ".join(descr_ues)}</li>
|
||||
<li>RCUEs: {len(d.get("decision_rcue", []))} décisions</li>
|
||||
<li>Autorisations: {descr_autorisations(ScolarAutorisationInscription.query.filter_by(origin_formsemestre_id=formsemestre_id,
|
||||
etudid=etudid))}
|
||||
</ul>
|
||||
"""
|
||||
return scu.confirm_dialog(
|
||||
f"""<h2>Confirmer la suppression des décisions du semestre
|
||||
{formsemestre.titre_mois()} pour {etud.nomprenom}
|
||||
</h2>
|
||||
<p>Cette opération est irréversible.</p>
|
||||
<div>
|
||||
{existing}
|
||||
</div>
|
||||
""",
|
||||
OK="Supprimer",
|
||||
dest_url="",
|
||||
cancel_url=next_url,
|
||||
parameters={"etudid": etudid, "formsemestre_id": formsemestre_id},
|
||||
)
|
||||
|
||||
sco_formsemestre_validation.formsemestre_validation_suppress_etud(
|
||||
formsemestre_id, etudid
|
||||
)
|
||||
flash("Décisions supprimées")
|
||||
return flask.redirect(next_url)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_jury_erase/<int:formsemestre_id>",
|
||||
methods=["GET", "POST"],
|
||||
defaults={"etudid": None},
|
||||
)
|
||||
@bp.route(
|
||||
"/formsemestre_jury_erase/<int:formsemestre_id>/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_jury_erase(formsemestre_id: int, etudid: int = None):
|
||||
"""Supprime toutes les décisions de jury (classique ou BUT) pour cette année.
|
||||
Si l'étudiant n'est pas spécifié, efface les décisions de tous les inscrits.
|
||||
En BUT, si only_one_sem n'efface que pour le formsemestre indiqué, pas les deux de l'année.
|
||||
En classique, n'affecte que les décisions issues de ce formsemestre.
|
||||
"""
|
||||
only_one_sem = int(request.args.get("only_one_sem") or False)
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
is_apc = formsemestre.formation.is_apc()
|
||||
if etudid is None:
|
||||
etud = None
|
||||
etuds = formsemestre.get_inscrits(include_demdef=True)
|
||||
dest_url = url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
)
|
||||
else:
|
||||
etud = Identite.get_etud(etudid)
|
||||
etuds = [etud]
|
||||
endpoint = (
|
||||
"notes.formsemestre_validation_but"
|
||||
if is_apc
|
||||
else "notes.formsemestre_validation_etud_form"
|
||||
)
|
||||
dest_url = url_for(
|
||||
endpoint,
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
if request.method == "POST":
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
for etud in etuds:
|
||||
if is_apc:
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
deca.erase(only_one_sem=only_one_sem)
|
||||
else:
|
||||
sco_formsemestre_validation.formsemestre_validation_suppress_etud(
|
||||
formsemestre.id, etud.id
|
||||
)
|
||||
log(f"formsemestre_jury_erase({formsemestre_id}, {etud.id})")
|
||||
flash(
|
||||
(
|
||||
"décisions de jury du semestre effacées"
|
||||
if (only_one_sem or is_apc)
|
||||
else "décisions de jury des semestres de l'année BUT effacées"
|
||||
)
|
||||
+ f" pour {len(etuds)} étudiant{'s' if len(etuds) > 1 else ''}"
|
||||
)
|
||||
return redirect(dest_url)
|
||||
|
||||
return render_template(
|
||||
"confirm_dialog.j2",
|
||||
title=f"""Effacer les validations de jury {
|
||||
("de " + etud.nomprenom)
|
||||
if etud
|
||||
else ("des " + str(len(etuds)) + " étudiants inscrits dans ce semestre")
|
||||
} ?""",
|
||||
explanation=(
|
||||
(
|
||||
f"""Les validations d'UE et autorisations de passage
|
||||
du semestre S{formsemestre.semestre_id} seront effacées."""
|
||||
if (only_one_sem or is_apc)
|
||||
else """Les validations de toutes les UE, RCUE (compétences) et année
|
||||
issues de cette année scolaire seront effacées.
|
||||
"""
|
||||
)
|
||||
+ """
|
||||
<p>Les décisions des années scolaires précédentes ne seront pas modifiées.</p>
|
||||
"""
|
||||
+ """
|
||||
<p>Efface aussi toutes les validations concernant l'année BUT de ce semestre,
|
||||
même si elles ont été acquises ailleurs, ainsi que les validations de DUT en 120 ECTS
|
||||
obtenues après BUT1/BUT2.
|
||||
</p>
|
||||
"""
|
||||
if is_apc
|
||||
else ""
|
||||
+ """
|
||||
<div class="warning">Cette opération est irréversible !
|
||||
A n'utiliser que dans des cas exceptionnels, vérifiez bien tous les étudiants ensuite.
|
||||
</div>
|
||||
"""
|
||||
),
|
||||
cancel_url=dest_url,
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/erase_decisions_annee_formation/<int:etudid>/<int:formation_id>/<int:annee>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
def erase_decisions_annee_formation(etudid: int, formation_id: int, annee: int):
|
||||
"""Efface toute les décisions d'une année pour cet étudiant"""
|
||||
etud: Identite = Identite.query.get_or_404(etudid)
|
||||
formation: Formation = Formation.query.filter_by(
|
||||
id=formation_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if request.method == "POST":
|
||||
jury.erase_decisions_annee_formation(etud, formation, annee, delete=True)
|
||||
flash("Décisions de jury effacées")
|
||||
return redirect(
|
||||
url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud.id,
|
||||
)
|
||||
)
|
||||
validations = jury.erase_decisions_annee_formation(etud, formation, annee)
|
||||
formsemestre_origine_id = request.args.get("formsemestre_id")
|
||||
formsemestre_origine = (
|
||||
FormSemestre.query.get_or_404(formsemestre_origine_id)
|
||||
if formsemestre_origine_id
|
||||
else None
|
||||
)
|
||||
return render_template(
|
||||
"jury/erase_decisions_annee_formation.j2",
|
||||
annee=annee,
|
||||
cancel_url=url_for(
|
||||
"scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||
),
|
||||
etud=etud,
|
||||
formation=formation,
|
||||
formsemestre_origine=formsemestre_origine,
|
||||
validations=validations,
|
||||
sco=ScoData(),
|
||||
title=f"Effacer décisions de jury {etud.nom} - année {annee}",
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/jury_delete_manual/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
def jury_delete_manual(etudid: int):
|
||||
"""Efface toute les décisions d'une année pour cet étudiant"""
|
||||
etud = Identite.get_etud(etudid)
|
||||
return jury_edit_manual.jury_delete_manual(etud)
|
@ -30,7 +30,6 @@ Module notes: issu de ScoDoc7 / ZNotes.py
|
||||
|
||||
Emmanuel Viennet, 2021
|
||||
"""
|
||||
import datetime
|
||||
import html
|
||||
from operator import itemgetter
|
||||
import time
|
||||
@ -40,24 +39,13 @@ from flask import flash, redirect, render_template, url_for
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app import db
|
||||
from app import db, log, send_scodoc_alarm
|
||||
from app import models
|
||||
from app.auth.models import User
|
||||
from app.but import (
|
||||
apc_edit_ue,
|
||||
cursus_but,
|
||||
jury_edit_manual,
|
||||
jury_but,
|
||||
jury_but_pv,
|
||||
jury_but_validation_auto,
|
||||
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
|
||||
from app.but import apc_edit_ue, jury_but_pv
|
||||
|
||||
|
||||
from app.comp import jury, res_sem
|
||||
from app.comp import res_sem
|
||||
from app.comp.res_compat import NotesTableCompat
|
||||
from app.models import (
|
||||
ApcNiveau,
|
||||
@ -72,10 +60,7 @@ from app.models import (
|
||||
Identite,
|
||||
Module,
|
||||
ModuleImpl,
|
||||
ScolarAutorisationInscription,
|
||||
ScolarNews,
|
||||
Scolog,
|
||||
ScoDocSiteConfig,
|
||||
UniteEns,
|
||||
)
|
||||
from app.scodoc.sco_exceptions import ScoFormationConflict, ScoPermissionDenied
|
||||
@ -90,9 +75,7 @@ from app.decorators import (
|
||||
|
||||
|
||||
# ---------------
|
||||
from app.pe import pe_view # ne pas enlever, ajoute des vues
|
||||
from app.scodoc import sco_bulletins_json, sco_utils as scu
|
||||
from app import log, send_scodoc_alarm
|
||||
from app.scodoc import sco_utils as scu
|
||||
|
||||
from app.scodoc.sco_exceptions import (
|
||||
AccessDenied,
|
||||
@ -131,7 +114,6 @@ from app.scodoc import (
|
||||
sco_formsemestre_exterieurs,
|
||||
sco_formsemestre_inscriptions,
|
||||
sco_formsemestre_status,
|
||||
sco_formsemestre_validation,
|
||||
sco_groups_view,
|
||||
sco_inscr_passage,
|
||||
sco_liste_notes,
|
||||
@ -157,7 +139,6 @@ from app.scodoc import (
|
||||
sco_users,
|
||||
)
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc.sco_pv_dict import descr_autorisations
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.views import ScoData
|
||||
@ -560,12 +541,14 @@ sco_publish(
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formation_table_recap")
|
||||
@bp.route("/formation_table_recap/<int:formation_id>")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formation_table_recap(formation_id, fmt="html"):
|
||||
return sco_formation_recap.formation_table_recap(formation_id, fmt=fmt)
|
||||
def formation_table_recap(formation_id: int):
|
||||
"Tableau récap. de la formation"
|
||||
formation = Formation.get_formation(formation_id)
|
||||
fmt = request.args.get("fmt", "html")
|
||||
return sco_formation_recap.formation_table_recap(formation, fmt=fmt)
|
||||
|
||||
|
||||
sco_publish(
|
||||
@ -829,9 +812,9 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.EditFormation)
|
||||
@permission_required(Permission.EditFormation)
|
||||
def ue_clone():
|
||||
"""Clone existing UE"""
|
||||
ue_id = int(request.form.get("ue_id"))
|
||||
ue = UniteEns.query.get_or_404(ue_id)
|
||||
ue2 = ue.clone()
|
||||
ue_id = request.form.get("ue_id")
|
||||
ue = UniteEns.get_ue(ue_id)
|
||||
_ = ue.clone()
|
||||
db.session.commit()
|
||||
flash(f"UE {ue.acronyme} dupliquée")
|
||||
return flask.redirect(
|
||||
@ -2205,520 +2188,6 @@ def appreciation_add_form(
|
||||
return flask.redirect(bul_url)
|
||||
|
||||
|
||||
# --- FORMULAIRE POUR VALIDATION DES UE ET SEMESTRES
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud_form")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud_form(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
etud_index=None,
|
||||
check=0,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Formulaire choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
read_only = not formsemestre.can_edit_jury()
|
||||
if formsemestre.formation.is_apc():
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud_form(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
etud_index=etud_index,
|
||||
check=check,
|
||||
read_only=read_only,
|
||||
dest_url=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
codechoice=None,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Enregistre choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
codechoice=codechoice,
|
||||
desturl=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_etud_manu")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_etud_manu(
|
||||
formsemestre_id,
|
||||
etudid=None,
|
||||
code_etat="",
|
||||
new_code_prev="",
|
||||
devenir="",
|
||||
assidu=False,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
):
|
||||
"Enregistre choix jury pour un étudiant"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validation_etud_manu(
|
||||
formsemestre_id,
|
||||
etudid=etudid,
|
||||
code_etat=code_etat,
|
||||
new_code_prev=new_code_prev,
|
||||
devenir=devenir,
|
||||
assidu=assidu,
|
||||
desturl=desturl,
|
||||
sortcol=sortcol,
|
||||
)
|
||||
|
||||
|
||||
# --- Jurys BUT
|
||||
@bp.route(
|
||||
"/formsemestre_validation_but/<int:formsemestre_id>/<etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validation_but(
|
||||
formsemestre_id: int,
|
||||
etudid: int,
|
||||
):
|
||||
"Form. saisie décision jury semestre BUT"
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
# la route ne donne pas le type d'etudid pour pouvoir construire des URLs
|
||||
# provisoires avec NEXT et PREV
|
||||
try:
|
||||
etudid = int(etudid)
|
||||
except ValueError as exc:
|
||||
raise ScoValueError("adresse invalide") from exc
|
||||
etud = Identite.get_etud(etudid)
|
||||
nb_etuds = formsemestre.etuds.count()
|
||||
read_only = not formsemestre.can_edit_jury()
|
||||
can_erase = current_user.has_permission(Permission.EtudInscrit)
|
||||
# --- Navigation
|
||||
prev_lnk = (
|
||||
f"""{scu.EMO_PREV_ARROW} <a href="{url_for(
|
||||
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id, etudid="PREV"
|
||||
)}" class="stdlink"">précédent</a>
|
||||
"""
|
||||
if nb_etuds > 1
|
||||
else ""
|
||||
)
|
||||
next_lnk = (
|
||||
f"""<a href="{url_for(
|
||||
"notes.formsemestre_validation_but", scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id, etudid="NEXT"
|
||||
)}" class="stdlink"">suivant</a> {scu.EMO_NEXT_ARROW}
|
||||
"""
|
||||
if nb_etuds > 1
|
||||
else ""
|
||||
)
|
||||
navigation_div = f"""
|
||||
<div class="but_navigation">
|
||||
<div class="prev">
|
||||
{prev_lnk}
|
||||
</div>
|
||||
<div class="back_list">
|
||||
<a href="{
|
||||
url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
selected_etudid=etud.id
|
||||
)}" class="stdlink">retour à la liste</a>
|
||||
</div>
|
||||
<div class="next">
|
||||
{next_lnk}
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
page_title=f"Validation BUT S{formsemestre.semestre_id}",
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
cssstyles=[
|
||||
"css/jury_but.css",
|
||||
"css/cursus_but.css",
|
||||
],
|
||||
javascripts=("js/jury_but.js",),
|
||||
),
|
||||
"""<div class="jury_but">
|
||||
""",
|
||||
]
|
||||
|
||||
if formsemestre.etuds_inscriptions[etudid].etat != scu.INSCRIT:
|
||||
return (
|
||||
"\n".join(H)
|
||||
+ f"""
|
||||
<div>
|
||||
<div class="bull_head">
|
||||
<div>
|
||||
<div class="titre_parcours">Jury BUT</div>
|
||||
<div class="nom_etud">{etud.html_link_fiche()}</div>
|
||||
</div>
|
||||
<div class="bull_photo"><a href="{
|
||||
etud.url_fiche()
|
||||
}">{etud.photo_html(title="fiche de " + etud.nomprenom)}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning">Impossible de statuer sur cet étudiant:
|
||||
il est démissionnaire ou défaillant (voir <a class="stdlink" href="{
|
||||
etud.url_fiche()}">sa fiche</a>)
|
||||
</div>
|
||||
</div>
|
||||
{navigation_div}
|
||||
</div>
|
||||
"""
|
||||
+ html_sco_header.sco_footer()
|
||||
)
|
||||
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
has_notes_en_attente = deca.has_notes_en_attente()
|
||||
evaluations_a_debloquer = Evaluation.get_evaluations_blocked_for_etud(
|
||||
formsemestre, etud
|
||||
)
|
||||
if has_notes_en_attente or evaluations_a_debloquer:
|
||||
read_only = True
|
||||
if request.method == "POST":
|
||||
if not read_only:
|
||||
deca.record_form(request.form)
|
||||
ScolarNews.add(
|
||||
typ=ScolarNews.NEWS_JURY,
|
||||
obj=formsemestre.id,
|
||||
text=f"""Saisie décision jury dans {formsemestre.html_link_status()}""",
|
||||
url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
),
|
||||
)
|
||||
flash("codes enregistrés")
|
||||
return flask.redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
)
|
||||
|
||||
warning = ""
|
||||
if len(deca.niveaux_competences) != len(deca.decisions_rcue_by_niveau):
|
||||
warning += f"""<div class="warning">Attention: {len(deca.niveaux_competences)}
|
||||
niveaux mais {len(deca.decisions_rcue_by_niveau)} regroupements RCUE.</div>"""
|
||||
if (deca.parcour is None) and len(formsemestre.parcours) > 0:
|
||||
warning += (
|
||||
"""<div class="warning">L'étudiant n'est pas inscrit à un parcours.</div>"""
|
||||
)
|
||||
if formsemestre.date_fin - datetime.date.today() > datetime.timedelta(days=12):
|
||||
# encore loin de la fin du semestre de départ de ce jury ?
|
||||
warning += f"""<div class="warning">Le semestre S{formsemestre.semestre_id}
|
||||
terminera le {formsemestre.date_fin.strftime(scu.DATE_FMT)} :
|
||||
êtes-vous certain de vouloir enregistrer une décision de jury ?
|
||||
</div>"""
|
||||
|
||||
if deca.formsemestre_impair:
|
||||
inscription = deca.formsemestre_impair.etuds_inscriptions.get(etud.id)
|
||||
if (not inscription) or inscription.etat != scu.INSCRIT:
|
||||
etat_ins = scu.ETATS_INSCRIPTION.get(inscription.etat, "inconnu?")
|
||||
warning += f"""<div class="warning">{etat_ins}
|
||||
en S{deca.formsemestre_impair.semestre_id}</div>"""
|
||||
|
||||
if deca.formsemestre_pair:
|
||||
inscription = deca.formsemestre_pair.etuds_inscriptions.get(etud.id)
|
||||
if (not inscription) or inscription.etat != scu.INSCRIT:
|
||||
etat_ins = scu.ETATS_INSCRIPTION.get(inscription.etat, "inconnu?")
|
||||
warning += f"""<div class="warning">{etat_ins}
|
||||
en S{deca.formsemestre_pair.semestre_id}</div>"""
|
||||
|
||||
if has_notes_en_attente:
|
||||
warning += f"""<div class="warning-bloquant">{etud.html_link_fiche()
|
||||
} a des notes en ATTente dans les modules suivants.
|
||||
Vous devez régler cela avant de statuer en jury !
|
||||
<ul class="modimpls_att">
|
||||
"""
|
||||
for modimpl in deca.get_modimpls_attente():
|
||||
warning += f"""<li><a href="{
|
||||
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, moduleimpl_id=modimpl.id)
|
||||
}" class="stdlink">{modimpl.module.code} {modimpl.module.titre_str()}</a></li>"""
|
||||
warning += "</ul></div>"
|
||||
if evaluations_a_debloquer:
|
||||
links_evals = [
|
||||
f"""<a class="stdlink" href="{url_for(
|
||||
'notes.evaluation_listenotes', scodoc_dept=g.scodoc_dept, evaluation_id=e.id
|
||||
)}">{e.description} en {e.moduleimpl.module.code}</a>"""
|
||||
for e in evaluations_a_debloquer
|
||||
]
|
||||
warning += f"""<div class="warning-bloquant">Impossible de statuer sur cet étudiant:
|
||||
il a des notes dans des évaluations qui seront débloquées plus tard:
|
||||
voir {", ".join(links_evals)}
|
||||
"""
|
||||
|
||||
if warning:
|
||||
warning = f"""<div class="jury_but_warning jury_but_box">{warning}</div>"""
|
||||
H.append(
|
||||
f"""
|
||||
<div>
|
||||
<div class="bull_head">
|
||||
<div>
|
||||
<div class="titre_parcours">Jury BUT{deca.annee_but}
|
||||
- Parcours {(deca.parcour.libelle if deca.parcour else False) or "non spécifié"}
|
||||
- {deca.annee_scolaire_str()}</div>
|
||||
<div class="nom_etud">{etud.html_link_fiche()}</div>
|
||||
</div>
|
||||
<div class="bull_photo"><a href="{
|
||||
etud.url_fiche()}">{etud.photo_html(title="fiche de " + etud.nomprenom)}</a>
|
||||
</div>
|
||||
</div>
|
||||
{warning}
|
||||
</div>
|
||||
|
||||
<form method="post" class="jury_but_box" id="jury_but">
|
||||
"""
|
||||
)
|
||||
|
||||
H.append(jury_but_view.show_etud(deca, read_only=read_only))
|
||||
|
||||
autorisations_idx = deca.get_autorisations_passage()
|
||||
div_autorisations_passage = (
|
||||
f"""
|
||||
<div class="but_autorisations_passage">
|
||||
<span>Autorisé à passer en :</span>
|
||||
{ ", ".join( ["S" + str(i) for i in autorisations_idx ] )}
|
||||
</div>
|
||||
"""
|
||||
if autorisations_idx
|
||||
else """<div class="but_autorisations_passage but_explanation">
|
||||
pas d'autorisations de passage enregistrées.
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
H.append(div_autorisations_passage)
|
||||
|
||||
if read_only:
|
||||
H.append(
|
||||
f"""
|
||||
<div class="but_explanation">
|
||||
{"Vous n'avez pas la permission de modifier ces décisions."
|
||||
if formsemestre.etat
|
||||
else "Semestre verrouillé."}
|
||||
Les champs entourés en vert sont enregistrés.
|
||||
</div>"""
|
||||
)
|
||||
else:
|
||||
erase_span = f"""
|
||||
<a style="margin-left: 16px;" class="stdlink {'' if can_erase else 'link_unauthorized'}"
|
||||
title="{'' if can_erase else 'réservé au responsable'}"
|
||||
href="{
|
||||
url_for("notes.erase_decisions_annee_formation",
|
||||
scodoc_dept=g.scodoc_dept, formation_id=deca.formsemestre.formation.id,
|
||||
etudid=deca.etud.id, annee=deca.annee_but, formsemestre_id=formsemestre_id)
|
||||
if can_erase else ''
|
||||
}"
|
||||
>effacer des décisions de jury</a>
|
||||
|
||||
<a style="margin-left: 16px;" class="stdlink"
|
||||
href="{
|
||||
url_for("notes.formsemestre_validate_previous_ue",
|
||||
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">
|
||||
<input type="checkbox" onchange="enable_manual_codes(this)">
|
||||
<em>permettre la saisie manuelles des codes
|
||||
{"d'année et " if deca.jury_annuel else ""}
|
||||
de niveaux.
|
||||
Dans ce cas, assurez-vous de la cohérence entre les codes d'UE/RCUE/Année !
|
||||
</em>
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="but_buttons">
|
||||
<span><input type="submit" value="Enregistrer ces décisions"></span>
|
||||
<span>{erase_span}</span>
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
H.append(navigation_div)
|
||||
H.append("</form>")
|
||||
|
||||
# Affichage cursus BUT
|
||||
but_cursus = cursus_but.EtudCursusBUT(etud, deca.formsemestre.formation)
|
||||
H += [
|
||||
"""<div class="jury_but_box">
|
||||
<div class="jury_but_box_title"><b>Niveaux de compétences enregistrés :</b></div>
|
||||
""",
|
||||
render_template(
|
||||
"but/cursus_etud.j2",
|
||||
cursus=but_cursus,
|
||||
scu=scu,
|
||||
),
|
||||
"</div>",
|
||||
]
|
||||
H.append(
|
||||
render_template(
|
||||
"but/documentation_codes_jury.j2",
|
||||
nom_univ=f"""Export {sco_preferences.get_preference("InstituteName")
|
||||
or sco_preferences.get_preference("UnivName")
|
||||
or "Apogée"}""",
|
||||
codes=ScoDocSiteConfig.get_codes_apo_dict(),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
f"""<div class="but_doc_codes but_warning_rcue_cap">
|
||||
{scu.EMO_WARNING} Rappel: pour les redoublants, seules les UE <b>capitalisées</b> (note > 10)
|
||||
lors d'une année précédente peuvent être prise en compte pour former
|
||||
un RCUE (associé à un niveau de compétence du BUT).
|
||||
</div>
|
||||
"""
|
||||
)
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_validation_auto_but/<int:formsemestre_id>", methods=["GET", "POST"]
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validation_auto_but(formsemestre_id: int = None):
|
||||
"Saisie automatique des décisions de jury BUT"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
if not formsemestre.formation.is_apc():
|
||||
raise ScoValueError(
|
||||
"formsemestre_validation_auto_but est réservé aux formations APC"
|
||||
)
|
||||
|
||||
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
|
||||
form = jury_but_forms.FormSemestreValidationAutoBUTForm()
|
||||
if request.method == "POST":
|
||||
if not form.cancel.data:
|
||||
nb_etud_modif, _ = (
|
||||
jury_but_validation_auto.formsemestre_validation_auto_but(formsemestre)
|
||||
)
|
||||
flash(f"Décisions enregistrées ({nb_etud_modif} étudiants modifiés)")
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
)
|
||||
)
|
||||
# Avertissement si formsemestre impair
|
||||
formsemestres_suspects = {}
|
||||
if formsemestre.semestre_id % 2:
|
||||
_, decas = jury_but_validation_auto.formsemestre_validation_auto_but(
|
||||
formsemestre, dry_run=True
|
||||
)
|
||||
# regarde si il y a des semestres pairs postérieurs qui ne soient pas bloqués
|
||||
formsemestres_suspects = {
|
||||
deca.formsemestre_pair.id: deca.formsemestre_pair
|
||||
for deca in decas
|
||||
if deca.formsemestre_pair
|
||||
and deca.formsemestre_pair.date_debut > formsemestre.date_debut
|
||||
and not deca.formsemestre_pair.block_moyennes
|
||||
}
|
||||
|
||||
return render_template(
|
||||
"but/formsemestre_validation_auto_but.j2",
|
||||
form=form,
|
||||
formsemestres_suspects=formsemestres_suspects,
|
||||
sco=ScoData(formsemestre=formsemestre),
|
||||
title="Calcul automatique jury BUT",
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_validate_previous_ue/<int:formsemestre_id>/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
|
||||
"Form. saisie UE validée hors ScoDoc"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
etud: Identite = (
|
||||
Identite.query.filter_by(id=etudid)
|
||||
.join(FormSemestreInscription)
|
||||
.filter_by(formsemestre_id=formsemestre_id)
|
||||
.first_or_404()
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.formsemestre_validate_previous_ue(
|
||||
formsemestre, etud
|
||||
)
|
||||
|
||||
|
||||
sco_publish(
|
||||
"/formsemestre_ext_create_form",
|
||||
sco_formsemestre_exterieurs.formsemestre_ext_create_form,
|
||||
@ -2727,150 +2196,6 @@ sco_publish(
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_ext_edit_ue_validations", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid=None):
|
||||
"Form. edition UE semestre extérieur"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
|
||||
formsemestre_id, etudid
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_auto")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_auto(formsemestre_id):
|
||||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
if formsemestre.formation.is_apc():
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_validation_auto_but",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre.id,
|
||||
)
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validation_auto(formsemestre_id)
|
||||
|
||||
|
||||
@bp.route("/do_formsemestre_validation_auto")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def do_formsemestre_validation_auto(formsemestre_id):
|
||||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
|
||||
return sco_formsemestre_validation.do_formsemestre_validation_auto(formsemestre_id)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_validation_suppress_etud", methods=["GET", "POST"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_validation_suppress_etud(
|
||||
formsemestre_id, etudid, dialog_confirmed=False
|
||||
):
|
||||
"""Suppression des décisions de jury pour un étudiant."""
|
||||
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
etud = Identite.get_etud(etudid)
|
||||
if formsemestre.formation.is_apc():
|
||||
next_url = url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etudid,
|
||||
)
|
||||
else:
|
||||
next_url = url_for(
|
||||
"notes.formsemestre_validation_etud_form",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
if not dialog_confirmed:
|
||||
d = sco_bulletins_json.dict_decision_jury(
|
||||
etud, formsemestre, with_decisions=True
|
||||
)
|
||||
|
||||
descr_ues = [f"{u['acronyme']}: {u['code']}" for u in d.get("decision_ue", [])]
|
||||
dec_annee = d.get("decision_annee")
|
||||
if dec_annee:
|
||||
descr_annee = dec_annee.get("code", "-")
|
||||
else:
|
||||
descr_annee = "-"
|
||||
|
||||
existing = f"""
|
||||
<ul>
|
||||
<li>Semestre : {d.get("decision", {"code":"-"})['code'] or "-"}</li>
|
||||
<li>Année BUT: {descr_annee}</li>
|
||||
<li>UEs : {", ".join(descr_ues)}</li>
|
||||
<li>RCUEs: {len(d.get("decision_rcue", []))} décisions</li>
|
||||
<li>Autorisations: {descr_autorisations(ScolarAutorisationInscription.query.filter_by(origin_formsemestre_id=formsemestre_id,
|
||||
etudid=etudid))}
|
||||
</ul>
|
||||
"""
|
||||
return scu.confirm_dialog(
|
||||
f"""<h2>Confirmer la suppression des décisions du semestre
|
||||
{formsemestre.titre_mois()} pour {etud.nomprenom}
|
||||
</h2>
|
||||
<p>Cette opération est irréversible.</p>
|
||||
<div>
|
||||
{existing}
|
||||
</div>
|
||||
""",
|
||||
OK="Supprimer",
|
||||
dest_url="",
|
||||
cancel_url=next_url,
|
||||
parameters={"etudid": etudid, "formsemestre_id": formsemestre_id},
|
||||
)
|
||||
|
||||
sco_formsemestre_validation.formsemestre_validation_suppress_etud(
|
||||
formsemestre_id, etudid
|
||||
)
|
||||
flash("Décisions supprimées")
|
||||
return flask.redirect(next_url)
|
||||
|
||||
|
||||
# ------------- PV de JURY et archives
|
||||
sco_publish(
|
||||
"/formsemestre_pvjury", sco_pv_forms.formsemestre_pvjury, Permission.ScoView
|
||||
@ -2878,192 +2203,6 @@ sco_publish(
|
||||
|
||||
sco_publish("/pvjury_page_but", jury_but_pv.pvjury_page_but, Permission.ScoView)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_saisie_jury")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_saisie_jury(formsemestre_id: int, selected_etudid: int = None):
|
||||
"""Page de saisie: liste des étudiants et lien vers page jury
|
||||
sinon, redirect vers page recap en mode jury
|
||||
"""
|
||||
return redirect(
|
||||
url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/formsemestre_jury_erase/<int:formsemestre_id>",
|
||||
methods=["GET", "POST"],
|
||||
defaults={"etudid": None},
|
||||
)
|
||||
@bp.route(
|
||||
"/formsemestre_jury_erase/<int:formsemestre_id>/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
def formsemestre_jury_erase(formsemestre_id: int, etudid: int = None):
|
||||
"""Supprime toutes les décisions de jury (classique ou BUT) pour cette année.
|
||||
Si l'étudiant n'est pas spécifié, efface les décisions de tous les inscrits.
|
||||
En BUT, si only_one_sem n'efface que pour le formsemestre indiqué, pas les deux de l'année.
|
||||
En classique, n'affecte que les décisions issues de ce formsemestre.
|
||||
"""
|
||||
only_one_sem = int(request.args.get("only_one_sem") or False)
|
||||
formsemestre: FormSemestre = FormSemestre.query.filter_by(
|
||||
id=formsemestre_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if not formsemestre.can_edit_jury():
|
||||
raise ScoPermissionDenied(
|
||||
dest_url=url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
)
|
||||
)
|
||||
is_apc = formsemestre.formation.is_apc()
|
||||
if etudid is None:
|
||||
etud = None
|
||||
etuds = formsemestre.get_inscrits(include_demdef=True)
|
||||
dest_url = url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
mode_jury=1,
|
||||
)
|
||||
else:
|
||||
etud = Identite.get_etud(etudid)
|
||||
etuds = [etud]
|
||||
endpoint = (
|
||||
"notes.formsemestre_validation_but"
|
||||
if is_apc
|
||||
else "notes.formsemestre_validation_etud_form"
|
||||
)
|
||||
dest_url = url_for(
|
||||
endpoint,
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
)
|
||||
if request.method == "POST":
|
||||
with sco_cache.DeferredSemCacheManager():
|
||||
for etud in etuds:
|
||||
if is_apc:
|
||||
deca = jury_but.DecisionsProposeesAnnee(etud, formsemestre)
|
||||
deca.erase(only_one_sem=only_one_sem)
|
||||
else:
|
||||
sco_formsemestre_validation.formsemestre_validation_suppress_etud(
|
||||
formsemestre.id, etud.id
|
||||
)
|
||||
log(f"formsemestre_jury_erase({formsemestre_id}, {etud.id})")
|
||||
flash(
|
||||
(
|
||||
"décisions de jury du semestre effacées"
|
||||
if (only_one_sem or is_apc)
|
||||
else "décisions de jury des semestres de l'année BUT effacées"
|
||||
)
|
||||
+ f" pour {len(etuds)} étudiant{'s' if len(etuds) > 1 else ''}"
|
||||
)
|
||||
return redirect(dest_url)
|
||||
|
||||
return render_template(
|
||||
"confirm_dialog.j2",
|
||||
title=f"""Effacer les validations de jury {
|
||||
("de " + etud.nomprenom)
|
||||
if etud
|
||||
else ("des " + str(len(etuds)) + " étudiants inscrits dans ce semestre")
|
||||
} ?""",
|
||||
explanation=(
|
||||
(
|
||||
f"""Les validations d'UE et autorisations de passage
|
||||
du semestre S{formsemestre.semestre_id} seront effacées."""
|
||||
if (only_one_sem or is_apc)
|
||||
else """Les validations de toutes les UE, RCUE (compétences) et année
|
||||
issues de cette année scolaire seront effacées.
|
||||
"""
|
||||
)
|
||||
+ """
|
||||
<p>Les décisions des années scolaires précédentes ne seront pas modifiées.</p>
|
||||
"""
|
||||
+ """
|
||||
<p>Efface aussi toutes les validations concernant l'année BUT de ce semestre,
|
||||
même si elles ont été acquises ailleurs, ainsi que les validations de DUT en 120 ECTS
|
||||
obtenues après BUT1/BUT2.
|
||||
</p>
|
||||
"""
|
||||
if is_apc
|
||||
else ""
|
||||
+ """
|
||||
<div class="warning">Cette opération est irréversible !
|
||||
A n'utiliser que dans des cas exceptionnels, vérifiez bien tous les étudiants ensuite.
|
||||
</div>
|
||||
"""
|
||||
),
|
||||
cancel_url=dest_url,
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/erase_decisions_annee_formation/<int:etudid>/<int:formation_id>/<int:annee>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
def erase_decisions_annee_formation(etudid: int, formation_id: int, annee: int):
|
||||
"""Efface toute les décisions d'une année pour cet étudiant"""
|
||||
etud: Identite = Identite.query.get_or_404(etudid)
|
||||
formation: Formation = Formation.query.filter_by(
|
||||
id=formation_id, dept_id=g.scodoc_dept_id
|
||||
).first_or_404()
|
||||
if request.method == "POST":
|
||||
jury.erase_decisions_annee_formation(etud, formation, annee, delete=True)
|
||||
flash("Décisions de jury effacées")
|
||||
return redirect(
|
||||
url_for(
|
||||
"scolar.fiche_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etud.id,
|
||||
)
|
||||
)
|
||||
validations = jury.erase_decisions_annee_formation(etud, formation, annee)
|
||||
formsemestre_origine_id = request.args.get("formsemestre_id")
|
||||
formsemestre_origine = (
|
||||
FormSemestre.query.get_or_404(formsemestre_origine_id)
|
||||
if formsemestre_origine_id
|
||||
else None
|
||||
)
|
||||
return render_template(
|
||||
"jury/erase_decisions_annee_formation.j2",
|
||||
annee=annee,
|
||||
cancel_url=url_for(
|
||||
"scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||
),
|
||||
etud=etud,
|
||||
formation=formation,
|
||||
formsemestre_origine=formsemestre_origine,
|
||||
validations=validations,
|
||||
sco=ScoData(),
|
||||
title=f"Effacer décisions de jury {etud.nom} - année {annee}",
|
||||
)
|
||||
|
||||
|
||||
@bp.route(
|
||||
"/jury_delete_manual/<int:etudid>",
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
@scodoc
|
||||
@permission_required(Permission.EtudInscrit)
|
||||
def jury_delete_manual(etudid: int):
|
||||
"""Efface toute les décisions d'une année pour cet étudiant"""
|
||||
etud: Identite = Identite.query.get_or_404(etudid)
|
||||
return jury_edit_manual.jury_delete_manual(etud)
|
||||
|
||||
|
||||
sco_publish(
|
||||
"/formsemestre_lettres_individuelles",
|
||||
sco_pv_forms.formsemestre_lettres_individuelles,
|
||||
@ -3306,12 +2445,12 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
if not bad_ue and not bad_sem:
|
||||
H.append("<p>Aucun problème à signaler !</p>")
|
||||
else:
|
||||
log("check_sem_integrity: problem detected: formations_set=%s" % formations_set)
|
||||
log(f"check_sem_integrity: problem detected: formations_set={formations_set}")
|
||||
if sem["formation_id"] in formations_set:
|
||||
formations_set.remove(sem["formation_id"])
|
||||
if len(formations_set) == 1:
|
||||
if fix:
|
||||
log("check_sem_integrity: trying to fix %s" % formsemestre_id)
|
||||
log(f"check_sem_integrity: trying to fix {formsemestre_id}")
|
||||
formation_id = formations_set.pop()
|
||||
if sem["formation_id"] != formation_id:
|
||||
sem["formation_id"] = formation_id
|
||||
@ -3319,11 +2458,11 @@ def check_sem_integrity(formsemestre_id, fix=False):
|
||||
H.append("""<p class="alert">Problème réparé: vérifiez</p>""")
|
||||
else:
|
||||
H.append(
|
||||
"""
|
||||
f"""
|
||||
<p class="alert">Problème détecté réparable:
|
||||
<a href="check_sem_integrity?formsemestre_id=%s&fix=1">réparer maintenant</a></p>
|
||||
<a href="check_sem_integrity?formsemestre_id={
|
||||
formsemestre_id}&fix=1">réparer maintenant</a></p>
|
||||
"""
|
||||
% (formsemestre_id,)
|
||||
)
|
||||
else:
|
||||
H.append("""<p class="alert">Problème détecté !</p>""")
|
||||
|
@ -1,99 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""Exemple connexion sur ScoDoc et utilisation de l'API
|
||||
|
||||
Attention: cet exemple est en Python 2.
|
||||
Voir example-api-1.py pour une version en Python3 plus moderne.
|
||||
"""
|
||||
|
||||
import urllib, urllib2
|
||||
|
||||
# A modifier pour votre serveur:
|
||||
BASEURL = "https://scodoc.xxx.net/ScoDoc/RT/Scolarite"
|
||||
USER = "XXX"
|
||||
PASSWORD = "XXX"
|
||||
|
||||
values = {
|
||||
"__ac_name": USER,
|
||||
"__ac_password": PASSWORD,
|
||||
}
|
||||
|
||||
# Configure memorisation des cookies:
|
||||
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
|
||||
urllib2.install_opener(opener)
|
||||
|
||||
data = urllib.urlencode(values)
|
||||
|
||||
req = urllib2.Request(BASEURL, data) # this is a POST http request
|
||||
response = urllib2.urlopen(req)
|
||||
|
||||
# --- Use API
|
||||
|
||||
# Affiche la liste des formations en format XML
|
||||
req = urllib2.Request(BASEURL + "/Notes/formation_list?fmt=xml")
|
||||
response = urllib2.urlopen(req)
|
||||
print response.read()[:100] # limite aux 100 premiers caracteres...
|
||||
|
||||
# Recupere la liste de tous les semestres:
|
||||
req = urllib2.Request(BASEURL + "/Notes/formsemestre_list?fmt=json") # format json
|
||||
response = urllib2.urlopen(req)
|
||||
js_data = response.read()
|
||||
|
||||
# Plus amusant: va retrouver le bulletin de notes du premier etudiant (au hasard donc) du premier semestre (au hasard aussi)
|
||||
try:
|
||||
import json # Attention: ceci demande Python >= 2.6
|
||||
except:
|
||||
import simplejson as json # python2.4 with simplejson installed
|
||||
|
||||
data = json.loads(js_data) # decode la reponse JSON
|
||||
if not data:
|
||||
print "Aucun semestre !"
|
||||
else:
|
||||
formsemestre_id = str(data[0]["formsemestre_id"])
|
||||
# Obtient la liste des groupes:
|
||||
req = urllib2.Request(
|
||||
BASEURL
|
||||
+ "/Notes/formsemestre_partition_list?fmt=json&formsemestre_id="
|
||||
+ str(formsemestre_id)
|
||||
) # format json
|
||||
response = urllib2.urlopen(req)
|
||||
js_data = response.read()
|
||||
data = json.loads(js_data)
|
||||
group_id = data[0]["group"][0][
|
||||
"group_id"
|
||||
] # premier groupe (normalement existe toujours)
|
||||
# Liste les étudiants de ce groupe:
|
||||
req = urllib2.Request(
|
||||
BASEURL + "/Notes/group_list?fmt=json&with_codes=1&group_id=" + str(group_id)
|
||||
) # format json
|
||||
response = urllib2.urlopen(req)
|
||||
js_data = response.read()
|
||||
data = json.loads(js_data)
|
||||
# Le code du premier étudiant:
|
||||
if not data:
|
||||
print ("pas d'etudiants dans ce semestre !")
|
||||
else:
|
||||
etudid = data[0]["etudid"]
|
||||
# Récupère bulletin de notes:
|
||||
req = urllib2.Request(
|
||||
BASEURL
|
||||
+ "/Notes/formsemestre_bulletinetud?formsemestre_id="
|
||||
+ str(formsemestre_id)
|
||||
+ "&etudid="
|
||||
+ str(etudid)
|
||||
+ "&fmt=xml"
|
||||
) # format XML ici !
|
||||
response = urllib2.urlopen(req)
|
||||
xml_bulletin = response.read()
|
||||
print "----- Bulletin de notes en XML:"
|
||||
print xml_bulletin
|
||||
# Récupère la moyenne générale:
|
||||
import xml.dom.minidom
|
||||
|
||||
doc = xml.dom.minidom.parseString(xml_bulletin)
|
||||
moy = doc.getElementsByTagName("note")[0].getAttribute(
|
||||
"value"
|
||||
) # une chaine unicode
|
||||
print "\nMoyenne generale: ", moy
|
Loading…
Reference in New Issue
Block a user