diff --git a/app/but/jury_but.py b/app/but/jury_but.py index 913297cc..401f8993 100644 --- a/app/but/jury_but.py +++ b/app/but/jury_but.py @@ -206,6 +206,7 @@ class DecisionsProposeesAnnee(DecisionsProposees): ): super().__init__(etud=etud) self.formsemestre_id = formsemestre.id + "l'id du formsemestre utilisé pour construire ce deca" formsemestre_impair, formsemestre_pair = self.comp_formsemestres(formsemestre) assert ( (formsemestre_pair is None) @@ -461,22 +462,9 @@ class DecisionsProposeesAnnee(DecisionsProposees): if (formsemestre is None) or (not formsemestre.formation.is_apc()): ues = [] else: - formation: Formation = formsemestre.formation - # Parcour dans lequel l'étudiant est inscrit, et liste des UEs - if res.etuds_parcour_id[etudid] is None: - # pas de parcour: prend toutes les UEs (non bonus) - ues = [ue for ue in res.etud_ues(etudid) if ue.type == UE_STANDARD] - ues.sort(key=lambda u: u.numero) - else: - parcour = ApcParcours.query.get(res.etuds_parcour_id[etudid]) - if parcour is not None: - self.parcour = parcour - ues = ( - formation.query_ues_parcour(parcour) - .filter_by(semestre_idx=formsemestre.semestre_id) - .order_by(UniteEns.numero) - .all() - ) + parcour, ues = list_ue_parcour_etud(formsemestre, self.etud, res) + if parcour is not None: + self.parcour = parcour ues_sems.append(ues) return ues_sems @@ -689,30 +677,40 @@ class DecisionsProposeesAnnee(DecisionsProposees): # s'il n'y a pas de code, efface dec.record(code, no_overwrite=True) - def erase(self): + def erase(self, only_one_sem=False): """Efface les décisions de jury de cet étudiant pour cette année: décisions d'UE, de RCUE, d'année, et autorisations d'inscription émises. """ - for dec_ue in self.decisions_ues.values(): - dec_ue.erase() - for dec_rcue in self.decisions_rcue_by_niveau.values(): - dec_rcue.erase() - if self.formsemestre_impair: + if only_one_sem: + # N'efface que les autorisations venant de ce semestre, + # et les validations de ses UEs ScolarAutorisationInscription.delete_autorisation_etud( - self.etud.id, self.formsemestre_impair.id + self.etud.id, self.formsemestre_id ) - if self.formsemestre_pair: - ScolarAutorisationInscription.delete_autorisation_etud( - self.etud.id, self.formsemestre_pair.id + for dec_ue in self.decisions_ues.values(): + if dec_ue.formsemestre.id == self.formsemestre_id: + dec_ue.erase() + else: + for dec_ue in self.decisions_ues.values(): + dec_ue.erase() + for dec_rcue in self.decisions_rcue_by_niveau.values(): + dec_rcue.erase() + if self.formsemestre_impair: + ScolarAutorisationInscription.delete_autorisation_etud( + self.etud.id, self.formsemestre_impair.id + ) + if self.formsemestre_pair: + ScolarAutorisationInscription.delete_autorisation_etud( + self.etud.id, self.formsemestre_pair.id + ) + validations = ApcValidationAnnee.query.filter_by( + etudid=self.etud.id, + formsemestre_id=self.formsemestre_impair.id, + ordre=self.annee_but, ) - validations = ApcValidationAnnee.query.filter_by( - etudid=self.etud.id, - formsemestre_id=self.formsemestre_impair.id, - ordre=self.annee_but, - ) - for validation in validations: - db.session.delete(validation) + for validation in validations: + db.session.delete(validation) db.session.flush() self.invalidate_formsemestre_cache() @@ -757,6 +755,26 @@ class DecisionsProposeesAnnee(DecisionsProposees): return line_sep.join(validations) +def list_ue_parcour_etud( + formsemestre: FormSemestre, etud: Identite, res: ResultatsSemestreBUT +) -> tuple[ApcParcours, list[UniteEns]]: + """Parcour dans lequel l'étudiant est inscrit, et liste des UEs pour ce semestre""" + if res.etuds_parcour_id[etud.id] is None: + parcour = None + # pas de parcour: prend toutes les UEs (non bonus) + ues = [ue for ue in res.etud_ues(etud.id) if ue.type == UE_STANDARD] + ues.sort(key=lambda u: u.numero) + else: + parcour = ApcParcours.query.get(res.etuds_parcour_id[etud.id]) + ues = ( + formsemestre.formation.query_ues_parcour(parcour) + .filter_by(semestre_idx=formsemestre.semestre_id) + .order_by(UniteEns.numero) + .all() + ) + return parcour, ues + + class DecisionsProposeesRCUE(DecisionsProposees): """Liste des codes de décisions que l'on peut proposer pour le RCUE de cet étudiant dans cette année. @@ -995,8 +1013,8 @@ class DecisionsProposeesUE(DecisionsProposees): etudid=self.etud.id, msg=f"Validation UE {self.ue.id}", ) - log(f"DecisionsProposeesUE: recording {self.validation}") db.session.add(self.validation) + log(f"DecisionsProposeesUE: recording {self.validation}") sco_cache.invalidate_formsemestre(formsemestre_id=self.formsemestre.id) self.recorded = True diff --git a/app/but/jury_but_view.py b/app/but/jury_but_view.py index 6470723e..1aa72d1e 100644 --- a/app/but/jury_but_view.py +++ b/app/but/jury_but_view.py @@ -1,173 +1,364 @@ -############################################################################## -# ScoDoc -# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved. -# See LICENSE -############################################################################## - -"""Jury BUT: affichage/formulaire -""" -from flask import g, url_for -from app.models.etudiants import Identite - -from app.scodoc import sco_utils as scu -from app.but.jury_but import DecisionsProposeesAnnee, DecisionsProposeesUE -from app.models import FormSemestre, FormSemestreInscription, UniteEns -from app.scodoc.sco_exceptions import ScoValueError - - -def show_etud(deca: DecisionsProposeesAnnee, read_only: bool = True) -> str: - """Affichage des décisions annuelles BUT - Si pas read_only, menus sélection codes jury. - """ - H = [] - if deca.code_valide and not read_only: - erase_span = f"""effacer décisions""" - else: - erase_span = "" - - H.append( - f"""
OK, évaluation supprimée.
- """ - + html_sco_header.sco_footer() - ) - - -sco_publish( - "/do_evaluation_list", - sco_evaluation_db.do_evaluation_list, - Permission.ScoView, -) - - -@bp.route("/evaluation_edit", methods=["GET", "POST"]) -@scodoc -@permission_required(Permission.ScoEnsView) -@scodoc7func -def evaluation_edit(evaluation_id): - "form edit evaluation" - return sco_evaluation_edit.evaluation_create_form( - evaluation_id=evaluation_id, edit=True - ) - - -@bp.route("/evaluation_create", methods=["GET", "POST"]) -@scodoc -@permission_required(Permission.ScoEnsView) -@scodoc7func -def evaluation_create(moduleimpl_id): - "form create evaluation" - modimpl = ModuleImpl.query.get(moduleimpl_id) - if modimpl is None: - raise ScoValueError("Ce module n'existe pas ou plus !") - return sco_evaluation_edit.evaluation_create_form( - moduleimpl_id=moduleimpl_id, edit=False - ) - - -@bp.route("/evaluation_listenotes", methods=["GET", "POST"]) # API ScoDoc 7 compat -@scodoc -@permission_required_compat_scodoc7(Permission.ScoView) -@scodoc7func -def evaluation_listenotes(): - """Affichage des notes d'une évaluation""" - evaluation_id = None - moduleimpl_id = None - vals = scu.get_request_args() - try: - if "evaluation_id" in vals: - evaluation_id = int(vals["evaluation_id"]) - if "moduleimpl_id" in vals and vals["moduleimpl_id"]: - moduleimpl_id = int(vals["moduleimpl_id"]) - except ValueError as exc: - raise ScoValueError("evaluation_listenotes: id invalides !") from exc - - format = vals.get("format", "html") - html_content, page_title = sco_liste_notes.do_evaluation_listenotes( - evaluation_id=evaluation_id, moduleimpl_id=moduleimpl_id, format=format - ) - if format == "html": - H = html_sco_header.sco_header( - page_title=page_title, - cssstyles=["css/verticalhisto.css"], - javascripts=["js/etud_info.js"], - init_qtip=True, - ) - F = html_sco_header.sco_footer() - return H + html_content + F - else: - return html_content - - -sco_publish( - "/evaluation_list_operations", - sco_undo_notes.evaluation_list_operations, - Permission.ScoView, -) -sco_publish( - "/evaluation_check_absences_html", - sco_evaluation_check_abs.evaluation_check_absences_html, - Permission.ScoView, -) -sco_publish( - "/formsemestre_check_absences_html", - sco_evaluation_check_abs.formsemestre_check_absences_html, - Permission.ScoView, -) - -# --- Placement des étudiants pour l'évaluation -sco_publish( - "/placement_eval_selectetuds", - sco_placement.placement_eval_selectetuds, - Permission.ScoEnsView, - methods=["GET", "POST"], -) - -# --- Saisie des notes -sco_publish( - "/saisie_notes_tableur", - sco_saisie_notes.saisie_notes_tableur, - Permission.ScoEnsView, - methods=["GET", "POST"], -) -sco_publish( - "/feuille_saisie_notes", - sco_saisie_notes.feuille_saisie_notes, - Permission.ScoEnsView, -) -sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.ScoEnsView) -sco_publish( - "/save_note", - sco_saisie_notes.save_note, - Permission.ScoEnsView, - methods=["GET", "POST"], -) -sco_publish( - "/do_evaluation_set_missing", - sco_saisie_notes.do_evaluation_set_missing, - Permission.ScoEnsView, - methods=["GET", "POST"], -) -sco_publish( - "/evaluation_suppress_alln", - sco_saisie_notes.evaluation_suppress_alln, - Permission.ScoView, - methods=["GET", "POST"], -) - - -# --- Bulletins -@bp.route("/formsemestre_bulletins_pdf") -@scodoc -@permission_required(Permission.ScoView) -@scodoc7func -def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): - "Publie les bulletins dans un classeur PDF" - pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( - formsemestre_id, version=version - ) - return scu.sendPDFFile(pdfdoc, filename) - - -_EXPL_BULL = """Versions des bulletins: -" - + expl_bull, - choose_mail=True, - ) - - -# not published -def formsemestre_bulletins_choice( - formsemestre_id, title="", explanation="", choose_mail=False -): - """Choix d'une version de bulletin""" - H = [ - html_sco_header.html_sem_header(title), - """ -
""" + explanation + """
""") - - return "\n".join(H) + html_sco_header.sco_footer() - - -@bp.route("/formsemestre_bulletins_mailetuds") -@scodoc -@permission_required(Permission.ScoView) -@scodoc7func -def formsemestre_bulletins_mailetuds( - formsemestre_id, - version="long", - dialog_confirmed=False, - prefer_mail_perso=0, -): - "envoi a chaque etudiant (inscrit et ayant un mail) son bulletin" - prefer_mail_perso = int(prefer_mail_perso) - formsemestre = FormSemestre.query.get_or_404(formsemestre_id) - nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - etudids = nt.get_etudids() - # - if not sco_bulletins.can_send_bulletin_by_mail(formsemestre_id): - raise AccessDenied("vous n'avez pas le droit d'envoyer les bulletins") - # Confirmation dialog - if not dialog_confirmed: - return scu.confirm_dialog( - "%d bulletins sur %d envoyés par mail !
' - % (nb_sent, len(etudids), formsemestre_id) - + html_sco_header.sco_footer() - ) - - -sco_publish( - "/external_ue_create_form", - sco_ue_external.external_ue_create_form, - Permission.ScoView, - methods=["GET", "POST"], -) - - -@bp.route("/appreciation_add_form", methods=["GET", "POST"]) -@scodoc -@permission_required(Permission.ScoEnsView) -@scodoc7func -def appreciation_add_form( - etudid=None, - formsemestre_id=None, - id=None, # si id, edit - suppress=False, # si true, supress id -): - "form ajout ou edition d'une appreciation" - cnx = ndb.GetDBConnexion() - if id: # edit mode - apps = sco_etud.appreciations_list(cnx, args={"id": id}) - if not apps: - raise ScoValueError("id d'appreciation invalide !") - app = apps[0] - formsemestre_id = app["formsemestre_id"] - etudid = app["etudid"] - vals = scu.get_request_args() - if "edit" in vals: - edit = int(vals["edit"]) - elif id: - edit = 1 - else: - edit = 0 - sem = sco_formsemestre.get_formsemestre(formsemestre_id) - # check custom access permission - can_edit_app = (current_user.id in sem["responsables"]) or ( - current_user.has_permission(Permission.ScoEtudInscrit) - ) - if not can_edit_app: - raise AccessDenied("vous n'avez pas le droit d'ajouter une appreciation") - # - bull_url = "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" % ( - formsemestre_id, - etudid, - ) - if suppress: - sco_etud.appreciations_delete(cnx, id) - logdb(cnx, method="appreciation_suppress", etudid=etudid, msg="") - return flask.redirect(bull_url) - # - etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] - if id: - a = "Edition" - else: - a = "Ajout" - H = [ - html_sco_header.sco_header() - + "Opération non autorisée pour %s" % current_user, - dest_url=scu.ScoURL(), - ) - - 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" - if not sco_permissions_check.can_validate_sem(formsemestre_id): - return scu.confirm_dialog( - message="
Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
-
- 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/ Opération non autorisée pour {current_user}",
- dest_url=url_for(
- "notes.formsemestre_status",
- scodoc_dept=g.scodoc_dept,
- formsemestre_id=formsemestre_id,
- ),
- )
- formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
- form = jury_but_forms.FormSemestreValidationAutoBUTForm()
- if request.method == "POST":
- if not form.cancel.data:
- nb_admis = jury_but_validation_auto.formsemestre_validation_auto_but(
- formsemestre
- )
- flash(f"Décisions enregistrées ({nb_admis} admis)")
- return redirect(
- url_for(
- "notes.formsemestre_saisie_jury",
- scodoc_dept=g.scodoc_dept,
- formsemestre_id=formsemestre_id,
- )
- )
- return render_template(
- "but/formsemestre_validation_auto_but.html",
- form=form,
- sco=ScoData(formsemestre=formsemestre),
- title=f"Calcul automatique jury BUT",
- )
-
-
-@bp.route("/formsemestre_validate_previous_ue", methods=["GET", "POST"])
-@scodoc
-@permission_required(Permission.ScoView)
-@scodoc7func
-def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
- "Form. saisie UE validée hors ScoDoc"
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
- return sco_formsemestre_validation.formsemestre_validate_previous_ue(
- formsemestre_id, etudid
- )
-
-
-sco_publish(
- "/formsemestre_ext_create_form",
- sco_formsemestre_exterieurs.formsemestre_ext_create_form,
- Permission.ScoView,
- methods=["GET", "POST"],
-)
-
-
-@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"
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
- return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
- formsemestre_id, etudid
- )
-
-
-sco_publish(
- "/get_etud_ue_cap_html",
- sco_formsemestre_validation.get_etud_ue_cap_html,
- Permission.ScoView,
-)
-
-
-@bp.route("/etud_ue_suppress_validation")
-@scodoc
-@permission_required(Permission.ScoView)
-@scodoc7func
-def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id):
- """Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
- return sco_formsemestre_validation.etud_ue_suppress_validation(
- etudid, formsemestre_id, ue_id
- )
-
-
-@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"
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
-
- 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"
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
-
- 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."""
- if not sco_permissions_check.can_validate_sem(formsemestre_id):
- return scu.confirm_dialog(
- message=" Opération non autorisée pour %s" % current_user,
- dest_url=scu.ScoURL(),
- )
- etud = Identite.query.get_or_404(etudid)
- formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
- if formsemestre.formation.is_apc():
- next_url = url_for(
- "scolar.ficheEtud",
- 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"""
- Cette opération est irréversible. formation_id=%s" % sem["formation_id"],
- ]
- if bad_ue:
- H += [
- " Aucun problème à signaler ! Problème réparé: vérifiez Problème détecté réparable:
- réparer maintenant Problème détecté ! empty page: see logs and mails Une "formation" est un programme pédagogique structuré
+ en UE, matières et modules. Chaque semestre se réfère à une formation.
+ La modification d'une formation affecte tous les semestres qui s'y
+ réfèrent. Création d'une formation (avec UE, matières, modules)
+ à partir un fichier XML (réservé aux utilisateurs avertis) Import effectué ! Les enseignants d'un module ont le droit de
+ saisir et modifier toutes les notes des évaluations de ce module.
+ Pour changer le responsable du module, passez par la
+ page "Modification du semestre", accessible uniquement au responsable de la formation (chef de département)
+ Pour ajouter un enseignant, choisissez un nom dans le menu Enseignant %s déjà dans la liste ! Taper le début du nom de l'enseignant. Expérimental: formule de calcul de la moyenne %(target)s Attention: l'utilisation de formules ralentit considérablement
+ les traitements. A utiliser uniquement dans les cas ne pouvant pas être traités autrement. Dans la formule, les variables suivantes sont définies: Les éléments des vecteurs sont ordonnés dans l'ordre des %(objs)s%(ordre)s. Les fonctions suivantes sont utilisables: abs, cmp, dot, len, map, max, min, pow, reduce, round, sum, ifelse. La notation V(1,2,3) représente un vecteur (1,2,3).
-
- """
- return scu.confirm_dialog(
- f"""Confirmer la suppression des décisions du semestre
- {formsemestre.titre_mois()} pour {etud.nomprenom}
-
- Modules d'une autre formation que leur UE:
",
- "
".join([str(x) for x in bad_ue]),
- ]
- if bad_sem:
- H += [
- "Module du semestre dans une autre formation:
",
- "
".join([str(x) for x in bad_sem]),
- ]
- if not bad_ue and not bad_sem:
- H.append("
".join([str(x) for x in bad])
- txt = "\n".join([str(x) for x in bad])
- log("check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id)
- log(txt)
- # Notify by e-mail
- send_scodoc_alarm("Notes: formation incoherente !", txt)
- else:
- txth = "OK"
- log("ok")
- return html_sco_header.sco_header() + txth + html_sco_header.sco_footer()
-
-
-@bp.route("/check_formsemestre_integrity")
-@scodoc
-@permission_required(Permission.ScoView)
-@scodoc7func
-def check_formsemestre_integrity(formsemestre_id):
- "debug"
- log("check_formsemestre_integrity: formsemestre_id=%s" % (formsemestre_id))
- # verifie que tous les moduleimpl d'un formsemestre
- # se réfèrent à un module dont l'UE appartient a la même formation
- # Ancien bug: les ue_id étaient mal copiés lors des création de versions
- # de formations
- diag = []
-
- Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
- for mod in Mlist:
- if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]:
- diag.append(
- "moduleimpl %s: module.ue_id=%s != matiere.ue_id=%s"
- % (
- mod["moduleimpl_id"],
- mod["module"]["ue_id"],
- mod["matiere"]["ue_id"],
- )
- )
- if mod["ue"]["formation_id"] != mod["module"]["formation_id"]:
- diag.append(
- "moduleimpl %s: ue.formation_id=%s != mod.formation_id=%s"
- % (
- mod["moduleimpl_id"],
- mod["ue"]["formation_id"],
- mod["module"]["formation_id"],
- )
- )
- if diag:
- send_scodoc_alarm(
- "Notes: formation incoherente dans semestre %s !" % formsemestre_id,
- "\n".join(diag),
- )
- log("check_formsemestre_integrity: formsemestre_id=%s" % formsemestre_id)
- log("inconsistencies:\n" + "\n".join(diag))
- else:
- diag = ["OK"]
- log("ok")
- return (
- html_sco_header.sco_header() + "
".join(diag) + html_sco_header.sco_footer()
- )
-
-
-@bp.route("/check_integrity_all")
-@scodoc
-@permission_required(Permission.ScoView)
-@scodoc7func
-def check_integrity_all():
- "debug: verifie tous les semestres et tt les formations"
- # formations
- for F in sco_formations.formation_list():
- check_form_integrity(F["formation_id"])
- # semestres
- for sem in sco_formsemestre.do_formsemestre_list():
- check_formsemestre_integrity(sem["formsemestre_id"])
- return (
- html_sco_header.sco_header()
- + "Programmes pédagogiques
+ """,
+ ]
+ T = sco_formations.formation_list_table()
+
+ H.append(T.html())
+
+ if editable:
+ H.append(
+ f"""
+
+
+ Référentiels de compétences
+
+
+ """
+ )
+
+ H.append(html_sco_header.sco_footer())
+ return "\n".join(H)
+
+
+# --------------------------------------------------------------------
+#
+# Notes Methods
+#
+# --------------------------------------------------------------------
+
+# --- Formations
+
+sco_publish(
+ "/do_formation_create",
+ sco_edit_formation.do_formation_create,
+ Permission.ScoChangeFormation,
+ methods=["GET", "POST"],
+)
+
+sco_publish(
+ "/do_formation_delete",
+ sco_edit_formation.do_formation_delete,
+ Permission.ScoChangeFormation,
+ methods=["GET", "POST"],
+)
+
+
+@bp.route("/formation_list")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def formation_list(format=None, formation_id=None, args={}):
+ """List formation(s) with given id, or matching args
+ (when args is given, formation_id is ignored).
+ """
+ r = sco_formations.formation_list(formation_id=formation_id, args=args)
+ return scu.sendResult(r, name="formation", format=format)
+
+
+@bp.route("/formation_export")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def formation_export(formation_id, export_ids=False, format=None):
+ "Export de la formation au format indiqué (xml ou json)"
+ return sco_formations.formation_export(
+ formation_id, export_ids=export_ids, format=format
+ )
+
+
+@bp.route("/formation_import_xml_form", methods=["GET", "POST"])
+@scodoc
+@permission_required(Permission.ScoChangeFormation)
+@scodoc7func
+def formation_import_xml_form():
+ "form import d'une formation en XML"
+ H = [
+ html_sco_header.sco_header(page_title="Import d'une formation"),
+ """Import d'une formation
+ Confirmer le %s du semestre ?
" % msg,
+ helpmsg="""Les notes d'un semestre verrouillé ne peuvent plus être modifiées.
+ Un semestre verrouillé peut cependant être déverrouillé facilement à tout moment
+ (par son responsable ou un administrateur).
+
+ Le programme d'une formation qui a un semestre verrouillé ne peut plus être modifié.
+ """,
+ dest_url="",
+ cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
+ parameters={"formsemestre_id": formsemestre_id},
+ )
+
+ sco_formsemestre_edit.formsemestre_change_lock(formsemestre_id)
+
+ return flask.redirect(
+ url_for(
+ "notes.formsemestre_status",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ )
+ )
+
+
+sco_publish(
+ "/formsemestre_change_publication_bul",
+ sco_formsemestre_edit.formsemestre_change_publication_bul,
+ Permission.ScoView,
+ methods=["GET", "POST"],
+)
+sco_publish(
+ "/view_formsemestre_by_etape",
+ sco_formsemestre.view_formsemestre_by_etape,
+ Permission.ScoView,
+)
+
+
+@bp.route("/formsemestre_custommenu_edit", methods=["GET", "POST"])
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def formsemestre_custommenu_edit(formsemestre_id):
+ "Dialogue modif menu"
+ # accessible à tous !
+ return sco_formsemestre_custommenu.formsemestre_custommenu_edit(formsemestre_id)
+
+
+# --- dialogue modif enseignants/moduleimpl
+@bp.route("/edit_enseignants_form", methods=["GET", "POST"])
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def edit_enseignants_form(moduleimpl_id):
+ "modif liste enseignants/moduleimpl"
+ M, sem = sco_moduleimpl.can_change_ens(moduleimpl_id)
+ # --
+ header = html_sco_header.html_sem_header(
+ 'Enseignants du module %s'
+ % (moduleimpl_id, M["module"]["titre"]),
+ page_title="Enseignants du module %s" % M["module"]["titre"],
+ javascripts=["libjs/AutoSuggest.js"],
+ cssstyles=["css/autosuggest_inquisitor.css"],
+ bodyOnLoad="init_tf_form('')",
+ )
+ footer = html_sco_header.sco_footer()
+
+ # Liste des enseignants avec forme pour affichage / saisie avec suggestion
+ userlist = sco_users.get_user_list()
+ uid2display = {} # uid : forme pour affichage = "NOM Prenom (login)"(login)"
+ for u in userlist:
+ uid2display[u.id] = u.get_nomplogin()
+ allowed_user_names = list(uid2display.values())
+
+ H = [
+ "
")
+ F = """
+
+
Vous pouvez désactiver la formule (et revenir au mode de calcul "classique") + en supprimant le texte ou en faisant précéder la première ligne par #
+""" + + +@bp.route("/edit_moduleimpl_expr", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def edit_moduleimpl_expr(moduleimpl_id): + """Edition formule calcul moyenne module + Accessible par Admin, dir des etud et responsable module + + Inutilisé en ScoDoc 9. + """ + M, sem = sco_moduleimpl.can_change_ens(moduleimpl_id) + H = [ + html_sco_header.html_sem_header( + 'Modification règle de calcul du module %s' + % (moduleimpl_id, M["module"]["titre"]), + ), + _EXPR_HELP + % { + "target": "du module", + "objs": "évaluations", + "ordre": " (le premier élément est la plus ancienne évaluation)", + }, + ] + initvalues = M + form = [ + ("moduleimpl_id", {"input_type": "hidden"}), + ( + "computation_expr", + { + "title": "Formule de calcul", + "input_type": "textarea", + "rows": 4, + "cols": 60, + "explanation": "formule de calcul (expérimental)", + }, + ), + ] + tf = TrivialFormulator( + request.base_url, + scu.get_request_args(), + form, + submitlabel="Modifier formule de calcul", + cancelbutton="Annuler", + initvalues=initvalues, + ) + if tf[0] == 0: + return "\n".join(H) + tf[1] + html_sco_header.sco_footer() + elif tf[0] == -1: + return flask.redirect( + url_for( + "notes.moduleimpl_status", + scodoc_dept=g.scodoc_dept, + moduleimpl_id=moduleimpl_id, + ) + ) + else: + sco_moduleimpl.do_moduleimpl_edit( + { + "moduleimpl_id": moduleimpl_id, + "computation_expr": tf[2]["computation_expr"], + }, + formsemestre_id=sem["formsemestre_id"], + ) + sco_cache.invalidate_formsemestre( + formsemestre_id=sem["formsemestre_id"] + ) # > modif regle calcul + return flask.redirect( + url_for( + "notes.moduleimpl_status", + scodoc_dept=g.scodoc_dept, + moduleimpl_id=moduleimpl_id, + head_message="règle%20de%20calcul%20modifiée", + ) + ) + + +@bp.route("/delete_moduleimpl_expr", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def delete_moduleimpl_expr(moduleimpl_id): + """Suppression formule calcul moyenne module + Accessible par Admin, dir des etud et responsable module + """ + modimpl = ModuleImpl.query.get_or_404(moduleimpl_id) + sco_moduleimpl.can_change_ens(moduleimpl_id) + modimpl.computation_expr = None + db.session.add(modimpl) + db.session.commit() + flash("Ancienne formule supprimée") + return flask.redirect( + url_for( + "notes.moduleimpl_status", + scodoc_dept=g.scodoc_dept, + moduleimpl_id=moduleimpl_id, + ) + ) + + +@bp.route("/view_module_abs") +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def view_module_abs(moduleimpl_id, format="html"): + """Visualisation des absences a un module""" + M = sco_moduleimpl.moduleimpl_withmodule_list(moduleimpl_id=moduleimpl_id)[0] + sem = sco_formsemestre.get_formsemestre(M["formsemestre_id"]) + debut_sem = ndb.DateDMYtoISO(sem["date_debut"]) + fin_sem = ndb.DateDMYtoISO(sem["date_fin"]) + list_insc = sco_moduleimpl.moduleimpl_listeetuds(moduleimpl_id) + + T = [] + for etudid in list_insc: + nb_abs = sco_abs.count_abs( + etudid=etudid, + debut=debut_sem, + fin=fin_sem, + moduleimpl_id=moduleimpl_id, + ) + if nb_abs: + nb_abs_just = sco_abs.count_abs_just( + etudid=etudid, + debut=debut_sem, + fin=fin_sem, + moduleimpl_id=moduleimpl_id, + ) + etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] + T.append( + { + "nomprenom": etud["nomprenom"], + "just": nb_abs_just, + "nojust": nb_abs - nb_abs_just, + "total": nb_abs, + "_nomprenom_target": url_for( + "scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid + ), + } + ) + + H = [ + html_sco_header.html_sem_header( + 'Absences du module %s' + % (moduleimpl_id, M["module"]["titre"]), + page_title="Absences du module %s" % (M["module"]["titre"]), + ) + ] + if not T and format == "html": + return ( + "\n".join(H) + + "Aucune absence signalée
" + + html_sco_header.sco_footer() + ) + + tab = GenTable( + titles={ + "nomprenom": "Nom", + "just": "Just.", + "nojust": "Non Just.", + "total": "Total", + }, + columns_ids=("nomprenom", "just", "nojust", "total"), + rows=T, + html_class="table_leftalign", + base_url="%s?moduleimpl_id=%s" % (request.base_url, moduleimpl_id), + filename="absmodule_" + scu.make_filename(M["module"]["titre"]), + caption="Absences dans le module %s" % M["module"]["titre"], + preferences=sco_preferences.SemPreferences(), + ) + + if format != "html": + return tab.make_page(format=format) + + return "\n".join(H) + tab.html() + html_sco_header.sco_footer() + + +@bp.route("/delete_ue_expr/%s sera désinscrit de tous les modules du semestre %s (%s - %s).
+Cette opération ne doit être utilisée que pour corriger une erreur ! + Un étudiant réellement inscrit doit le rester, le faire éventuellement démissionner. +
+ """ % ( + etud["nomprenom"], + sem["titre_num"], + sem["date_debut"], + sem["date_fin"], + ) + else: # semestre extérieur + msg_ext = """ +%s sera désinscrit du semestre extérieur %s (%s - %s).
+ """ % ( + etud["nomprenom"], + sem["titre_num"], + sem["date_debut"], + sem["date_fin"], + ) + inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list( + args={"formsemestre_id": formsemestre_id} + ) + nbinscrits = len(inscrits) + if nbinscrits <= 1: + msg_ext = """Attention: le semestre extérieur + sera supprimé car il n'a pas d'autre étudiant inscrit. +
+ """ + return scu.confirm_dialog( + """Opération irréversible. Si vous supprimez l'évaluation, vous ne pourrez pas retrouver les notes associées.
""", + ] + warning = False + if etat["nb_notes_total"]: + warning = True + nb_desinscrits = etat["nb_notes_total"] - etat["nb_notes"] + H.append( + """Vous pouvez quand même supprimer l'évaluation, les notes des étudiants désincrits seront effacées.
""" + ) + + if etat["nb_notes"]: + H.append( + """Suppression impossible (effacer les notes d'abord)
OK, évaluation supprimée.
+ """ + + html_sco_header.sco_footer() + ) + + +sco_publish( + "/do_evaluation_list", + sco_evaluation_db.do_evaluation_list, + Permission.ScoView, +) + + +@bp.route("/evaluation_edit", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoEnsView) +@scodoc7func +def evaluation_edit(evaluation_id): + "form edit evaluation" + return sco_evaluation_edit.evaluation_create_form( + evaluation_id=evaluation_id, edit=True + ) + + +@bp.route("/evaluation_create", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoEnsView) +@scodoc7func +def evaluation_create(moduleimpl_id): + "form create evaluation" + modimpl = ModuleImpl.query.get(moduleimpl_id) + if modimpl is None: + raise ScoValueError("Ce module n'existe pas ou plus !") + return sco_evaluation_edit.evaluation_create_form( + moduleimpl_id=moduleimpl_id, edit=False + ) + + +@bp.route("/evaluation_listenotes", methods=["GET", "POST"]) # API ScoDoc 7 compat +@scodoc +@permission_required_compat_scodoc7(Permission.ScoView) +@scodoc7func +def evaluation_listenotes(): + """Affichage des notes d'une évaluation""" + evaluation_id = None + moduleimpl_id = None + vals = scu.get_request_args() + try: + if "evaluation_id" in vals: + evaluation_id = int(vals["evaluation_id"]) + if "moduleimpl_id" in vals and vals["moduleimpl_id"]: + moduleimpl_id = int(vals["moduleimpl_id"]) + except ValueError as exc: + raise ScoValueError("evaluation_listenotes: id invalides !") from exc + + format = vals.get("format", "html") + html_content, page_title = sco_liste_notes.do_evaluation_listenotes( + evaluation_id=evaluation_id, moduleimpl_id=moduleimpl_id, format=format + ) + if format == "html": + H = html_sco_header.sco_header( + page_title=page_title, + cssstyles=["css/verticalhisto.css"], + javascripts=["js/etud_info.js"], + init_qtip=True, + ) + F = html_sco_header.sco_footer() + return H + html_content + F + else: + return html_content + + +sco_publish( + "/evaluation_list_operations", + sco_undo_notes.evaluation_list_operations, + Permission.ScoView, +) +sco_publish( + "/evaluation_check_absences_html", + sco_evaluation_check_abs.evaluation_check_absences_html, + Permission.ScoView, +) +sco_publish( + "/formsemestre_check_absences_html", + sco_evaluation_check_abs.formsemestre_check_absences_html, + Permission.ScoView, +) + +# --- Placement des étudiants pour l'évaluation +sco_publish( + "/placement_eval_selectetuds", + sco_placement.placement_eval_selectetuds, + Permission.ScoEnsView, + methods=["GET", "POST"], +) + +# --- Saisie des notes +sco_publish( + "/saisie_notes_tableur", + sco_saisie_notes.saisie_notes_tableur, + Permission.ScoEnsView, + methods=["GET", "POST"], +) +sco_publish( + "/feuille_saisie_notes", + sco_saisie_notes.feuille_saisie_notes, + Permission.ScoEnsView, +) +sco_publish("/saisie_notes", sco_saisie_notes.saisie_notes, Permission.ScoEnsView) +sco_publish( + "/save_note", + sco_saisie_notes.save_note, + Permission.ScoEnsView, + methods=["GET", "POST"], +) +sco_publish( + "/do_evaluation_set_missing", + sco_saisie_notes.do_evaluation_set_missing, + Permission.ScoEnsView, + methods=["GET", "POST"], +) +sco_publish( + "/evaluation_suppress_alln", + sco_saisie_notes.evaluation_suppress_alln, + Permission.ScoView, + methods=["GET", "POST"], +) + + +# --- Bulletins +@bp.route("/formsemestre_bulletins_pdf") +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): + "Publie les bulletins dans un classeur PDF" + pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( + formsemestre_id, version=version + ) + return scu.sendPDFFile(pdfdoc, filename) + + +_EXPL_BULL = """Versions des bulletins: +" + + expl_bull, + choose_mail=True, + ) + + +# not published +def formsemestre_bulletins_choice( + formsemestre_id, title="", explanation="", choose_mail=False +): + """Choix d'une version de bulletin""" + H = [ + html_sco_header.html_sem_header(title), + """ +
""" + explanation + """
""") + + return "\n".join(H) + html_sco_header.sco_footer() + + +@bp.route("/formsemestre_bulletins_mailetuds") +@scodoc +@permission_required(Permission.ScoView) +@scodoc7func +def formsemestre_bulletins_mailetuds( + formsemestre_id, + version="long", + dialog_confirmed=False, + prefer_mail_perso=0, +): + "envoi a chaque etudiant (inscrit et ayant un mail) son bulletin" + prefer_mail_perso = int(prefer_mail_perso) + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) + nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + etudids = nt.get_etudids() + # + if not sco_bulletins.can_send_bulletin_by_mail(formsemestre_id): + raise AccessDenied("vous n'avez pas le droit d'envoyer les bulletins") + # Confirmation dialog + if not dialog_confirmed: + return scu.confirm_dialog( + "%d bulletins sur %d envoyés par mail !
' + % (nb_sent, len(etudids), formsemestre_id) + + html_sco_header.sco_footer() + ) + + +sco_publish( + "/external_ue_create_form", + sco_ue_external.external_ue_create_form, + Permission.ScoView, + methods=["GET", "POST"], +) + + +@bp.route("/appreciation_add_form", methods=["GET", "POST"]) +@scodoc +@permission_required(Permission.ScoEnsView) +@scodoc7func +def appreciation_add_form( + etudid=None, + formsemestre_id=None, + id=None, # si id, edit + suppress=False, # si true, supress id +): + "form ajout ou edition d'une appreciation" + cnx = ndb.GetDBConnexion() + if id: # edit mode + apps = sco_etud.appreciations_list(cnx, args={"id": id}) + if not apps: + raise ScoValueError("id d'appreciation invalide !") + app = apps[0] + formsemestre_id = app["formsemestre_id"] + etudid = app["etudid"] + vals = scu.get_request_args() + if "edit" in vals: + edit = int(vals["edit"]) + elif id: + edit = 1 + else: + edit = 0 + sem = sco_formsemestre.get_formsemestre(formsemestre_id) + # check custom access permission + can_edit_app = (current_user.id in sem["responsables"]) or ( + current_user.has_permission(Permission.ScoEtudInscrit) + ) + if not can_edit_app: + raise AccessDenied("vous n'avez pas le droit d'ajouter une appreciation") + # + bull_url = "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" % ( + formsemestre_id, + etudid, + ) + if suppress: + sco_etud.appreciations_delete(cnx, id) + logdb(cnx, method="appreciation_suppress", etudid=etudid, msg="") + return flask.redirect(bull_url) + # + etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] + if id: + a = "Edition" + else: + a = "Ajout" + H = [ + html_sco_header.sco_header() + + "Opération non autorisée pour %s" % current_user, + dest_url=scu.ScoURL(), + ) + + 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" + if not sco_permissions_check.can_validate_sem(formsemestre_id): + return scu.confirm_dialog( + message="
Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+
+ 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/ Opération non autorisée pour {current_user}",
+ dest_url=url_for(
+ "notes.formsemestre_status",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ ),
+ )
+ formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
+ form = jury_but_forms.FormSemestreValidationAutoBUTForm()
+ if request.method == "POST":
+ if not form.cancel.data:
+ nb_admis = jury_but_validation_auto.formsemestre_validation_auto_but(
+ formsemestre
+ )
+ flash(f"Décisions enregistrées ({nb_admis} admis)")
+ return redirect(
+ url_for(
+ "notes.formsemestre_saisie_jury",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ )
+ )
+ return render_template(
+ "but/formsemestre_validation_auto_but.html",
+ form=form,
+ sco=ScoData(formsemestre=formsemestre),
+ title=f"Calcul automatique jury BUT",
+ )
+
+
+@bp.route("/formsemestre_validate_previous_ue", methods=["GET", "POST"])
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def formsemestre_validate_previous_ue(formsemestre_id, etudid=None):
+ "Form. saisie UE validée hors ScoDoc"
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+ return sco_formsemestre_validation.formsemestre_validate_previous_ue(
+ formsemestre_id, etudid
+ )
+
+
+sco_publish(
+ "/formsemestre_ext_create_form",
+ sco_formsemestre_exterieurs.formsemestre_ext_create_form,
+ Permission.ScoView,
+ methods=["GET", "POST"],
+)
+
+
+@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"
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+ return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
+ formsemestre_id, etudid
+ )
+
+
+sco_publish(
+ "/get_etud_ue_cap_html",
+ sco_formsemestre_validation.get_etud_ue_cap_html,
+ Permission.ScoView,
+)
+
+
+@bp.route("/etud_ue_suppress_validation")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id):
+ """Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+ return sco_formsemestre_validation.etud_ue_suppress_validation(
+ etudid, formsemestre_id, ue_id
+ )
+
+
+@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"
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+
+ 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"
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+
+ 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."""
+ if not sco_permissions_check.can_validate_sem(formsemestre_id):
+ return scu.confirm_dialog(
+ message=" Opération non autorisée pour %s" % current_user,
+ dest_url=scu.ScoURL(),
+ )
+ etud = Identite.query.get_or_404(etudid)
+ formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
+ if formsemestre.formation.is_apc():
+ next_url = url_for(
+ "scolar.ficheEtud",
+ 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"""
+ Cette opération est irréversible. formation_id=%s" % sem["formation_id"],
+ ]
+ if bad_ue:
+ H += [
+ " Aucun problème à signaler ! Problème réparé: vérifiez Problème détecté réparable:
+ réparer maintenant Problème détecté ! empty page: see logs and mails
+
+ """
+ return scu.confirm_dialog(
+ f"""Confirmer la suppression des décisions du semestre
+ {formsemestre.titre_mois()} pour {etud.nomprenom}
+
+ Modules d'une autre formation que leur UE:
",
+ "
".join([str(x) for x in bad_ue]),
+ ]
+ if bad_sem:
+ H += [
+ "Module du semestre dans une autre formation:
",
+ "
".join([str(x) for x in bad_sem]),
+ ]
+ if not bad_ue and not bad_sem:
+ H.append("
".join([str(x) for x in bad])
+ txt = "\n".join([str(x) for x in bad])
+ log("check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id)
+ log(txt)
+ # Notify by e-mail
+ send_scodoc_alarm("Notes: formation incoherente !", txt)
+ else:
+ txth = "OK"
+ log("ok")
+ return html_sco_header.sco_header() + txth + html_sco_header.sco_footer()
+
+
+@bp.route("/check_formsemestre_integrity")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def check_formsemestre_integrity(formsemestre_id):
+ "debug"
+ log("check_formsemestre_integrity: formsemestre_id=%s" % (formsemestre_id))
+ # verifie que tous les moduleimpl d'un formsemestre
+ # se réfèrent à un module dont l'UE appartient a la même formation
+ # Ancien bug: les ue_id étaient mal copiés lors des création de versions
+ # de formations
+ diag = []
+
+ Mlist = sco_moduleimpl.moduleimpl_withmodule_list(formsemestre_id=formsemestre_id)
+ for mod in Mlist:
+ if mod["module"]["ue_id"] != mod["matiere"]["ue_id"]:
+ diag.append(
+ "moduleimpl %s: module.ue_id=%s != matiere.ue_id=%s"
+ % (
+ mod["moduleimpl_id"],
+ mod["module"]["ue_id"],
+ mod["matiere"]["ue_id"],
+ )
+ )
+ if mod["ue"]["formation_id"] != mod["module"]["formation_id"]:
+ diag.append(
+ "moduleimpl %s: ue.formation_id=%s != mod.formation_id=%s"
+ % (
+ mod["moduleimpl_id"],
+ mod["ue"]["formation_id"],
+ mod["module"]["formation_id"],
+ )
+ )
+ if diag:
+ send_scodoc_alarm(
+ "Notes: formation incoherente dans semestre %s !" % formsemestre_id,
+ "\n".join(diag),
+ )
+ log("check_formsemestre_integrity: formsemestre_id=%s" % formsemestre_id)
+ log("inconsistencies:\n" + "\n".join(diag))
+ else:
+ diag = ["OK"]
+ log("ok")
+ return (
+ html_sco_header.sco_header() + "
".join(diag) + html_sco_header.sco_footer()
+ )
+
+
+@bp.route("/check_integrity_all")
+@scodoc
+@permission_required(Permission.ScoView)
+@scodoc7func
+def check_integrity_all():
+ "debug: verifie tous les semestres et tt les formations"
+ # formations
+ for F in sco_formations.formation_list():
+ check_form_integrity(F["formation_id"])
+ # semestres
+ for sem in sco_formsemestre.do_formsemestre_list():
+ check_formsemestre_integrity(sem["formsemestre_id"])
+ return (
+ html_sco_header.sco_header()
+ + "