fix des conflits

This commit is contained in:
Léonard Montalbano 2022-07-07 14:47:06 +02:00
commit 1fd296179d
12 changed files with 160 additions and 82 deletions

View File

@ -473,7 +473,7 @@ def etudiant_bulletin_semestre( # XXX TODO Ajouter la possibilité de retourner
return response return response
return sco_bulletins.get_formsemestre_bulletin_etud_json( return sco_bulletins.get_formsemestre_bulletin_etud_json(
formsemestre, etud, version formsemestre, etud, version=version
) )

View File

@ -650,7 +650,7 @@ class ResultatsSemestre(ResultatsCache):
elif nb_ues_validables < len(ues_sans_bonus): elif nb_ues_validables < len(ues_sans_bonus):
row["_ues_validables_class"] += " moy_inf" row["_ues_validables_class"] += " moy_inf"
row["_ues_validables_order"] = nb_ues_validables # pour tri 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) dec_sem = self.validations.decisions_jury.get(etudid)
jury_code_sem = dec_sem["code"] if dec_sem else "" jury_code_sem = dec_sem["code"] if dec_sem else ""
idx = add_cell( idx = add_cell(

View File

@ -451,6 +451,7 @@ class ApcAppCritique(db.Model, XMLModel):
if competence is not None: if competence is not None:
query = query.filter(ApcNiveau.competence == competence) query = query.filter(ApcNiveau.competence == competence)
return query return query
<<<<<<< HEAD
def __init__(self, id, niveau_id, code, libelle, modules): def __init__(self, id, niveau_id, code, libelle, modules):
self.id = id self.id = id
@ -458,6 +459,8 @@ class ApcAppCritique(db.Model, XMLModel):
self.code = code self.code = code
self.libelle = libelle self.libelle = libelle
self.modules = modules self.modules = modules
=======
>>>>>>> 7c340c798ad59c41653efc83bfd079f11fce1938
def to_dict(self) -> dict: def to_dict(self) -> dict:
return {"libelle": self.libelle} return {"libelle": self.libelle}
@ -544,11 +547,14 @@ class ApcAnneeParcours(db.Model, XMLModel):
) )
ordre = db.Column(db.Integer) ordre = db.Column(db.Integer)
"numéro de l'année: 1, 2, 3" "numéro de l'année: 1, 2, 3"
<<<<<<< HEAD
def __init__(self, id, parcours_id, ordre): def __init__(self, id, parcours_id, ordre):
self.id = id self.id = id
self.parcours_id = parcours_id self.parcours_id = parcours_id
self.ordre = ordre self.ordre = ordre
=======
>>>>>>> 7c340c798ad59c41653efc83bfd079f11fce1938
def __repr__(self): def __repr__(self):
return f"<{self.__class__.__name__} ordre={self.ordre!r} parcours={self.parcours.code!r}>" return f"<{self.__class__.__name__} ordre={self.ordre!r} parcours={self.parcours.code!r}>"

View File

@ -43,6 +43,7 @@ class ApcValidationRCUE(db.Model):
formsemestre_id = db.Column( formsemestre_id = db.Column(
db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True db.Integer, db.ForeignKey("notes_formsemestre.id"), index=True, nullable=True
) )
"formsemestre pair du RCUE"
# Les deux UE associées à ce niveau: # Les deux UE associées à ce niveau:
ue1_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False) 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) ue2_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False)

View File

