From d4fd6527e54dab722cacfa77400c4cac70eef553 Mon Sep 17 00:00:00 2001
From: Emmanuel Viennet <emmanuel.viennet@gmail.com>
Date: Tue, 9 Jul 2024 13:37:22 +0200
Subject: [PATCH] =?UTF-8?q?S=C3=A9paration=20vues=20notes/jurys=20et=20val?=
=?UTF-8?q?idations?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/but/validations_view.py | 5 +-
app/scodoc/sco_formation_recap.py | 15 +-
app/views/__init__.py | 5 +
app/views/jury_validations.py | 904 ++++++++++++++++++++++++++++++
app/views/notes.py | 897 +----------------------------
misc/example-api-python2.py | 99 ----
6 files changed, 935 insertions(+), 990 deletions(-)
create mode 100644 app/views/jury_validations.py
delete mode 100644 misc/example-api-python2.py
diff --git a/app/but/validations_view.py b/app/but/validations_view.py
index 83c843015..06145a194 100644
--- a/app/but/validations_view.py
+++ b/app/but/validations_view.py
@@ -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
diff --git a/app/scodoc/sco_formation_recap.py b/app/scodoc/sco_formation_recap.py
index f0074e4eb..331e641cb 100644
--- a/app/scodoc/sco_formation_recap.py
+++ b/app/scodoc/sco_formation_recap.py
@@ -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
)
diff --git a/app/views/__init__.py b/app/views/__init__.py
index df78e7cfa..152a725c2 100644
--- a/app/views/__init__.py
+++ b/app/views/__init__.py
@@ -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,
diff --git a/app/views/jury_validations.py b/app/views/jury_validations.py
new file mode 100644
index 000000000..ccef89465
--- /dev/null
+++ b/app/views/jury_validations.py
@@ -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)
diff --git a/app/views/notes.py b/app/views/notes.py
index 482b6c48f..806a607dd 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -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>""")
diff --git a/misc/example-api-python2.py b/misc/example-api-python2.py
deleted file mode 100644
index 03fd26beb..000000000
--- a/misc/example-api-python2.py
+++ /dev/null
@@ -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
--
GitLab