Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
12 changed files with 180 additions and 94 deletions
Showing only changes of commit 96420c534f - Show all commits

View File

@ -345,8 +345,8 @@ class BulletinBUT:
- Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
(bulletins non publiés).
"""
if version not in scu.BULLETINS_VERSIONS:
raise ScoValueError("version de bulletin demandée invalide")
if version not in scu.BULLETINS_VERSIONS_BUT:
raise ScoValueError("bulletin_etud: version de bulletin demandée invalide")
res = self.res
formsemestre = res.formsemestre
etat_inscription = etud.inscription_etat(formsemestre.id)

View File

@ -65,6 +65,42 @@ def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"):
)
if not formsemestre.formation.is_apc():
raise ScoValueError("formation non BUT")
args = _build_bulletin_but_infos(etud, formsemestre, fmt=fmt)
if fmt == "pdf":
filename = scu.bul_filename(formsemestre, etud, prefix="bul-but")
bul_pdf = bulletin_but_court_pdf.make_bulletin_but_court_pdf(args)
return scu.sendPDFFile(bul_pdf, filename + ".pdf")
return render_template(
"but/bulletin_court_page.j2",
datetime=datetime,
sco=ScoData(formsemestre=formsemestre, etud=etud),
time=time,
version="butcourt",
**args,
)
def bulletin_but_court_pdf_frag(
etud: Identite, formsemestre: FormSemestre, stand_alone=False
) -> bytes:
"""Le code PDF d'un bulletin BUT court, à intégrer dans un document
(pour les classeurs de tous les bulletins)
"""
args = _build_bulletin_but_infos(etud, formsemestre)
return bulletin_but_court_pdf.make_bulletin_but_court_pdf(
args, stand_alone=stand_alone
)
def _build_bulletin_but_infos(
etud: Identite, formsemestre: FormSemestre, fmt="pdf"
) -> dict:
"""Réuni toutes les information pour le contenu d'un bulletin BUT court.
On indique le format ("html" ou "pdf") car il y a moins d'infos en HTML.
"""
bulletins_sem = BulletinBUT(formsemestre)
if fmt == "pdf":
bul: dict = bulletins_sem.bulletin_etud_complet(etud)
@ -106,16 +142,4 @@ def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"):
if ue.type == UE_STANDARD and ue.acronyme in ue_acronyms
],
}
if fmt == "pdf":
filename = scu.bul_filename(formsemestre, etud, prefix="bul-but")
bul_pdf = bulletin_but_court_pdf.make_bulletin_but_court_pdf(**args)
return scu.sendPDFFile(bul_pdf, filename + ".pdf")
return render_template(
"but/bulletin_court_page.j2",
datetime=datetime,
sco=ScoData(formsemestre=formsemestre, etud=etud),
time=time,
version="butcourt",
**args,
)
return args

View File

@ -34,25 +34,32 @@ from app.scodoc.sco_preferences import SemPreferences
def make_bulletin_but_court_pdf(
bul: dict = None,
cursus: cursus_but.EtudCursusBUT = None,
decision_ues: dict = None,
ects_total: float = 0.0,
etud: Identite = None,
formsemestre: FormSemestre = None,
logo: Logo = None,
prefs: SemPreferences = None,
title: str = "",
ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None,
ues_acronyms: list[str] = None,
args: dict,
stand_alone: bool = True,
) -> bytes:
"génère le bulletin court BUT en pdf"
"""génère le bulletin court BUT en pdf.
Si stand_alone, génère un doc pdf complet (une page ici),
sinon un morceau (fragment) à intégrer dans un autre document.
args donne toutes les infos du contenu du bulletin:
bul: dict = None,
cursus: cursus_but.EtudCursusBUT = None,
decision_ues: dict = None,
ects_total: float = 0.0,
etud: Identite = None,
formsemestre: FormSemestre = None,
logo: Logo = None,
prefs: SemPreferences = None,
title: str = "",
ue_validation_by_niveau: dict[tuple[int, str], ScolarFormSemestreValidation] = None,
ues_acronyms: list[str] = None,
"""
# A priori ce verrou n'est plus nécessaire avec Flask (multi-process)
# mais...
try:
PDFLOCK.acquire()
bul_generator = BulletinGeneratorBUTCourt(**locals())
bul_pdf = bul_generator.generate(fmt="pdf")
bul_generator = BulletinGeneratorBUTCourt(**args)
bul_pdf = bul_generator.generate(fmt="pdf", stand_alone=stand_alone)
finally:
PDFLOCK.release()
return bul_pdf

View File

@ -346,7 +346,9 @@ def do_formsemestre_archive(
)
if bul_version not in scu.BULLETINS_VERSIONS:
raise ScoValueError("version de bulletin demandée invalide")
raise ScoValueError(
"do_formsemestre_archive: version de bulletin demandée invalide"
)
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
sem_archive_id = formsemestre_id

View File

@ -985,6 +985,8 @@ def do_formsemestre_bulletinetud(
bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
et filigranne est un message à placer en "filigranne" (eg "Provisoire").
"""
from app.but import bulletin_but_court
fmt = fmt or "html"
if fmt == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
@ -1045,9 +1047,19 @@ def do_formsemestre_bulletinetud(
if not can_send_bulletin_by_mail(formsemestre.id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletin_etud(
bul_dict, etud=etud, formsemestre=formsemestre, version=version, fmt="pdf"
)
if version == "butcourt":
pdfdata = bulletin_but_court.bulletin_but_court_pdf_frag(
etud, formsemestre, stand_alone=True
)
filename = scu.bul_filename(formsemestre, etud, prefix="bul-court")
else:
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletin_etud(
bul_dict,
etud=etud,
formsemestre=formsemestre,
version=version,
fmt="pdf",
)
if prefer_mail_perso:
recipient_addr = (

View File

@ -61,7 +61,6 @@ from flask import g, request
from app import log, ScoValueError
from app.models import FormSemestre, Identite
from app.scodoc import sco_cache
from app.scodoc import codes_cursus
from app.scodoc import sco_pdf
@ -213,23 +212,35 @@ def process_field(
def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
"Document pdf avec tous les bulletins du semestre, et filename"
from app.but import bulletin_but_court
from app.scodoc import sco_bulletins
if version not in scu.BULLETINS_VERSIONS:
raise ScoValueError("version de bulletin demandée invalide")
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
versions = (
scu.BULLETINS_VERSIONS_BUT
if formsemestre.formation.is_apc()
else scu.BULLETINS_VERSIONS
)
if version not in versions:
raise ScoValueError(
"get_formsemestre_bulletins_pdf: version de bulletin demandée invalide !"
)
cached = sco_cache.SemBulletinsPDFCache.get(str(formsemestre_id) + "_" + version)
if cached:
return cached[1], cached[0]
fragments = []
# Make each bulletin
formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
frag, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre,
etud,
fmt="pdfpart",
version=version,
)
if version == "butcourt":
frag = bulletin_but_court.bulletin_but_court_pdf_frag(etud, formsemestre)
else:
frag, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre,
etud,
fmt="pdfpart",
version=version,
)
fragments += frag
#
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}

View File

@ -190,7 +190,7 @@ class SemBulletinsPDFCache(ScoDocCache):
@classmethod
def invalidate_sems(cls, formsemestre_ids):
"""Clear cached pdf for all given formsemestres"""
for version in scu.BULLETINS_VERSIONS:
for version in scu.BULLETINS_VERSIONS_BUT:
oids = [
str(formsemestre_id) + "_" + version
for formsemestre_id in formsemestre_ids

View File

@ -157,11 +157,24 @@ def formsemestre_recapcomplet(
H.append(f'<option value="{fmt}"{selected}>{label}</option>')
H.append(
f"""
</select>&nbsp;(cliquer sur un nom pour afficher son bulletin ou
</select>&nbsp;<span class="help">cliquer sur un nom pour afficher son bulletin ou
<a class="stdlink"
href="{url_for('notes.formsemestre_bulletins_pdf',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
}">ici avoir le classeur papier</a>)
}">ici avoir le classeur pdf</a>
"""
)
if formsemestre.formation.is_apc():
H.append(
f"""&nbsp;ou en <a class="stdlink"
href="{url_for('notes.formsemestre_bulletins_pdf',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id, version="butcourt")
}">version courte BUT</a>
"""
)
H.append(
"""</span>
</form>
"""
)

View File

@ -69,7 +69,10 @@ function showSums(sumsRessources, sumsUE) {
--nbX:1;
--nbY:1;
">
${value / 100}
${(value / 100).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})}
</div>`;
});
@ -86,7 +89,10 @@ function showSums(sumsRessources, sumsUE) {
--nbX:1;
--nbY:1;
">
${value / 100}
${(value / 100).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})}
</div>`;
});
@ -223,7 +229,10 @@ function processSums() {
});
document.querySelector(
`.sums[data-x="${this.dataset.x}"][data-y="${lastY}"]`
).innerText = sum / 100;
).innerText = (sum / 100).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
});
sum = 0;
document
@ -238,7 +247,10 @@ function processSums() {
});
document.querySelector(
`.sums[data-x="${lastX}"][data-y="${this.dataset.y}"]`
).innerText = sum / 100;
).innerText = (sum / 100).toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
});
}
/******************************/

