From 54bcbff179a646a5ec5c648cc48d44e8d90dc08c Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 18 Apr 2023 11:47:10 +0200 Subject: [PATCH] =?UTF-8?q?Apo:=20modif=20semset=20pour=20BUT.=20Interdit?= =?UTF-8?q?=20changement=20p=C3=A9riode.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/semset.py | 55 +++++++++++---------- app/comp/res_compat.py | 24 +++++---- app/scodoc/sco_etape_apogee.py | 7 ++- app/scodoc/sco_etape_apogee_view.py | 6 ++- app/scodoc/sco_semset.py | 77 ++++++++++++++++------------- 5 files changed, 93 insertions(+), 76 deletions(-) diff --git a/app/api/semset.py b/app/api/semset.py index f1239534f..5eba3f3ee 100644 --- a/app/api/semset.py +++ b/app/api/semset.py @@ -7,33 +7,34 @@ """ ScoDoc 9 API : accès aux formsemestres """ -from flask import g, jsonify, request -from flask_login import login_required +# from flask import g, jsonify, request +# from flask_login import login_required -import app -from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR -from app.decorators import scodoc, permission_required -from app.scodoc.sco_utils import json_error -from app.models.formsemestre import NotesSemSet -from app.scodoc.sco_permissions import Permission +# import app +# from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR +# from app.decorators import scodoc, permission_required +# from app.scodoc.sco_utils import json_error +# from app.models.formsemestre import NotesSemSet +# from app.scodoc.sco_permissions import Permission -@bp.route("/semset/set_periode/", methods=["POST"]) -@api_web_bp.route("/semset/set_periode/", methods=["POST"]) -@login_required -@scodoc -@permission_required(Permission.ScoEditApo) -# TODO à modifier pour utiliser @as_json -def semset_set_periode(semset_id: int): - "Change la période d'un semset" - query = NotesSemSet.query.filter_by(semset_id=semset_id) - if g.scodoc_dept: - query = query.filter_by(dept_id=g.scodoc_dept_id) - semset: NotesSemSet = query.first_or_404() - data = request.get_json(force=True) # may raise 400 Bad Request - try: - periode = int(data) - semset.set_periode(periode) - except ValueError: - return json_error(API_CLIENT_ERROR, "invalid periode value") - return jsonify({"OK": True}) +# Impossible de changer la période à cause des archives +# @bp.route("/semset/set_periode/", methods=["POST"]) +# @api_web_bp.route("/semset/set_periode/", methods=["POST"]) +# @login_required +# @scodoc +# @permission_required(Permission.ScoEditApo) +# # TODO à modifier pour utiliser @as_json +# def semset_set_periode(semset_id: int): +# "Change la période d'un semset" +# query = NotesSemSet.query.filter_by(semset_id=semset_id) +# if g.scodoc_dept: +# query = query.filter_by(dept_id=g.scodoc_dept_id) +# semset: NotesSemSet = query.first_or_404() +# data = request.get_json(force=True) # may raise 400 Bad Request +# try: +# periode = int(data) +# semset.set_periode(periode) +# except ValueError: +# return json_error(API_CLIENT_ERROR, "invalid periode value") +# return jsonify({"OK": True}) diff --git a/app/comp/res_compat.py b/app/comp/res_compat.py index bc501b64a..7b20ffd51 100644 --- a/app/comp/res_compat.py +++ b/app/comp/res_compat.py @@ -19,6 +19,7 @@ from app.models import Identite, FormSemestre, ModuleImpl, ScolarAutorisationIns from app.scodoc.codes_cursus import UE_SPORT, DEF from app.scodoc import sco_utils as scu + # Pour raccorder le code des anciens codes qui attendent une NoteTable class NotesTableCompat(ResultatsSemestre): """Implementation partielle de NotesTable @@ -266,16 +267,21 @@ class NotesTableCompat(ResultatsSemestre): ue_status_list.append(ue_status) return self.parcours.check_barre_ues(ue_status_list) - def all_etuds_have_sem_decisions(self): - """True si tous les étudiants du semestre ont une décision de jury. - Ne regarde pas les décisions d'UE. + def etudids_without_decisions(self) -> list[int]: + """Liste des id d'étudiants du semestre non démissionnaires + n'ayant pas de décision de jury. + - En classic: ne regarde pas que la décision de semestre (pas les décisions d'UE). + - en BUT: utilise etud_has_decision """ - for ins in self.formsemestre.inscriptions: - if ins.etat != scu.INSCRIT: - continue # skip démissionnaires - if self.get_etud_decision_sem(ins.etudid) is None: - return False - return True + check_func = ( + self.etud_has_decision if self.is_apc else self.get_etud_decision_sem + ) + etudids = [ + ins.etudid + for ins in self.formsemestre.inscriptions + if (ins.etat == scu.INSCRIT) and (not check_func(ins.etudid)) + ] + return etudids def etud_has_decision(self, etudid): """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre. diff --git a/app/scodoc/sco_etape_apogee.py b/app/scodoc/sco_etape_apogee.py index be10eb23b..ef60acca8 100644 --- a/app/scodoc/sco_etape_apogee.py +++ b/app/scodoc/sco_etape_apogee.py @@ -143,10 +143,9 @@ def apo_csv_list_stored_archives(annee_scolaire=None, sem_id=None, etapes=None): if annee_scolaire: e = re.compile(str(annee_scolaire) + "-.+") oids = [x for x in oids if e.match(x)] - # Note: ne filtre plus sur la période, afin que l'on puisse chnager le semset de période. - # if sem_id: - # e = re.compile(r"[0-9]{4}-" + str(sem_id)) - # oids = [x for x in oids if e.match(x)] + if sem_id: + e = re.compile(r"[0-9]{4}-" + str(sem_id)) + oids = [x for x in oids if e.match(x)] infos = [] # liste d'infos for oid in oids: diff --git a/app/scodoc/sco_etape_apogee_view.py b/app/scodoc/sco_etape_apogee_view.py index e40cbe649..77d2bb40c 100644 --- a/app/scodoc/sco_etape_apogee_view.py +++ b/app/scodoc/sco_etape_apogee_view.py @@ -240,7 +240,11 @@ def apo_semset_maq_status( if semset["jury_ok"]: H.append("""
  • Décisions de jury saisies
  • """) else: - H.append("""
  • Il manque des décisions de jury !
  • """) + H.append( + f"""
  • Il manque de {semset["jury_nb_missing"]} + décision{"s" if semset["jury_nb_missing"] > 1 else ""} + de jury !
  • """ + ) if ok_for_export: H.append("""
  • %d étudiants, prêt pour l'export.
  • """ % len(nips_ok)) diff --git a/app/scodoc/sco_semset.py b/app/scodoc/sco_semset.py index f8cf5e818..e4cb58520 100644 --- a/app/scodoc/sco_semset.py +++ b/app/scodoc/sco_semset.py @@ -156,6 +156,8 @@ class SemSet(dict): def add(self, formsemestre_id): "Ajoute ce semestre à l'ensemble" + # check for valid formsemestre_id + formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id) # check if formsemestre_id in self.formsemestre_ids: return # already there @@ -166,6 +168,19 @@ class SemSet(dict): f"can't add {formsemestre_id} to set {self.semset_id}: incompatible sem_id" ) + if self.formsemestre_ids: + formsemestre_1 = formsemestre.query.get(self.formsemestre_ids[0]) + if formsemestre.formation.is_apc() != formsemestre_1.formation.is_apc(): + raise ScoValueError( + """On ne peut pas mélanger des semestres BUT/APC + avec des semestres ordinaires dans le même export.""", + dest_url=url_for( + "notes.apo_semset_maq_status", + scodoc_dept=g.scodoc_dept, + semset_id=self.semset_id, + ), + ) + ndb.SimpleQuery( """INSERT INTO notes_semset_formsemestre (formsemestre_id, semset_id) @@ -249,17 +264,28 @@ class SemSet(dict): def load_etuds(self): self["etuds_without_nip"] = set() # etudids self["jury_ok"] = True + self["jury_nb_missing"] = 0 + is_apc = None for sem in self.sems: formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) + if is_apc is not None and is_apc != nt.is_apc: + raise ScoValueError( + "Incohérence: semestre APC (BUT) et ordinaires mélangés !" + ) + else: + is_apc = nt.is_apc sem["etuds"] = list(nt.identdict.values()) sem["nips"] = {e["code_nip"] for e in sem["etuds"] if e["code_nip"]} sem["etuds_without_nip"] = { e["etudid"] for e in sem["etuds"] if not e["code_nip"] } self["etuds_without_nip"] |= sem["etuds_without_nip"] - sem["jury_ok"] = nt.all_etuds_have_sem_decisions() + sem["etudids_no_jury"] = nt.etudids_without_decisions() + sem["jury_ok"] = not sem["etudids_no_jury"] self["jury_ok"] &= sem["jury_ok"] + self["jury_nb_missing"] += len(sem["etudids_no_jury"]) + self["is_apc"] = bool(is_apc) def html_descr(self): """Short HTML description""" @@ -279,36 +305,21 @@ class SemSet(dict): ) H.append("

    ") - H.append( - f"""

    Période: -

    - - """ - ) + if self["sem_id"] == 1: + periode = "1re période (S1, S3)" + elif self["sem_id"] == 1: + periode = "2de période (S2, S4)" + else: + periode = "non semestrialisée (LP, ...). Incompatible avec BUT." H.append( - f"

    Etapes: {sco_formsemestre.etapes_apo_str(self.list_etapes())}

    " + f""" +

    Période: {periode}

    +

    Etapes: {sco_formsemestre.etapes_apo_str(self.list_etapes())}

    + +

    Semestres de l'ensemble:

      + """ ) - H.append("""

      Semestres de l'ensemble:

        """) for sem in self.sems: H.append( @@ -429,12 +440,8 @@ def do_semset_add_sem(semset_id, formsemestre_id): raise ScoValueError("empty semset_id") if formsemestre_id == "": raise ScoValueError("pas de semestre choisi !") - s = SemSet(semset_id=semset_id) - # check for valid formsemestre_id - _ = sco_formsemestre.get_formsemestre(formsemestre_id) # raise exc - - s.add(formsemestre_id) - + semset = SemSet(semset_id=semset_id) + semset.add(formsemestre.id) return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id) @@ -541,7 +548,7 @@ def semset_page(format="html"):