Apo: modif semset pour BUT. Interdit changement période.

This commit is contained in:
Emmanuel Viennet 2023-04-18 11:47:10 +02:00
parent 339d70edd2
commit 54bcbff179
5 changed files with 93 additions and 76 deletions

View File

@ -7,33 +7,34 @@
""" """
ScoDoc 9 API : accès aux formsemestres ScoDoc 9 API : accès aux formsemestres
""" """
from flask import g, jsonify, request # from flask import g, jsonify, request
from flask_login import login_required # from flask_login import login_required
import app # import app
from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR # from app.api import api_bp as bp, api_web_bp, API_CLIENT_ERROR
from app.decorators import scodoc, permission_required # from app.decorators import scodoc, permission_required
from app.scodoc.sco_utils import json_error # from app.scodoc.sco_utils import json_error
from app.models.formsemestre import NotesSemSet # from app.models.formsemestre import NotesSemSet
from app.scodoc.sco_permissions import Permission # from app.scodoc.sco_permissions import Permission
@bp.route("/semset/set_periode/<int:semset_id>", methods=["POST"]) # Impossible de changer la période à cause des archives
@api_web_bp.route("/semset/set_periode/<int:semset_id>", methods=["POST"]) # @bp.route("/semset/set_periode/<int:semset_id>", methods=["POST"])
@login_required # @api_web_bp.route("/semset/set_periode/<int:semset_id>", methods=["POST"])
@scodoc # @login_required
@permission_required(Permission.ScoEditApo) # @scodoc
# TODO à modifier pour utiliser @as_json # @permission_required(Permission.ScoEditApo)
def semset_set_periode(semset_id: int): # # TODO à modifier pour utiliser @as_json
"Change la période d'un semset" # def semset_set_periode(semset_id: int):
query = NotesSemSet.query.filter_by(semset_id=semset_id) # "Change la période d'un semset"
if g.scodoc_dept: # query = NotesSemSet.query.filter_by(semset_id=semset_id)
query = query.filter_by(dept_id=g.scodoc_dept_id) # if g.scodoc_dept:
semset: NotesSemSet = query.first_or_404() # query = query.filter_by(dept_id=g.scodoc_dept_id)
data = request.get_json(force=True) # may raise 400 Bad Request # semset: NotesSemSet = query.first_or_404()
try: # data = request.get_json(force=True) # may raise 400 Bad Request
periode = int(data) # try:
semset.set_periode(periode) # periode = int(data)
except ValueError: # semset.set_periode(periode)
return json_error(API_CLIENT_ERROR, "invalid periode value") # except ValueError:
return jsonify({"OK": True}) # return json_error(API_CLIENT_ERROR, "invalid periode value")
# return jsonify({"OK": True})

View File

@ -19,6 +19,7 @@ from app.models import Identite, FormSemestre, ModuleImpl, ScolarAutorisationIns
from app.scodoc.codes_cursus import UE_SPORT, DEF from app.scodoc.codes_cursus import UE_SPORT, DEF
from app.scodoc import sco_utils as scu from app.scodoc import sco_utils as scu
# Pour raccorder le code des anciens codes qui attendent une NoteTable # Pour raccorder le code des anciens codes qui attendent une NoteTable
class NotesTableCompat(ResultatsSemestre): class NotesTableCompat(ResultatsSemestre):
"""Implementation partielle de NotesTable """Implementation partielle de NotesTable
@ -266,16 +267,21 @@ class NotesTableCompat(ResultatsSemestre):
ue_status_list.append(ue_status) ue_status_list.append(ue_status)
return self.parcours.check_barre_ues(ue_status_list) return self.parcours.check_barre_ues(ue_status_list)
def all_etuds_have_sem_decisions(self): def etudids_without_decisions(self) -> list[int]:
"""True si tous les étudiants du semestre ont une décision de jury. """Liste des id d'étudiants du semestre non démissionnaires
Ne regarde pas les décisions d'UE. 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: check_func = (
if ins.etat != scu.INSCRIT: self.etud_has_decision if self.is_apc else self.get_etud_decision_sem
continue # skip démissionnaires )
if self.get_etud_decision_sem(ins.etudid) is None: etudids = [
return False ins.etudid
return True 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): def etud_has_decision(self, etudid):
"""True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre. """True s'il y a une décision de jury pour cet étudiant émanant de ce formsemestre.

View File

@ -143,10 +143,9 @@ def apo_csv_list_stored_archives(annee_scolaire=None, sem_id=None, etapes=None):
if annee_scolaire: if annee_scolaire:
e = re.compile(str(annee_scolaire) + "-.+") e = re.compile(str(annee_scolaire) + "-.+")
oids = [x for x in oids if e.match(x)] 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:
# if sem_id: e = re.compile(r"[0-9]{4}-" + str(sem_id))
# e = re.compile(r"[0-9]{4}-" + str(sem_id)) oids = [x for x in oids if e.match(x)]
# oids = [x for x in oids if e.match(x)]
infos = [] # liste d'infos infos = [] # liste d'infos
for oid in oids: for oid in oids:

View File

