Merge branch 'prod' of https://scodoc.org/git/viennet/ScoDoc into assi_ev

This commit is contained in:
Emmanuel Viennet 2023-12-06 20:44:03 +01:00
commit 96420c534f
12 changed files with 180 additions and 94 deletions

View File

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

View File

@ -18,7 +18,7 @@ Ces données sont des objets passés au template.
- `bul: dict` : le bulletin (dict, même structure que le json publié) - `bul: dict` : le bulletin (dict, même structure que le json publié)
- `cursus: EtudCursusBUT`: infos sur le cursus BUT (niveaux validés etc) - `cursus: EtudCursusBUT`: infos sur le cursus BUT (niveaux validés etc)
- `decision_ues: dict`: `{ acronyme_ue : { 'code' : 'ADM' }}` accès aux décisions - `decision_ues: dict`: `{ acronyme_ue : { 'code' : 'ADM' }}` accès aux décisions
de jury d'UE de jury d'UE
- `ects_total` : nombre d'ECTS validées dans ce cursus - `ects_total` : nombre d'ECTS validées dans ce cursus
- `ue_validation_by_niveau : dict` : les validations d'UE de chaque niveau du cursus - `ue_validation_by_niveau : dict` : les validations d'UE de chaque niveau du cursus
""" """
@ -65,6 +65,42 @@ def bulletin_but(formsemestre_id: int, etudid: int = None, fmt="html"):
) )
if not formsemestre.formation.is_apc(): if not formsemestre.formation.is_apc():
raise ScoValueError("formation non BUT") 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) bulletins_sem = BulletinBUT(formsemestre)
if fmt == "pdf": if fmt == "pdf":
bul: dict = bulletins_sem.bulletin_etud_complet(etud) 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 ue.type == UE_STANDARD and ue.acronyme in ue_acronyms
], ],
} }
if fmt == "pdf": return args
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,
)

View File

@ -6,7 +6,7 @@
"""Génération bulletin BUT PDF synthétique en une page """Génération bulletin BUT PDF synthétique en une page
On génère du PDF avec reportLab en utilisant les classes On génère du PDF avec reportLab en utilisant les classes
ScoDoc BulletinGenerator et GenTable. ScoDoc BulletinGenerator et GenTable.
""" """
@ -34,25 +34,32 @@ from app.scodoc.sco_preferences import SemPreferences
def make_bulletin_but_court_pdf( def make_bulletin_but_court_pdf(
bul: dict = None, args: dict,
cursus: cursus_but.EtudCursusBUT = None, stand_alone: bool = True,
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,
) -> bytes: ) -> 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) # A priori ce verrou n'est plus nécessaire avec Flask (multi-process)
# mais... # mais...
try: try:
PDFLOCK.acquire() PDFLOCK.acquire()
bul_generator = BulletinGeneratorBUTCourt(**locals()) bul_generator = BulletinGeneratorBUTCourt(**args)
bul_pdf = bul_generator.generate(fmt="pdf") bul_pdf = bul_generator.generate(fmt="pdf", stand_alone=stand_alone)
finally: finally:
PDFLOCK.release() PDFLOCK.release()
return bul_pdf return bul_pdf

View File

@ -346,7 +346,9 @@ def do_formsemestre_archive(
) )
if bul_version not in scu.BULLETINS_VERSIONS: 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) formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre) res: NotesTableCompat = res_sem.load_formsemestre_results(formsemestre)
sem_archive_id = formsemestre_id sem_archive_id = formsemestre_id
@ -505,7 +507,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
""", """,
] ]
F = [ F = [
f"""<p><em>Note: les documents sont aussi affectés par les réglages sur la page f"""<p><em>Note: les documents sont aussi affectés par les réglages sur la page
"<a class="stdlink" href="{ "<a class="stdlink" href="{
url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept) url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept)
}">Paramétrage</a>" }">Paramétrage</a>"

View File