@ -58,7 +58,6 @@ from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre from app.scodoc import sco_formsemestre
from app.scodoc import sco_groups from app.scodoc import sco_groups
from app.scodoc import sco_permissions_check from app.scodoc import sco_permissions_check
from app.scodoc import sco_photos
from app.scodoc import sco_preferences from app.scodoc import sco_preferences
from app.scodoc import sco_pvjury from app.scodoc import sco_pvjury
from app.scodoc import sco_users 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 from app.scodoc.sco_utils import ModuleType, fmt_note
import app.scodoc.notesdb as ndb 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( def get_formsemestre_bulletin_etud_json(
formsemestre: FormSemestre, formsemestre: FormSemestre,

View File

@ -92,7 +92,6 @@ def formsemestre_bulletinetud_published_dict(
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
d = {"type": "classic", "version": "0"} d = {"type": "classic", "version": "0"}
if (not sem["bul_hide_xml"]) or force_publishing: if (not sem["bul_hide_xml"]) or force_publishing:
published = True published = True
else: else:
@ -134,6 +133,7 @@ def formsemestre_bulletinetud_published_dict(
) )
d["etudiant"]["sexe"] = d["etudiant"]["civilite"] # backward compat for our clients d["etudiant"]["sexe"] = d["etudiant"]["civilite"] # backward compat for our clients
# Disponible pour publication ? # Disponible pour publication ?
d["publie"] = published
if not published: if not published:
return d # stop ! return d # stop !
@ -364,8 +364,35 @@ def formsemestre_bulletinetud_published_dict(
return d return d
def dict_decision_jury(etudid, formsemestre_id, with_decisions=False): def dict_decision_jury(etudid, formsemestre_id, with_decisions=False) -> dict:
"dict avec decision pour bulletins json" """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 from app.scodoc import sco_bulletins
d = {} d = {}

View File

@ -35,13 +35,17 @@ from app.models.etudiants import Identite
import app.scodoc.notesdb as ndb import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu 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 import res_sem
from app.comp.res_compat import NotesTableCompat from app.comp.res_compat import NotesTableCompat
from app.models import FormSemestre from app.models import FormSemestre
from app.models.notes import etud_has_notes_attente 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.sco_exceptions import ScoValueError
from app.scodoc.scolog import logdb from app.scodoc.scolog import logdb
from app.scodoc.sco_codes_parcours import * 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): def formsemestre_validation_suppress_etud(formsemestre_id, etudid):
"""Suppression des decisions de jury pour un etudiant.""" """Suppression des décisions de jury pour un étudiant/formsemestre.
log("formsemestre_validation_suppress_etud( %s, %s)" % (formsemestre_id, etudid)) Efface toutes les décisions enregistrées concernant ce formsemestre et cet étudiant:
cnx = ndb.GetDBConnexion() code semestre, UEs, autorisations d'inscription
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) """
args = {"formsemestre_id": formsemestre_id, "etudid": etudid} log(f"formsemestre_validation_suppress_etud( {formsemestre_id}, {etudid})")
try:
# -- Validation du semestre et des UEs # Validations jury classiques (semestres, UEs, autorisations)
cursor.execute( for v in ScolarFormSemestreValidation.query.filter_by(
"""delete from scolar_formsemestre_validation etudid=etudid, formsemestre_id=formsemestre_id
where etudid = %(etudid)s and formsemestre_id=%(formsemestre_id)s""", ):
args, db.session.delete(v)
) for v in ScolarAutorisationInscription.query.filter_by(
# -- Autorisations d'inscription etudid=etudid, origin_formsemestre_id=formsemestre_id
cursor.execute( ):
"""delete from scolar_autorisation_inscription db.session.delete(v)
where etudid = %(etudid)s and origin_formsemestre_id=%(formsemestre_id)s""", # Validations jury spécifiques BUT
args, for v in ApcValidationRCUE.query.filter_by(
) etudid=etudid, formsemestre_id=formsemestre_id
cnx.commit() ):
except: db.session.delete(v)
cnx.rollback() for v in ApcValidationAnnee.query.filter_by(
raise etudid=etudid, formsemestre_id=formsemestre_id
):
db.session.delete(v)
db.session.commit()
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id)
_invalidate_etud_formation_caches( _invalidate_etud_formation_caches(

View File

@ -224,9 +224,26 @@ class releveBUT extends HTMLElement {
<div class=abs>Non justifiées</div> <div class=abs>Non justifiées</div>
<div>${data.semestre.absences?.injustifie ?? "-"}</div> <div>${data.semestre.absences?.injustifie ?? "-"}</div>
<div class=abs>Total</div><div>${data.semestre.absences?.total ?? "-"}</div> <div class=abs>Total</div><div>${data.semestre.absences?.total ?? "-"}</div>
</div> </div>`;
<a class=photo href="${data.etudiant.fiche_url}"><img src="${data.etudiant.photo_url || "default_Student.svg"}" alt="photo de l'étudiant" title="fiche de l'étudiant" height="120" border="0"></a> if(data.semestre.decision_rcue?.length){
`; output += `
<div>
<div class=enteteSemestre>RCUE</div><div></div>
${(()=>{
let output = "";
data.semestre.decision_rcue.forEach(competence=>{
output += `<div class=rang>${competence.niveau.competence.titre}</div><div>${competence.code}</div>`;
})
return output;
})()}
</div>
</div>`
}
output += `
<a class=photo href="${data.etudiant.fiche_url}">
<img src="${data.etudiant.photo_url || "default_Student.svg"}" alt="photo de l'étudiant" title="fiche de l'étudiant" height="120" border="0">
</a>`;
/*${data.semestre.groupes.map(groupe => { /*${data.semestre.groupes.map(groupe => {
return ` return `
<div> <div>
@ -240,9 +257,11 @@ class releveBUT extends HTMLElement {
}).join("") }).join("")
}*/ }*/
this.shadow.querySelector(".infoSemestre").innerHTML = output; 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_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 || ""; this.shadow.querySelector(".decision").innerHTML = data.semestre.situation || "";
/*if (data.semestre.decision?.code) { /*if (data.semestre.decision?.code) {

View File

@ -258,7 +258,7 @@
<li><a href="https://www.enseignementsup-recherche.gouv.fr/fr/bo/21/Special4/ESRS2114777A.htm">Bulletin <li><a href="https://www.enseignementsup-recherche.gouv.fr/fr/bo/21/Special4/ESRS2114777A.htm">Bulletin
officiel spécial n°4 du 17 juin 2021</a></li> officiel spécial n°4 du 17 juin 2021</a></li>
<li><a <li><a
href="https://cache.media.enseignementsup-recherche.gouv.fr//file/SPE4-MESRI-17-6-2021/19/4/SP4_ESR_17_6_2021_1413194.pdf">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</a></li> pdf complète</a></li>
</ul> </ul>

View File

@ -57,7 +57,7 @@ from app.models.ues import UniteEns
from app import api from app import api
from app import db from app import db
from app import models from app import models
from app.models import ScolarNews from app.models import ScolarNews, but_validations
from app.auth.models import User from app.auth.models import User
from app.but import apc_edit_ue, jury_but_recap from app.but import apc_edit_ue, jury_but_recap
from app.decorators import ( 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.scodoc import notesdb as ndb
from app import log, send_scodoc_alarm from app import log, send_scodoc_alarm
@ -2143,6 +2143,16 @@ def formsemestre_validation_etud_form(
): ):
"Formulaire choix jury pour un étudiant" "Formulaire choix jury pour un étudiant"
readonly = not sco_permissions_check.can_validate_sem(formsemestre_id) 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( return sco_formsemestre_validation.formsemestre_validation_etud_form(
formsemestre_id, formsemestre_id,
etudid=etudid, etudid=etudid,
@ -2515,51 +2525,68 @@ def do_formsemestre_validation_auto(formsemestre_id):
def formsemestre_validation_suppress_etud( def formsemestre_validation_suppress_etud(
formsemestre_id, etudid, dialog_confirmed=False 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): if not sco_permissions_check.can_validate_sem(formsemestre_id):
return scu.confirm_dialog( return scu.confirm_dialog(
message="<p>Opération non autorisée pour %s</h2>" % current_user, message="<p>Opération non autorisée pour %s</h2>" % current_user,
dest_url=scu.ScoURL(), 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: if not dialog_confirmed:
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0] d = sco_bulletins_json.dict_decision_jury(
formsemestre = FormSemestre.query.get_or_404(formsemestre_id) etudid, formsemestre_id, with_decisions=True
sem = formsemestre.to_dict() )
nt: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) d.update(but_validations.dict_decision_jury(etud, formsemestre))
decision_jury = nt.get_etud_decision_sem(etudid)
if decision_jury: descr_ues = [f"{u['acronyme']}: {u['code']}" for u in d.get("decision_ue", [])]
existing = ( dec_annee = d.get("decision_annee")
"<p>Décision existante: %(code)s du %(event_date)s</p>" % decision_jury if dec_annee:
) descr_annee = dec_annee.get("code", "-")
else: else:
existing = "" descr_annee = "-"
existing = f"""
<ul>
<li>Semestre : {d.get("decision", {"code":"-"})['code'] or "-"}</li>
<li>Année BUT: {descr_annee}</li>
<li>UEs : {", ".join(descr_ues)}</li>
<li>RCUEs: {len(d.get("decision_rcue", []))} décisions</li>
</ul>
"""
return scu.confirm_dialog( return scu.confirm_dialog(
"""<h2>Confirmer la suppression des décisions du semestre %s (%s - %s) pour %s ?</h2>%s f"""<h2>Confirmer la suppression des décisions du semestre
<p>Cette opération est irréversible. {formsemestre.titre_mois()} pour {etud.nomprenom}
</p> </h2>
""" <p>Cette opération est irréversible.</p>
% ( <div>
sem["titre_num"], {existing}
sem["date_debut"], </div>
sem["date_fin"], """,
etud["nomprenom"],
existing,
),
OK="Supprimer", OK="Supprimer",
dest_url="", dest_url="",
cancel_url="formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s" cancel_url=next_url,
% (formsemestre_id, etudid),
parameters={"etudid": etudid, "formsemestre_id": formsemestre_id}, parameters={"etudid": etudid, "formsemestre_id": formsemestre_id},
) )
sco_formsemestre_validation.formsemestre_validation_suppress_etud( sco_formsemestre_validation.formsemestre_validation_suppress_etud(
formsemestre_id, etudid formsemestre_id, etudid
) )
return flask.redirect( flash("Décisions supprimées")
scu.ScoURL() return flask.redirect(next_url)
+ "/Notes/formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&head_message=Décision%%20supprimée"
% (formsemestre_id, etudid)
)
# ------------- PV de JURY et archives # ------------- PV de JURY et archives

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.3.15" SCOVERSION = "9.3.16"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -33,7 +33,7 @@ except NameError:
load_dotenv(os.path.join(BASEDIR, ".env")) load_dotenv(os.path.join(BASEDIR, ".env"))
CHK_CERT = bool(int(os.environ.get("CHECK_CERTIFICATE", False))) 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" API_URL = SCODOC_URL + "/ScoDoc/api"
SCODOC_USER = os.environ["SCODOC_USER"] SCODOC_USER = os.environ["SCODOC_USER"]
SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"] SCODOC_PASSWORD = os.environ["SCODOC_PASSWORD"]
@ -85,13 +85,13 @@ if r.status_code != 200:
print(f"{len(r.json())} étudiants courants") print(f"{len(r.json())} étudiants courants")
# Bulletin d'un BUT # Bulletin d'un BUT
formsemestre_id = 1052 # A adapter formsemestre_id = 1063 # A adapter
etudid = 16400 etudid = 16450
bul = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin") bul = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")
# d'un DUT # d'un DUT
formsemestre_id = 1028 # A adapter formsemestre_id = 1062 # A adapter
etudid = 14721 etudid = 16309
bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin") bul_dut = GET(f"/etudiant/etudid/{etudid}/formsemestre/{formsemestre_id}/bulletin")