@ -240,7 +240,11 @@ def apo_semset_maq_status(
if semset["jury_ok"]: if semset["jury_ok"]:
H.append("""<li>Décisions de jury saisies</li>""") H.append("""<li>Décisions de jury saisies</li>""")
else: else:
H.append("""<li>Il manque des décisions de jury !</li>""") H.append(
f"""<li>Il manque de {semset["jury_nb_missing"]}
décision{"s" if semset["jury_nb_missing"] > 1 else ""}
de jury !</li>"""
)
if ok_for_export: if ok_for_export:
H.append("""<li>%d étudiants, prêt pour l'export.</li>""" % len(nips_ok)) H.append("""<li>%d étudiants, prêt pour l'export.</li>""" % len(nips_ok))

View File

@ -156,6 +156,8 @@ class SemSet(dict):
def add(self, formsemestre_id): def add(self, formsemestre_id):
"Ajoute ce semestre à l'ensemble" "Ajoute ce semestre à l'ensemble"
# check for valid formsemestre_id
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
# check # check
if formsemestre_id in self.formsemestre_ids: if formsemestre_id in self.formsemestre_ids:
return # already there return # already there
@ -166,6 +168,19 @@ class SemSet(dict):
f"can't add {formsemestre_id} to set {self.semset_id}: incompatible sem_id" 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( ndb.SimpleQuery(
"""INSERT INTO notes_semset_formsemestre """INSERT INTO notes_semset_formsemestre
(formsemestre_id, semset_id) (formsemestre_id, semset_id)
@ -249,17 +264,28 @@ class SemSet(dict):
def load_etuds(self): def load_etuds(self):
self["etuds_without_nip"] = set() # etudids self["etuds_without_nip"] = set() # etudids
self["jury_ok"] = True self["jury_ok"] = True
self["jury_nb_missing"] = 0
is_apc = None
for sem in self.sems: for sem in self.sems:
formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"]) formsemestre = FormSemestre.query.get_or_404(sem["formsemestre_id"])
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) 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["etuds"] = list(nt.identdict.values())
sem["nips"] = {e["code_nip"] for e in sem["etuds"] if e["code_nip"]} sem["nips"] = {e["code_nip"] for e in sem["etuds"] if e["code_nip"]}
sem["etuds_without_nip"] = { sem["etuds_without_nip"] = {
e["etudid"] for e in sem["etuds"] if not e["code_nip"] e["etudid"] for e in sem["etuds"] if not e["code_nip"]
} }
self["etuds_without_nip"] |= sem["etuds_without_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_ok"] &= sem["jury_ok"]
self["jury_nb_missing"] += len(sem["etudids_no_jury"])
self["is_apc"] = bool(is_apc)
def html_descr(self): def html_descr(self):
"""Short HTML description""" """Short HTML description"""
@ -279,36 +305,21 @@ class SemSet(dict):
) )
H.append("</p>") H.append("</p>")
H.append( if self["sem_id"] == 1:
f"""<p>Période: <select name="periode" onchange="set_periode(this);"> periode = "1re période (S1, S3)"
<option value="1" {"selected" if self["sem_id"] == 1 else ""}>1re période (S1, S3)</option> elif self["sem_id"] == 1:
<option value="2" {"selected" if self["sem_id"] == 2 else ""}>2de période (S2, S4)</option> periode = "2de période (S2, S4)"
<option value="0" {"selected" if self["sem_id"] == 0 else ""}>non semestrialisée (LP, ...)</option> else:
</select> periode = "non semestrialisée (LP, ...). Incompatible avec BUT."
</p>
<script>
function set_periode(elt) {{
fetch(
"{ url_for("apiweb.semset_set_periode", scodoc_dept=g.scodoc_dept,
semset_id=self.semset_id )
}",
{{
method: "POST",
headers: {{
'Content-Type': 'application/json'
}},
body: JSON.stringify( elt.value )
}},
).then(sco_message("période modifiée"));
}};
</script>
"""
)
H.append( H.append(
f"<p>Etapes: <tt>{sco_formsemestre.etapes_apo_str(self.list_etapes())}</tt></p>" f"""
<p>Période: <b>{periode}</b></p>
<p>Etapes: <tt>{sco_formsemestre.etapes_apo_str(self.list_etapes())}</tt></p>
<h4>Semestres de l'ensemble:</h4><ul class="semset_listsems">
"""
) )
H.append("""<h4>Semestres de l'ensemble:</h4><ul class="semset_listsems">""")
for sem in self.sems: for sem in self.sems:
H.append( H.append(
@ -429,12 +440,8 @@ def do_semset_add_sem(semset_id, formsemestre_id):
raise ScoValueError("empty semset_id") raise ScoValueError("empty semset_id")
if formsemestre_id == "": if formsemestre_id == "":
raise ScoValueError("pas de semestre choisi !") raise ScoValueError("pas de semestre choisi !")
s = SemSet(semset_id=semset_id) semset = SemSet(semset_id=semset_id)
# check for valid formsemestre_id semset.add(formsemestre.id)
_ = sco_formsemestre.get_formsemestre(formsemestre_id) # raise exc
s.add(formsemestre_id)
return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id) return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
@ -541,7 +548,7 @@ def semset_page(format="html"):
<select name="sem_id"> <select name="sem_id">
<option value="1">1re période (S1, S3)</option> <option value="1">1re période (S1, S3)</option>
<option value="2">2de période (S2, S4)</option> <option value="2">2de période (S2, S4)</option>
<option value="0">non semestrialisée (LP, ...)</option> <option value="0">non semestrialisée (LP, ... mais pas pour le BUT !)</option>
</select> </select>
<input type="text" name="title" size="32"/> <input type="text" name="title" size="32"/>
<input type="submit" value="Créer"/> <input type="submit" value="Créer"/>