View File

@ -52,7 +52,7 @@
restreindre l'affichage aux UE et modules de l'un des parcours à l'aide du
menu "Parcours" au dessus du tableau. Les UEs et modules de tronc commun
apparaissent toujours. </p>
<p>Les cases grisées à droite et en bas donnent la somme des coefficients.</p>
<p>Les cases grisées à droite et en bas donnent la somme indicative des coefficients.</p>
</div>
<div class="tableau_legende">

View File

@ -271,9 +271,7 @@ sco_publish(
)
@bp.route(
"/formsemestre_bulletinetud", methods=["GET", "POST"]
) # POST pour compat anciens clients PHP (deprecated)
@bp.route("/formsemestre_bulletinetud")
@scodoc
@permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func
@ -290,7 +288,9 @@ def formsemestre_bulletinetud(
):
fmt = fmt or "html"
if version not in scu.BULLETINS_VERSIONS_BUT:
raise ScoValueError("version de bulletin demandée invalide")
raise ScoValueError(
"formsemestre_bulletinetud: version de bulletin demandée invalide"
)
if not isinstance(etudid, int):
raise ScoInvalidIdType("formsemestre_bulletinetud: etudid must be an integer !")
if formsemestre_id is not None and not isinstance(formsemestre_id, int):
@ -1863,8 +1863,6 @@ sco_publish(
@scodoc7func
def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
"Publie les bulletins dans un classeur PDF"
if version not in scu.BULLETINS_VERSIONS:
raise ScoValueError("version de bulletin demandée invalide")
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, version=version
)
@ -1885,13 +1883,14 @@ _EXPL_BULL = """Versions des bulletins:
@scodoc7func
def formsemestre_bulletins_pdf_choice(formsemestre_id, version=None):
"""Choix version puis envoi classeur bulletins pdf"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if version:
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, version=version
)
return scu.sendPDFFile(pdfdoc, filename)
return formsemestre_bulletins_choice(
formsemestre_id,
return _formsemestre_bulletins_choice(
formsemestre,
title="Choisir la version des bulletins à générer",
explanation=_EXPL_BULL,
)
@ -1904,7 +1903,7 @@ def formsemestre_bulletins_pdf_choice(formsemestre_id, version=None):
def etud_bulletins_pdf(etudid, version="selectedevals"):
"Publie tous les bulletins d'un etudiants dans un classeur PDF"
if version not in scu.BULLETINS_VERSIONS:
raise ScoValueError("version de bulletin demandée invalide")
raise ScoValueError("etud_bulletins_pdf: version de bulletin demandée invalide")
pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf(etudid, version=version)
return scu.sendPDFFile(pdfdoc, filename)
@ -1931,15 +1930,20 @@ def formsemestre_bulletins_mailetuds_choice(
prefer_mail_perso=prefer_mail_perso,
)
)
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
expl_bull = """Versions des bulletins:
<ul>
<li><b>courte</b>: moyennes des modules</li>
<li><b>intermédiaire</b>: moyennes des modules et notes des évaluations sélectionnées</li>
<li><b>complète</b>: toutes les notes</li>
<ul>"""
return formsemestre_bulletins_choice(
formsemestre_id,
"""
if formsemestre.formation.is_apc():
expl_bull += """
<li><b>courte spéciale BUT</b>: un résumé en une page pour les BUTs</li>
"""
expl_bull += "</ul>"
return _formsemestre_bulletins_choice(
formsemestre,
title="Choisir la version des bulletins à envoyer par mail",
explanation="""Chaque étudiant (non démissionnaire ni défaillant)
ayant une adresse mail connue de ScoDoc
@ -1951,23 +1955,24 @@ def formsemestre_bulletins_mailetuds_choice(
# not published
def formsemestre_bulletins_choice(
formsemestre_id, title="", explanation="", choose_mail=False
def _formsemestre_bulletins_choice(
formsemestre: FormSemestre, title="", explanation="", choose_mail=False
):
"""Choix d'une version de bulletin"""
versions = (
scu.BULLETINS_VERSIONS_BUT
if formsemestre.formation.is_apc()
else scu.BULLETINS_VERSIONS
)
H = [
html_sco_header.html_sem_header(title),
f"""
<form name="f" method="GET" action="{request.base_url}">
<input type="hidden" name="formsemestre_id" value="{formsemestre_id}"></input>
<input type="hidden" name="formsemestre_id" value="{formsemestre.id}"></input>
""",
]
H.append("""<select name="version" class="noprint">""")
for version, description in (
("short", "Version courte"),
("selectedevals", "Version intermédiaire"),
("long", "Version complète"),
):
for version, description in versions.items():
H.append(f"""<option value="{version}">{description}</option>""")
H.append("""</select>&nbsp;&nbsp;<input type="submit" value="Générer"/>""")

View File

@ -488,7 +488,8 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
},
),
]
# Si SuperAdmin, propose de choisir librement le dept du nouvel utilisateur
# Si on a le droit d'administrer les utilisateurs de plusieurs départements,
# propose le choix du dept du nouvel utilisateur
selectable_dept_acronyms = set(administrable_dept_acronyms)
if edit:
if the_user.dept is not None: # ajoute dept actuel de l'utilisateur
@ -500,7 +501,7 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
if g.scodoc_dept in selectable_dept_acronyms
else (auth_dept or "")
)
if is_super_admin and len(selectable_dept_acronyms) > 1:
if len(selectable_dept_acronyms) > 1:
selectable_dept_acronyms = sorted(list(selectable_dept_acronyms))
descr.append(
(
@ -685,9 +686,8 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
if "status" in vals:
vals["active"] = vals["status"] == ""
# Département:
if auth_dept: # pas super-admin
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
del vals["dept"] # ne change pas de dept
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
del vals["dept"] # ne change pas de dept
# Traitement des roles: ne doit pas affecter les rôles
# que l'on en contrôle pas:
for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... }