1
0
forked from ScoDoc/ScoDoc

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
"""
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/<int:semset_id>", methods=["POST"])
@api_web_bp.route("/semset/set_periode/<int:semset_id>", 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/<int:semset_id>", methods=["POST"])
# @api_web_bp.route("/semset/set_periode/<int:semset_id>", 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})

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 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.

View File

@ -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:

View File

@ -240,7 +240,11 @@ def apo_semset_maq_status(
if semset["jury_ok"]:
H.append("""<li>Décisions de jury saisies</li>""")
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:
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):
"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("</p>")
H.append(
f"""<p>Période: <select name="periode" onchange="set_periode(this);">
<option value="1" {"selected" if self["sem_id"] == 1 else ""}>1re période (S1, S3)</option>
<option value="2" {"selected" if self["sem_id"] == 2 else ""}>2de période (S2, S4)</option>
<option value="0" {"selected" if self["sem_id"] == 0 else ""}>non semestrialisée (LP, ...)</option>
</select>
</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>
"""
)
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"<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:
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"):
<select name="sem_id">
<option value="1">1re période (S1, S3)</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>
<input type="text" name="title" size="32"/>
<input type="submit" value="Créer"/>