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
|
# See LICENSE
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
"""Jury édition manuelle des décisions (correction d'erreurs, parcours hors normes)
|
"""Jury édition manuelle des décisions RCUE antérieures
|
||||||
|
|
||||||
Non spécifique au BUT.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
from app.but import cursus_but
|
from app.but import cursus_but
|
||||||
|
@ -45,16 +45,15 @@ import app.scodoc.sco_utils as scu
|
|||||||
|
|
||||||
|
|
||||||
# ---- Table recap formation
|
# ---- Table recap formation
|
||||||
def formation_table_recap(formation_id, fmt="html") -> Response:
|
def formation_table_recap(formation: Formation, fmt="html") -> Response:
|
||||||
"""Table recapitulant formation."""
|
"""Table recapitulant formation."""
|
||||||
T = []
|
rows = []
|
||||||
formation = Formation.query.get_or_404(formation_id)
|
|
||||||
ues = formation.ues.order_by(UniteEns.semestre_idx, UniteEns.numero)
|
ues = formation.ues.order_by(UniteEns.semestre_idx, UniteEns.numero)
|
||||||
can_edit = current_user.has_permission(Permission.EditFormation)
|
can_edit = current_user.has_permission(Permission.EditFormation)
|
||||||
li = 0
|
li = 0
|
||||||
for ue in ues:
|
for ue in ues:
|
||||||
# L'UE
|
# L'UE
|
||||||
T.append(
|
rows.append(
|
||||||
{
|
{
|
||||||
"sem": f"S{ue.semestre_idx}" if ue.semestre_idx is not None else "-",
|
"sem": f"S{ue.semestre_idx}" if ue.semestre_idx is not None else "-",
|
||||||
"_sem_order": f"{li:04d}",
|
"_sem_order": f"{li:04d}",
|
||||||
@ -83,7 +82,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
|||||||
for mod in modules:
|
for mod in modules:
|
||||||
nb_moduleimpls = mod.modimpls.count()
|
nb_moduleimpls = mod.modimpls.count()
|
||||||
# le module (ou ressource ou sae)
|
# le module (ou ressource ou sae)
|
||||||
T.append(
|
rows.append(
|
||||||
{
|
{
|
||||||
"sem": (
|
"sem": (
|
||||||
f"S{mod.semestre_id}"
|
f"S{mod.semestre_id}"
|
||||||
@ -152,7 +151,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
|||||||
|
|
||||||
tab = GenTable(
|
tab = GenTable(
|
||||||
columns_ids=columns_ids,
|
columns_ids=columns_ids,
|
||||||
rows=T,
|
rows=rows,
|
||||||
titles=titles,
|
titles=titles,
|
||||||
origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
|
origin=f"Généré par {scu.sco_version.SCONAME} le {scu.timedate_human_repr()}",
|
||||||
caption=title,
|
caption=title,
|
||||||
@ -168,7 +167,7 @@ def formation_table_recap(formation_id, fmt="html") -> Response:
|
|||||||
}"
|
}"
|
||||||
""",
|
""",
|
||||||
html_with_td_classes=True,
|
html_with_td_classes=True,
|
||||||
base_url=f"{request.base_url}?formation_id={formation_id}",
|
base_url=f"{request.base_url}",
|
||||||
page_title=title,
|
page_title=title,
|
||||||
html_title=f"<h2>{title}</h2>",
|
html_title=f"<h2>{title}</h2>",
|
||||||
pdf_title=title,
|
pdf_title=title,
|
||||||
@ -192,7 +191,7 @@ def export_recap_formations_annee_scolaire(annee_scolaire):
|
|||||||
formation_ids = {formsemestre.formation.id for formsemestre in formsemestres}
|
formation_ids = {formsemestre.formation.id for formsemestre in formsemestres}
|
||||||
for formation_id in formation_ids:
|
for formation_id in formation_ids:
|
||||||
formation = db.session.get(Formation, formation_id)
|
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 = (
|
filename = (
|
||||||
scu.sanitize_filename(formation.get_titre_version()) + scu.XLSX_SUFFIX
|
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)
|
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 (
|
from app.views import (
|
||||||
absences,
|
absences,
|
||||||
assiduites,
|
assiduites,
|
||||||
but_formation,
|
but_formation,
|
||||||
|
jury_validations,
|
||||||
notes_formsemestre,
|
notes_formsemestre,
|
||||||
notes,
|
notes,
|
||||||
pn_modules,
|
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
|
Emmanuel Viennet, 2021
|
||||||
"""
|
"""
|
||||||
import datetime
|
|
||||||
import html
|
import html
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import time
|
import time
|
||||||
@ -40,24 +39,13 @@ from flask import flash, redirect, render_template, url_for
|
|||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import db
|
from app import db, log, send_scodoc_alarm
|
||||||
from app import models
|
from app import models
|
||||||
from app.auth.models import User
|
from app.auth.models import User
|
||||||
from app.but import (
|
from app.but import apc_edit_ue, jury_but_pv
|
||||||
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.comp import jury, res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_compat import NotesTableCompat
|
from app.comp.res_compat import NotesTableCompat
|
||||||
from app.models import (
|
from app.models import (
|
||||||
ApcNiveau,
|
ApcNiveau,
|
||||||
@ -72,10 +60,7 @@ from app.models import (
|
|||||||
Identite,
|
Identite,
|
||||||
Module,
|
Module,
|
||||||
ModuleImpl,
|
ModuleImpl,
|
||||||
ScolarAutorisationInscription,
|
|
||||||
ScolarNews,
|
|
||||||
Scolog,
|
Scolog,
|
||||||
ScoDocSiteConfig,
|
|
||||||
UniteEns,
|
UniteEns,
|
||||||
)
|
)
|
||||||
from app.scodoc.sco_exceptions import ScoFormationConflict, ScoPermissionDenied
|
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_utils as scu
|
||||||
from app.scodoc import sco_bulletins_json, sco_utils as scu
|
|
||||||
from app import log, send_scodoc_alarm
|
|
||||||
|
|
||||||
from app.scodoc.sco_exceptions import (
|
from app.scodoc.sco_exceptions import (
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
@ -131,7 +114,6 @@ from app.scodoc import (
|
|||||||
sco_formsemestre_exterieurs,
|
sco_formsemestre_exterieurs,
|
||||||
sco_formsemestre_inscriptions,
|
sco_formsemestre_inscriptions,
|
||||||
sco_formsemestre_status,
|
sco_formsemestre_status,
|
||||||
sco_formsemestre_validation,
|
|
||||||
sco_groups_view,
|
sco_groups_view,
|
||||||
sco_inscr_passage,
|
sco_inscr_passage,
|
||||||
sco_liste_notes,
|
sco_liste_notes,
|
||||||
@ -157,7 +139,6 @@ from app.scodoc import (
|
|||||||
sco_users,
|
sco_users,
|
||||||
)
|
)
|
||||||
from app.scodoc.gen_tables import GenTable
|
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.sco_permissions import Permission
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
from app.views import ScoData
|
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
|
@scodoc
|
||||||
@permission_required(Permission.ScoView)
|
@permission_required(Permission.ScoView)
|
||||||
@scodoc7func
|
def formation_table_recap(formation_id: int):
|
||||||
def formation_table_recap(formation_id, fmt="html"):
|
"Tableau récap. de la formation"
|
||||||
return sco_formation_recap.formation_table_recap(formation_id, fmt=fmt)
|
formation = Formation.get_formation(formation_id)
|
||||||
|
fmt = request.args.get("fmt", "html")
|
||||||
|
return sco_formation_recap.formation_table_recap(formation, fmt=fmt)
|
||||||
|
|
||||||
|
|
||||||
sco_publish(
|
sco_publish(
|
||||||
@ -829,9 +812,9 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.EditFormation)
|
|||||||
@permission_required(Permission.EditFormation)
|
@permission_required(Permission.EditFormation)
|
||||||
def ue_clone():
|
def ue_clone():
|
||||||
"""Clone existing UE"""
|
"""Clone existing UE"""
|
||||||
ue_id = int(request.form.get("ue_id"))
|
ue_id = request.form.get("ue_id")
|
||||||
ue = UniteEns.query.get_or_404(ue_id)
|
ue = UniteEns.get_ue(ue_id)
|
||||||
ue2 = ue.clone()
|
_ = ue.clone()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash(f"UE {ue.acronyme} dupliquée")
|
flash(f"UE {ue.acronyme} dupliquée")
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
@ -2205,520 +2188,6 @@ def appreciation_add_form(
|
|||||||
return flask.redirect(bul_url)
|
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(
|
sco_publish(
|
||||||
"/formsemestre_ext_create_form",
|
"/formsemestre_ext_create_form",
|
||||||
sco_formsemestre_exterieurs.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
|
# ------------- PV de JURY et archives
|
||||||
sco_publish(
|
sco_publish(
|
||||||
"/formsemestre_pvjury", sco_pv_forms.formsemestre_pvjury, Permission.ScoView
|
"/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)
|
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(
|
sco_publish(
|
||||||
"/formsemestre_lettres_individuelles",
|
"/formsemestre_lettres_individuelles",
|
||||||
sco_pv_forms.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:
|
if not bad_ue and not bad_sem:
|
||||||
H.append("<p>Aucun problème à signaler !</p>")
|
H.append("<p>Aucun problème à signaler !</p>")
|
||||||
else:
|
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:
|
if sem["formation_id"] in formations_set:
|
||||||
formations_set.remove(sem["formation_id"])
|
formations_set.remove(sem["formation_id"])
|
||||||
if len(formations_set) == 1:
|
if len(formations_set) == 1:
|
||||||
if fix:
|
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()
|
formation_id = formations_set.pop()
|
||||||
if sem["formation_id"] != formation_id:
|
if sem["formation_id"] != formation_id:
|
||||||
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>""")
|
H.append("""<p class="alert">Problème réparé: vérifiez</p>""")
|
||||||
else:
|
else:
|
||||||
H.append(
|
H.append(
|
||||||
"""
|
f"""
|
||||||
<p class="alert">Problème détecté réparable:
|
<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:
|
else:
|
||||||
H.append("""<p class="alert">Problème détecté !</p>""")
|
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