diff --git a/app/api/etudiants.py b/app/api/etudiants.py index 2b1128364..46478231b 100644 --- a/app/api/etudiants.py +++ b/app/api/etudiants.py @@ -473,7 +473,7 @@ def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner return response return sco_bulletins.get_formsemestre_bulletin_etud_json( - formsemestre, etud, version + formsemestre, etud, version=version ) diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 04bd92d13..98b5a9c6f 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -650,7 +650,7 @@ class ResultatsSemestre(ResultatsCache): elif nb_ues_validables < len(ues_sans_bonus): row["_ues_validables_class"] += " moy_inf" row["_ues_validables_order"] = nb_ues_validables # pour tri - if mode_jury: + if mode_jury and self.validations: dec_sem = self.validations.decisions_jury.get(etudid) jury_code_sem = dec_sem["code"] if dec_sem else "" idx = add_cell( diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index 29428c05d..3aff0e9ce 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -451,6 +451,7 @@ class ApcAppCritique(db.Model, XMLModel): if competence is not None: query = query.filter(ApcNiveau.competence == competence) return query +<<<<<<< HEAD def __init__(self, id, niveau_id, code, libelle, modules): self.id = id @@ -458,6 +459,8 @@ class ApcAppCritique(db.Model, XMLModel): self.code = code self.libelle = libelle self.modules = modules +======= +>>>>>>> 7c340c798ad59c41653efc83bfd079f11fce1938 def to_dict(self) -> dict: return {"libelle": self.libelle} @@ -544,11 +547,14 @@ class ApcAnneeParcours(db.Model, XMLModel): ) ordre = db.Column(db.Integer) "numéro de l'année: 1, 2, 3" +<<<<<<< HEAD def __init__(self, id, parcours_id, ordre): self.id = id self.parcours_id = parcours_id self.ordre = ordre +======= +>>>>>>> 7c340c798ad59c41653efc83bfd079f11fce1938 def __repr__(self): return f"<{self.__class__.__name__} ordre={self.ordre!r} parcours={self.parcours.code!r}>" diff --git a/app/models/but_validations.py b/app/models/but_validations.py index ebedadd71..9d42c14d3 100644 --- a/app/models/but_validations.py +++ b/app/models/but_validations.py @@ -43,6 +43,7 @@ class ApcValidationRCUE(db.Model): formsemestre_id = db.Column( db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True ) + "formsemestre pair du RCUE" # Les deux UE associées à ce niveau: ue1_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False) ue2_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False) diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index daf4a9419..4a82674e2 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -58,7 +58,6 @@ from app.scodoc import sco_formations from app.scodoc import sco_formsemestre from app.scodoc import sco_groups from app.scodoc import sco_permissions_check -from app.scodoc import sco_photos from app.scodoc import sco_preferences from app.scodoc import sco_pvjury from app.scodoc import sco_users @@ -66,15 +65,6 @@ import app.scodoc.sco_utils as scu from app.scodoc.sco_utils import ModuleType, fmt_note import app.scodoc.notesdb as ndb -# ----- CLASSES DE BULLETINS DE NOTES -from app.scodoc import sco_bulletins_standard -from app.scodoc import sco_bulletins_legacy - -# import sco_bulletins_example # format exemple (à désactiver en production) - -# ... ajouter ici vos modules ... -from app.scodoc import sco_bulletins_ucac # format expérimental UCAC Cameroun - def get_formsemestre_bulletin_etud_json( formsemestre: FormSemestre, diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py index 78425028d..c48c0d439 100644 --- a/app/scodoc/sco_bulletins_json.py +++ b/app/scodoc/sco_bulletins_json.py @@ -92,7 +92,6 @@ def formsemestre_bulletinetud_published_dict( nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) d = {"type": "classic", "version": "0"} - if (not sem["bul_hide_xml"]) or force_publishing: published = True else: @@ -134,6 +133,7 @@ def formsemestre_bulletinetud_published_dict( ) d["etudiant"]["sexe"] = d["etudiant"]["civilite"] # backward compat for our clients # Disponible pour publication ? + d["publie"] = published if not published: return d # stop ! @@ -364,8 +364,35 @@ def formsemestre_bulletinetud_published_dict( return d -def dict_decision_jury(etudid, formsemestre_id, with_decisions=False): - "dict avec decision pour bulletins json" +def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict: + """dict avec decision pour bulletins json + - decision : décision semestre + - decision_ue : list des décisions UE + - situation + + with_decision donne les décision même si bul_show_decision est faux. + + Exemple: + { + 'autorisation_inscription': [{'semestre_id': 4}], + 'decision': {'code': 'ADM', + 'compense_formsemestre_id': None, + 'date': '2022-01-21', + 'etat': 'I'}, + 'decision_ue': [ + { + 'acronyme': 'UE31', + 'code': 'ADM', + 'ects': 16.0, + 'numero': 23, + 'titre': 'Approfondissement métiers', + 'ue_id': 1787 + }, + ... + ], + 'situation': 'Inscrit le 25/06/2021. Décision jury: Validé. UE acquises: ' + 'UE31, UE32. Diplôme obtenu.'} + """ from app.scodoc import sco_bulletins d = {} diff --git a/app/scodoc/sco_formsemestre_validation.py b/app/scodoc/sco_formsemestre_validation.py index 2af209c2a..a9d13c016 100644 --- a/app/scodoc/sco_formsemestre_validation.py +++ b/app/scodoc/sco_formsemestre_validation.py @@ -35,13 +35,17 @@ from app.models.etudiants import Identite import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu -from app import log +from app import db, log from app.comp import res_sem from app.comp.res_compat import NotesTableCompat from app.models import FormSemestre from app.models.notes import etud_has_notes_attente - +from app.models.validations import ( + ScolarAutorisationInscription, + ScolarFormSemestreValidation, +) +from app.models.but_validations import ApcValidationRCUE, ApcValidationAnnee from app.scodoc.sco_exceptions import ScoValueError from app.scodoc.scolog import logdb from app.scodoc.sco_codes_parcours import * @@ -989,28 +993,32 @@ def do_formsemestre_validation_auto(formsemestre_id): def formsemestre_validation_suppress_etud(formsemestre_id, etudid): - """Suppression des decisions de jury pour un etudiant.""" - log("formsemestre_validation_suppress_etud( %s, %s)" % (formsemestre_id, etudid)) - cnx = ndb.GetDBConnexion() - cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - args = {"formsemestre_id": formsemestre_id, "etudid": etudid} - try: - # -- Validation du semestre et des UEs - cursor.execute( - """delete from scolar_formsemestre_validation - where etudid = %(etudid)s and formsemestre_id=%(formsemestre_id)s""", - args, - ) - # -- Autorisations d'inscription - cursor.execute( - """delete from scolar_autorisation_inscription - where etudid = %(etudid)s and origin_formsemestre_id=%(formsemestre_id)s""", - args, - ) - cnx.commit() - except: - cnx.rollback() - raise + """Suppression des décisions de jury pour un étudiant/formsemestre. + Efface toutes les décisions enregistrées concernant ce formsemestre et cet étudiant: + code semestre, UEs, autorisations d'inscription + """ + log(f"formsemestre_validation_suppress_etud( {formsemestre_id}, {etudid})") + + # Validations jury classiques (semestres, UEs, autorisations) + for v in ScolarFormSemestreValidation.query.filter_by( + etudid=etudid, formsemestre_id=formsemestre_id + ): + db.session.delete(v) + for v in ScolarAutorisationInscription.query.filter_by( + etudid=etudid, origin_formsemestre_id=formsemestre_id + ): + db.session.delete(v) + # Validations jury spécifiques BUT + for v in ApcValidationRCUE.query.filter_by( + etudid=etudid, formsemestre_id=formsemestre_id + ): + db.session.delete(v) + for v in ApcValidationAnnee.query.filter_by( + etudid=etudid, formsemestre_id=formsemestre_id + ): + db.session.delete(v) + + db.session.commit() sem = sco_formsemestre.get_formsemestre(formsemestre_id) _invalidate_etud_formation_caches( diff --git a/app/static/js/releve-but.js b/app/static/js/releve-but.js index edfdf79f1..7d3bc985e 100644 --- a/app/static/js/releve-but.js +++ b/app/static/js/releve-but.js @@ -224,9 +224,26 @@ class releveBUT extends HTMLElement {
Non justifiées
${data.semestre.absences?.injustifie ?? "-"}
Total
${data.semestre.absences?.total ?? "-"}
- - photo de l'étudiant - `; + `; + if(data.semestre.decision_rcue?.length){ + output += ` +
+
RCUE
+ ${(()=>{ + let output = ""; + data.semestre.decision_rcue.forEach(competence=>{ + output += `
${competence.niveau.competence.titre}
${competence.code}
`; + }) + return output; + })()} +
+ ` + } + + output += ` + + photo de l'étudiant + `; /*${data.semestre.groupes.map(groupe => { return `
@@ -240,9 +257,11 @@ class releveBUT extends HTMLElement { }).join("") }*/ this.shadow.querySelector(".infoSemestre").innerHTML = output; - if(data.semestre.decision_annee?.code){ + + + /*if(data.semestre.decision_annee?.code){ this.shadow.querySelector(".decision_annee").innerHTML = "Décision année : " + data.semestre.decision_annee.code + " - " + correspondanceCodes[data.semestre.decision_annee.code]; - } + }*/ this.shadow.querySelector(".decision").innerHTML = data.semestre.situation || ""; /*if (data.semestre.decision?.code) { diff --git a/app/templates/but/documentation_codes_jury.html b/app/templates/but/documentation_codes_jury.html index dbaf51ca2..abd3a7422 100644 --- a/app/templates/but/documentation_codes_jury.html +++ b/app/templates/but/documentation_codes_jury.html @@ -258,7 +258,7 @@
  • Bulletin officiel spécial n°4 du 17 juin 2021
  • version + href="https://cache.media.enseignementsup-recherche.gouv.fr//file/SPE4-MESRI-17-6-2021/19/4/SP4_ESR_17_6_2021_1413194.pdf">Version pdf complète
  • diff --git a/app/views/notes.py b/app/views/notes.py index 11b794810..c1715c927 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -57,7 +57,7 @@ from app.models.ues import UniteEns from app import api from app import db from app import models -from app.models import ScolarNews +from app.models import ScolarNews, but_validations from app.auth.models import User from app.but import apc_edit_ue, jury_but_recap from app.decorators import ( @@ -71,7 +71,7 @@ from app.views import notes_bp as bp # --------------- -from app.scodoc import sco_utils as scu +from app.scodoc import sco_bulletins_json, sco_utils as scu from app.scodoc import notesdb as ndb from app import log, send_scodoc_alarm @@ -2143,6 +2143,16 @@ def formsemestre_validation_etud_form( ): "Formulaire choix jury pour un étudiant" readonly = not sco_permissions_check.can_validate_sem(formsemestre_id) + formsemestre = FormSemestre.query.get_or_404(formsemestre_id) + if formsemestre.formation.is_apc(): + return redirect( + url_for( + "notes.formsemestre_validation_but", + scodoc_dept=g.scodoc_dept, + formsemestre_id=formsemestre_id, + etudid=etudid, + ) + ) return sco_formsemestre_validation.formsemestre_validation_etud_form( formsemestre_id, etudid=etudid, @@ -2515,51 +2525,68 @@ def do_formsemestre_validation_auto(formsemestre_id): def formsemestre_validation_suppress_etud( formsemestre_id, etudid, dialog_confirmed=False ): - """Suppression des decisions de jury pour un etudiant.""" + """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: - etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] - formsemestre = FormSemestre.query.get_or_404(formsemestre_id) - sem = formsemestre.to_dict() - nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) - decision_jury = nt.get_etud_decision_sem(etudid) - if decision_jury: - existing = ( - "

    Décision existante: %(code)s du %(event_date)s

    " % decision_jury - ) + d = sco_bulletins_json.dict_decision_jury( + etudid, formsemestre_id, with_decisions=True + ) + d.update(but_validations.dict_decision_jury(etud, formsemestre)) + + 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: - existing = "" + descr_annee = "-" + + existing = f""" + + """ return scu.confirm_dialog( - """

    Confirmer la suppression des décisions du semestre %s (%s - %s) pour %s ?

    %s -

    Cette opération est irréversible. -

    - """ - % ( - sem["titre_num"], - sem["date_debut"], - sem["date_fin"], - etud["nomprenom"], - existing, - ), + f"""

    Confirmer la suppression des décisions du semestre + {formsemestre.titre_mois()} pour {etud.nomprenom} +

    +

    Cette opération est irréversible.

    +
    + {existing} +
    + """, OK="Supprimer", dest_url="", - cancel_url="formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s" - % (formsemestre_id, etudid), + cancel_url=next_url, parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, ) sco_formsemestre_validation.formsemestre_validation_suppress_etud( formsemestre_id, etudid ) - return flask.redirect( - scu.ScoURL() - + "/Notes/formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&head_message=Décision%%20supprimée" - % (formsemestre_id, etudid) - ) + flash("Décisions supprimées") + return flask.redirect(next_url) # ------------- PV de JURY et archives diff --git a/sco_version.py b/sco_version.py index ed3c66a32..256794b73 100755 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.3.15" +SCOVERSION = "9.3.16" SCONAME = "ScoDoc" diff --git a/tests/api/exemple-api-basic.py b/tests/api/exemple-api-basic.py index a581a8783..50b4a11f0 100644 --- a/tests/api/exemple-api-basic.py +++ b/tests/api/exemple-api-basic.py @@ -33,7 +33,7 @@ except NameError: load_dotenv(os.path.join(BASEDIR, ".env")) CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False))) -SCODOC_URL = os.environ["SCODOC_URL"] or "http://localhost:5000" +SCODOC_URL = os.environ.get("SCODOC_URL") or "http://localhost:5000" API_URL = SCODOC_URL + "/ScoDoc/api" SCODOC_USER = os.environ["SCODOC_USER"] SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"] @@ -85,13 +85,13 @@ if r.status_code != 200: print(f"{len(r.json())} étudiants courants") # Bulletin d'un BUT -formsemestre_id = 1052 # A adapter -etudid = 16400 +formsemestre_id = 1063 # A adapter +etudid = 16450 bul = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin") # d'un DUT -formsemestre_id = 1028 # A adapter -etudid = 14721 +formsemestre_id = 1062 # A adapter +etudid = 16309 bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")