@ -513,8 +513,8 @@ def _ue_mod_bulletin(
sco_users.user_info(modimpl["responsable_id"])["nomcomplet"], sco_users.user_info(modimpl["responsable_id"])["nomcomplet"],
) )
link_mod = f"""<a class="bull_link" href="{ link_mod = f"""<a class="bull_link" href="{
url_for("notes.moduleimpl_status", url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
moduleimpl_id=modimpl["moduleimpl_id"] moduleimpl_id=modimpl["moduleimpl_id"]
) )
}" title="{mod["mod_descr_txt"]}">""" }" title="{mod["mod_descr_txt"]}">"""
@ -576,7 +576,7 @@ def _ue_mod_bulletin(
"name" "name"
] = f"""{e.description or ""} { ] = f"""{e.description or ""} {
e.descr_date() e.descr_date()
if e.date_debut and not is_complete if e.date_debut and not is_complete
else ""}""" else ""}"""
e_dict["target_html"] = url_for( e_dict["target_html"] = url_for(
"notes.evaluation_listenotes", "notes.evaluation_listenotes",
@ -985,6 +985,8 @@ def do_formsemestre_bulletinetud(
bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json) bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
et filigranne est un message à placer en "filigranne" (eg "Provisoire"). et filigranne est un message à placer en "filigranne" (eg "Provisoire").
""" """
from app.but import bulletin_but_court
fmt = fmt or "html" fmt = fmt or "html"
if fmt == "xml": if fmt == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud( 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): if not can_send_bulletin_by_mail(formsemestre.id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !") raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletin_etud( if version == "butcourt":
bul_dict, etud=etud, formsemestre=formsemestre, version=version, fmt="pdf" 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: if prefer_mail_perso:
recipient_addr = ( recipient_addr = (

View File

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

View File

@ -190,7 +190,7 @@ class SemBulletinsPDFCache(ScoDocCache):
@classmethod @classmethod
def invalidate_sems(cls, formsemestre_ids): def invalidate_sems(cls, formsemestre_ids):
"""Clear cached pdf for all given formsemestres""" """Clear cached pdf for all given formsemestres"""
for version in scu.BULLETINS_VERSIONS: for version in scu.BULLETINS_VERSIONS_BUT:
oids = [ oids = [
str(formsemestre_id) + "_" + version str(formsemestre_id) + "_" + version
for formsemestre_id in formsemestre_ids 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'<option value="{fmt}"{selected}>{label}</option>')
H.append( H.append(
f""" 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" <a class="stdlink"
href="{url_for('notes.formsemestre_bulletins_pdf', href="{url_for('notes.formsemestre_bulletins_pdf',
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id) 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> </form>
""" """
) )

View File

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

View File

@ -33,8 +33,8 @@
</div> </div>
{% endif %} {% endif %}
<div> <div>
<a class="stdlink" href="{{ <a class="stdlink" href="{{
url_for('notes.ue_table', scodoc_dept=g.scodoc_dept, url_for('notes.ue_table', scodoc_dept=g.scodoc_dept,
formation_id=formation.id, semestre_idx=semestre_idx) formation_id=formation.id, semestre_idx=semestre_idx)
}}">revenir à la formation</a> }}">revenir à la formation</a>
</div> </div>
@ -52,7 +52,7 @@
restreindre l'affichage aux UE et modules de l'un des parcours à l'aide du 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 menu "Parcours" au dessus du tableau. Les UEs et modules de tronc commun
apparaissent toujours. </p> 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>
<div class="tableau_legende"> <div class="tableau_legende">
@ -103,7 +103,7 @@
obj.classList.add("modified"); obj.classList.add("modified");
else else
obj.classList.remove("modified"); obj.classList.remove("modified");
// Lorsque les données sont bien enregistrées, on enlève // Lorsque les données sont bien enregistrées, on enlève
// l'indication que c'est bon au bout d'un temps // l'indication que c'est bon au bout d'un temps
//setTimeout(() => { //setTimeout(() => {
// obj.classList.remove("modified"); // obj.classList.remove("modified");
@ -112,4 +112,4 @@
); );
return true; return true;
} }
</script> </script>

View File

@ -271,9 +271,7 @@ sco_publish(
) )
@bp.route( @bp.route("/formsemestre_bulletinetud")
"/formsemestre_bulletinetud", methods=["GET", "POST"]
) # POST pour compat anciens clients PHP (deprecated)
@scodoc @scodoc
@permission_required_compat_scodoc7(Permission.ScoView) @permission_required_compat_scodoc7(Permission.ScoView)
@scodoc7func @scodoc7func
@ -290,7 +288,9 @@ def formsemestre_bulletinetud(
): ):
fmt = fmt or "html" fmt = fmt or "html"
if version not in scu.BULLETINS_VERSIONS_BUT: 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): if not isinstance(etudid, int):
raise ScoInvalidIdType("formsemestre_bulletinetud: etudid must be an integer !") raise ScoInvalidIdType("formsemestre_bulletinetud: etudid must be an integer !")
if formsemestre_id is not None and not isinstance(formsemestre_id, int): if formsemestre_id is not None and not isinstance(formsemestre_id, int):
@ -790,7 +790,7 @@ def formation_import_xml_form():
</li> </li>
<li><a class="stdlink" href="{ <li><a class="stdlink" href="{
url_for("notes.formation_delete", scodoc_dept=g.scodoc_dept, formation_id=formation_id url_for("notes.formation_delete", scodoc_dept=g.scodoc_dept, formation_id=formation_id
)}">Supprimer cette formation</a> )}">Supprimer cette formation</a>
(en cas d'erreur, par exemple pour charger auparavant le référentiel de compétences) (en cas d'erreur, par exemple pour charger auparavant le référentiel de compétences)
</li> </li>
</ul> </ul>
@ -987,7 +987,7 @@ def edit_enseignants_form(moduleimpl_id):
</p> </p>
<p class="help">Pour changer le responsable du module, passez par la <p class="help">Pour changer le responsable du module, passez par la
page "<a class="stdlink" href="{ page "<a class="stdlink" href="{
url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept, url_for("notes.formsemestre_editwithmodules", scodoc_dept=g.scodoc_dept,
formsemestre_id=M["formsemestre_id"]) formsemestre_id=M["formsemestre_id"])
}">Modification du semestre</a>", }">Modification du semestre</a>",
accessible uniquement au responsable de la formation (chef de département) accessible uniquement au responsable de la formation (chef de département)
@ -1200,7 +1200,7 @@ def view_module_abs(moduleimpl_id, fmt="html"):
H = [ H = [
html_sco_header.html_sem_header( html_sco_header.html_sem_header(
f"""Absences du <a href="{ f"""Absences du <a href="{
url_for("notes.moduleimpl_status", url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id scodoc_dept=g.scodoc_dept, moduleimpl_id=moduleimpl_id
)}">module {modimpl.module.titre_str()}</a>""", )}">module {modimpl.module.titre_str()}</a>""",
page_title=f"Absences du module {modimpl.module.titre_str()}", page_title=f"Absences du module {modimpl.module.titre_str()}",
@ -1692,8 +1692,8 @@ def evaluation_delete(evaluation_id):
if etat["nb_notes"]: if etat["nb_notes"]:
H.append( H.append(
f"""<p>Suppression impossible (effacer les notes d'abord)</p> f"""<p>Suppression impossible (effacer les notes d'abord)</p>
<p><a class="stdlink" href="{ <p><a class="stdlink" href="{
url_for("notes.moduleimpl_status", url_for("notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept, moduleimpl_id=evaluation.moduleimpl_id) scodoc_dept=g.scodoc_dept, moduleimpl_id=evaluation.moduleimpl_id)
}">retour au tableau de bord du module</a> }">retour au tableau de bord du module</a>
</p> </p>
@ -1727,7 +1727,7 @@ def evaluation_delete(evaluation_id):
"\n".join(H) "\n".join(H)
+ f"""<p>OK, évaluation supprimée.</p> + f"""<p>OK, évaluation supprimée.</p>
<p><a class="stdlink" href="{ <p><a class="stdlink" href="{
url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept, url_for("notes.moduleimpl_status", scodoc_dept=g.scodoc_dept,
moduleimpl_id=evaluation.moduleimpl_id) moduleimpl_id=evaluation.moduleimpl_id)
}">Continuer</a></p>""" }">Continuer</a></p>"""
+ html_sco_header.sco_footer() + html_sco_header.sco_footer()
@ -1863,8 +1863,6 @@ sco_publish(
@scodoc7func @scodoc7func
def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"): def formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
"Publie les bulletins dans un classeur PDF" "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( pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, version=version formsemestre_id, version=version
) )
@ -1885,13 +1883,14 @@ _EXPL_BULL = """Versions des bulletins:
@scodoc7func @scodoc7func
def formsemestre_bulletins_pdf_choice(formsemestre_id, version=None): def formsemestre_bulletins_pdf_choice(formsemestre_id, version=None):
"""Choix version puis envoi classeur bulletins pdf""" """Choix version puis envoi classeur bulletins pdf"""
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if version: if version:
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf( pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
formsemestre_id, version=version formsemestre_id, version=version
) )
return scu.sendPDFFile(pdfdoc, filename) return scu.sendPDFFile(pdfdoc, filename)
return formsemestre_bulletins_choice( return _formsemestre_bulletins_choice(
formsemestre_id, formsemestre,
title="Choisir la version des bulletins à générer", title="Choisir la version des bulletins à générer",
explanation=_EXPL_BULL, explanation=_EXPL_BULL,
) )
@ -1904,7 +1903,7 @@ def formsemestre_bulletins_pdf_choice(formsemestre_id, version=None):
def etud_bulletins_pdf(etudid, version="selectedevals"): def etud_bulletins_pdf(etudid, version="selectedevals"):
"Publie tous les bulletins d'un etudiants dans un classeur PDF" "Publie tous les bulletins d'un etudiants dans un classeur PDF"
if version not in scu.BULLETINS_VERSIONS: 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) pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf(etudid, version=version)
return scu.sendPDFFile(pdfdoc, filename) return scu.sendPDFFile(pdfdoc, filename)
@ -1931,15 +1930,20 @@ def formsemestre_bulletins_mailetuds_choice(
prefer_mail_perso=prefer_mail_perso, prefer_mail_perso=prefer_mail_perso,
) )
) )
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
expl_bull = """Versions des bulletins: expl_bull = """Versions des bulletins:
<ul> <ul>
<li><b>courte</b>: moyennes des modules</li> <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>intermédiaire</b>: moyennes des modules et notes des évaluations sélectionnées</li>
<li><b>complète</b>: toutes les notes</li> <li><b>complète</b>: toutes les notes</li>
<ul>""" """
return formsemestre_bulletins_choice( if formsemestre.formation.is_apc():
formsemestre_id, 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", title="Choisir la version des bulletins à envoyer par mail",
explanation="""Chaque étudiant (non démissionnaire ni défaillant) explanation="""Chaque étudiant (non démissionnaire ni défaillant)
ayant une adresse mail connue de ScoDoc ayant une adresse mail connue de ScoDoc
@ -1951,23 +1955,24 @@ def formsemestre_bulletins_mailetuds_choice(
# not published # not published
def formsemestre_bulletins_choice( def _formsemestre_bulletins_choice(
formsemestre_id, title="", explanation="", choose_mail=False formsemestre: FormSemestre, title="", explanation="", choose_mail=False
): ):
"""Choix d'une version de bulletin""" """Choix d'une version de bulletin"""
versions = (
scu.BULLETINS_VERSIONS_BUT
if formsemestre.formation.is_apc()
else scu.BULLETINS_VERSIONS
)
H = [ H = [
html_sco_header.html_sem_header(title), html_sco_header.html_sem_header(title),
f""" f"""
<form name="f" method="GET" action="{request.base_url}"> <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">""") H.append("""<select name="version" class="noprint">""")
for version, description in ( for version, description in versions.items():
("short", "Version courte"),
("selectedevals", "Version intermédiaire"),
("long", "Version complète"),
):
H.append(f"""<option value="{version}">{description}</option>""") H.append(f"""<option value="{version}">{description}</option>""")
H.append("""</select>&nbsp;&nbsp;<input type="submit" value="Générer"/>""") H.append("""</select>&nbsp;&nbsp;<input type="submit" value="Générer"/>""")
@ -2499,7 +2504,7 @@ def formsemestre_validation_but(
<a style="margin-left: 16px;" class="stdlink" <a style="margin-left: 16px;" class="stdlink"
href="{ href="{
url_for("notes.formsemestre_validate_previous_ue", url_for("notes.formsemestre_validate_previous_ue",
scodoc_dept=g.scodoc_dept, scodoc_dept=g.scodoc_dept,
etudid=deca.etud.id, formsemestre_id=formsemestre_id)}" etudid=deca.etud.id, formsemestre_id=formsemestre_id)}"
>enregistrer des UEs antérieures</a> >enregistrer des UEs antérieures</a>
""" """

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) selectable_dept_acronyms = set(administrable_dept_acronyms)
if edit: if edit:
if the_user.dept is not None: # ajoute dept actuel de l'utilisateur 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 if g.scodoc_dept in selectable_dept_acronyms
else (auth_dept or "") 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)) selectable_dept_acronyms = sorted(list(selectable_dept_acronyms))
descr.append( descr.append(
( (
@ -685,9 +686,8 @@ def create_user_form(user_name=None, edit=0, all_roles=True):
if "status" in vals: if "status" in vals:
vals["active"] = vals["status"] == "" vals["active"] = vals["status"] == ""
# Département: # Département:
if auth_dept: # pas super-admin if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms):
if ("dept" in vals) and (vals["dept"] not in selectable_dept_acronyms): del vals["dept"] # ne change pas de dept
del vals["dept"] # ne change pas de dept
# Traitement des roles: ne doit pas affecter les rôles # Traitement des roles: ne doit pas affecter les rôles
# que l'on en contrôle pas: # que l'on en contrôle pas:
for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... } for role in orig_roles_strings: # { "Ens_RT", "Secr_CJ", ... }