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"