forked from ScoDoc/ScoDoc
Merge branch 'dev92' of https://scodoc.org/git/ScoDoc/ScoDoc into api
This commit is contained in:
commit
28ec8a482a
@ -20,10 +20,10 @@ Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
|
|||||||
|
|
||||||
### État actuel (26 jan 22)
|
### État actuel (26 jan 22)
|
||||||
|
|
||||||
- 9.1 (master) reproduit l'ensemble des fonctions de ScoDoc 7 (donc pas de BUT), sauf:
|
- 9.1.5x (master) reproduit l'ensemble des fonctions de ScoDoc 7 (donc pas de BUT), sauf:
|
||||||
- ancien module "Entreprises" (obsolète) et ajoute la gestion du BUT.
|
- ancien module "Entreprises" (obsolète) et ajoute la gestion du BUT.
|
||||||
|
|
||||||
- 9.2 (branche refactor_nt) est la version de développement.
|
- 9.2 (branche dev92) est la version de développement.
|
||||||
|
|
||||||
|
|
||||||
### Lignes de commandes
|
### Lignes de commandes
|
||||||
|
@ -295,10 +295,12 @@ def create_app(config_class=DevConfig):
|
|||||||
|
|
||||||
from app.scodoc.sco_bulletins_legacy import BulletinGeneratorLegacy
|
from app.scodoc.sco_bulletins_legacy import BulletinGeneratorLegacy
|
||||||
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
||||||
|
from app.but.bulletin_but_pdf import BulletinGeneratorStandardBUT
|
||||||
from app.scodoc.sco_bulletins_ucac import BulletinGeneratorUCAC
|
from app.scodoc.sco_bulletins_ucac import BulletinGeneratorUCAC
|
||||||
|
|
||||||
# l'ordre est important, le premier sera le "défaut" pour les nouveaux départements.
|
# l'ordre est important, le premier sera le "défaut" pour les nouveaux départements.
|
||||||
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandard)
|
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandard)
|
||||||
|
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorStandardBUT)
|
||||||
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy)
|
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorLegacy)
|
||||||
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorUCAC)
|
sco_bulletins_generator.register_bulletin_class(BulletinGeneratorUCAC)
|
||||||
if app.testing or app.debug:
|
if app.testing or app.debug:
|
||||||
|
@ -9,14 +9,15 @@
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
from flask import url_for, g
|
from flask import url_for, g
|
||||||
from app.models.formsemestre import FormSemestre
|
|
||||||
|
|
||||||
from app.scodoc import sco_utils as scu
|
|
||||||
from app.scodoc import sco_bulletins_json
|
|
||||||
from app.scodoc import sco_preferences
|
|
||||||
from app.scodoc.sco_codes_parcours import UE_SPORT
|
|
||||||
from app.scodoc.sco_utils import fmt_note
|
|
||||||
from app.comp.res_but import ResultatsSemestreBUT
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
|
from app.models import FormSemestre, Identite, formsemestre
|
||||||
|
from app.scodoc import sco_bulletins, sco_utils as scu
|
||||||
|
from app.scodoc import sco_bulletins_json
|
||||||
|
from app.scodoc import sco_bulletins_pdf
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
|
from app.scodoc.sco_codes_parcours import UE_SPORT, DEF
|
||||||
|
from app.scodoc.sco_utils import fmt_note
|
||||||
|
|
||||||
|
|
||||||
class BulletinBUT:
|
class BulletinBUT:
|
||||||
@ -28,6 +29,7 @@ class BulletinBUT:
|
|||||||
def __init__(self, formsemestre: FormSemestre):
|
def __init__(self, formsemestre: FormSemestre):
|
||||||
""" """
|
""" """
|
||||||
self.res = ResultatsSemestreBUT(formsemestre)
|
self.res = ResultatsSemestreBUT(formsemestre)
|
||||||
|
self.prefs = sco_preferences.SemPreferences(formsemestre.id)
|
||||||
|
|
||||||
def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
|
def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
|
||||||
"dict synthèse résultats dans l'UE pour les modules indiqués"
|
"dict synthèse résultats dans l'UE pour les modules indiqués"
|
||||||
@ -84,7 +86,7 @@ class BulletinBUT:
|
|||||||
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
"saes": self.etud_ue_mod_results(etud, ue, res.saes),
|
||||||
}
|
}
|
||||||
if ue.type != UE_SPORT:
|
if ue.type != UE_SPORT:
|
||||||
if sco_preferences.get_preference("bul_show_ue_rangs", res.formsemestre.id):
|
if self.prefs["bul_show_ue_rangs"]:
|
||||||
rangs, effectif = res.ue_rangs[ue.id]
|
rangs, effectif = res.ue_rangs[ue.id]
|
||||||
rang = rangs[etud.id]
|
rang = rangs[etud.id]
|
||||||
else:
|
else:
|
||||||
@ -109,9 +111,10 @@ class BulletinBUT:
|
|||||||
d["modules"] = self.etud_mods_results(etud, modimpls_spo)
|
d["modules"] = self.etud_mods_results(etud, modimpls_spo)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def etud_mods_results(self, etud, modimpls) -> dict:
|
def etud_mods_results(self, etud, modimpls, version="long") -> dict:
|
||||||
"""dict synthèse résultats des modules indiqués,
|
"""dict synthèse résultats des modules indiqués,
|
||||||
avec évaluations de chacun."""
|
avec évaluations de chacun (sauf si version == "short")
|
||||||
|
"""
|
||||||
res = self.res
|
res = self.res
|
||||||
d = {}
|
d = {}
|
||||||
# etud_idx = self.etud_index[etud.id]
|
# etud_idx = self.etud_index[etud.id]
|
||||||
@ -152,14 +155,14 @@ class BulletinBUT:
|
|||||||
"evaluations": [
|
"evaluations": [
|
||||||
self.etud_eval_results(etud, e)
|
self.etud_eval_results(etud, e)
|
||||||
for e in modimpl.evaluations
|
for e in modimpl.evaluations
|
||||||
if e.visibulletin
|
if (e.visibulletin or version == "long")
|
||||||
and (
|
and (
|
||||||
modimpl_results.evaluations_etat[e.id].is_complete
|
modimpl_results.evaluations_etat[e.id].is_complete
|
||||||
or sco_preferences.get_preference(
|
or self.prefs["bul_show_all_evals"]
|
||||||
"bul_show_all_evals", res.formsemestre.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
],
|
]
|
||||||
|
if version != "short"
|
||||||
|
else [],
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
|
|
||||||
@ -216,13 +219,23 @@ class BulletinBUT:
|
|||||||
else:
|
else:
|
||||||
return f"Bonus de {fmt_note(bonus_vect.iloc[0])}"
|
return f"Bonus de {fmt_note(bonus_vect.iloc[0])}"
|
||||||
|
|
||||||
def bulletin_etud(self, etud, formsemestre, force_publishing=False) -> dict:
|
def bulletin_etud(
|
||||||
"""Le bulletin de l'étudiant dans ce semestre.
|
self,
|
||||||
Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
|
etud: Identite,
|
||||||
|
formsemestre: FormSemestre,
|
||||||
|
force_publishing=False,
|
||||||
|
version="long",
|
||||||
|
) -> dict:
|
||||||
|
"""Le bulletin de l'étudiant dans ce semestre: dict pour la version JSON / HTML.
|
||||||
|
- version:
|
||||||
|
"long", "selectedevals": toutes les infos (notes des évaluations)
|
||||||
|
"short" : ne descend pas plus bas que les modules.
|
||||||
|
|
||||||
|
- Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
|
||||||
(bulletins non publiés).
|
(bulletins non publiés).
|
||||||
"""
|
"""
|
||||||
res = self.res
|
res = self.res
|
||||||
etat_inscription = etud.etat_inscription(formsemestre.id)
|
etat_inscription = etud.inscription_etat(formsemestre.id)
|
||||||
nb_inscrits = self.res.get_inscriptions_counts()[scu.INSCRIT]
|
nb_inscrits = self.res.get_inscriptions_counts()[scu.INSCRIT]
|
||||||
published = (not formsemestre.bul_hide_xml) or force_publishing
|
published = (not formsemestre.bul_hide_xml) or force_publishing
|
||||||
d = {
|
d = {
|
||||||
@ -239,7 +252,9 @@ class BulletinBUT:
|
|||||||
},
|
},
|
||||||
"formsemestre_id": formsemestre.id,
|
"formsemestre_id": formsemestre.id,
|
||||||
"etat_inscription": etat_inscription,
|
"etat_inscription": etat_inscription,
|
||||||
"options": sco_preferences.bulletin_option_affichage(formsemestre.id),
|
"options": sco_preferences.bulletin_option_affichage(
|
||||||
|
formsemestre.id, self.prefs
|
||||||
|
),
|
||||||
}
|
}
|
||||||
if not published:
|
if not published:
|
||||||
return d
|
return d
|
||||||
@ -278,8 +293,10 @@ class BulletinBUT:
|
|||||||
)
|
)
|
||||||
d.update(
|
d.update(
|
||||||
{
|
{
|
||||||
"ressources": self.etud_mods_results(etud, res.ressources),
|
"ressources": self.etud_mods_results(
|
||||||
"saes": self.etud_mods_results(etud, res.saes),
|
etud, res.ressources, version=version
|
||||||
|
),
|
||||||
|
"saes": self.etud_mods_results(etud, res.saes, version=version),
|
||||||
"ues": {
|
"ues": {
|
||||||
ue.acronyme: self.etud_ue_results(etud, ue)
|
ue.acronyme: self.etud_ue_results(etud, ue)
|
||||||
for ue in res.ues
|
for ue in res.ues
|
||||||
@ -312,3 +329,54 @@ class BulletinBUT:
|
|||||||
)
|
)
|
||||||
|
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
def bulletin_etud_complet(self, etud: Identite) -> dict:
|
||||||
|
"""Bulletin dict complet avec toutes les infos pour les bulletins BUT pdf
|
||||||
|
Résultat compatible avec celui de sco_bulletins.formsemestre_bulletinetud_dict
|
||||||
|
"""
|
||||||
|
d = self.bulletin_etud(etud, self.res.formsemestre, force_publishing=True)
|
||||||
|
d["etudid"] = etud.id
|
||||||
|
d["etud"] = d["etudiant"]
|
||||||
|
d["etud"]["nomprenom"] = etud.nomprenom
|
||||||
|
d.update(self.res.sem)
|
||||||
|
etud_etat = self.res.get_etud_etat(etud.id)
|
||||||
|
d["filigranne"] = sco_bulletins_pdf.get_filigranne(
|
||||||
|
etud_etat,
|
||||||
|
self.prefs,
|
||||||
|
decision_sem=d["semestre"].get("decision_sem"),
|
||||||
|
)
|
||||||
|
if etud_etat == scu.DEMISSION:
|
||||||
|
d["demission"] = "(Démission)"
|
||||||
|
elif etud_etat == DEF:
|
||||||
|
d["demission"] = "(Défaillant)"
|
||||||
|
else:
|
||||||
|
d["demission"] = ""
|
||||||
|
|
||||||
|
# --- Absences
|
||||||
|
d["nbabs"], d["nbabsjust"] = self.res.formsemestre.get_abs_count(etud.id)
|
||||||
|
|
||||||
|
# --- Decision Jury
|
||||||
|
infos, dpv = sco_bulletins.etud_descr_situation_semestre(
|
||||||
|
etud.id,
|
||||||
|
self.res.formsemestre.id,
|
||||||
|
format="html",
|
||||||
|
show_date_inscr=self.prefs["bul_show_date_inscr"],
|
||||||
|
show_decisions=self.prefs["bul_show_decision"],
|
||||||
|
show_uevalid=self.prefs["bul_show_uevalid"],
|
||||||
|
show_mention=self.prefs["bul_show_mention"],
|
||||||
|
)
|
||||||
|
|
||||||
|
d.update(infos)
|
||||||
|
# --- Rangs
|
||||||
|
d[
|
||||||
|
"rang_nt"
|
||||||
|
] = f"{d['semestre']['rang']['value']} / {d['semestre']['rang']['total']}"
|
||||||
|
d["rang_txt"] = "Rang " + d["rang_nt"]
|
||||||
|
|
||||||
|
# --- Appréciations
|
||||||
|
d.update(
|
||||||
|
sco_bulletins.get_appreciations_list(self.res.formsemestre.id, etud.id)
|
||||||
|
)
|
||||||
|
d.update(sco_bulletins.make_context_dict(self.res.formsemestre, d["etud"]))
|
||||||
|
|
||||||
|
return d
|
||||||
|
116
app/but/bulletin_but_pdf.py
Normal file
116
app/but/bulletin_but_pdf.py
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
##############################################################################
|
||||||
|
# ScoDoc
|
||||||
|
# Copyright (c) 1999 - 2022 Emmanuel Viennet. All rights reserved.
|
||||||
|
# See LICENSE
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
"""Génération bulletin BUT au format PDF standard
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from app.scodoc.sco_pdf import blue, cm, mm
|
||||||
|
|
||||||
|
from flask import url_for, g
|
||||||
|
from app.models.formsemestre import FormSemestre
|
||||||
|
|
||||||
|
from app.scodoc import gen_tables
|
||||||
|
from app.scodoc import sco_utils as scu
|
||||||
|
from app.scodoc import sco_bulletins_json
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
|
from app.scodoc.sco_codes_parcours import UE_SPORT
|
||||||
|
from app.scodoc.sco_utils import fmt_note
|
||||||
|
from app.comp.res_but import ResultatsSemestreBUT
|
||||||
|
|
||||||
|
from app.scodoc.sco_bulletins_standard import BulletinGeneratorStandard
|
||||||
|
|
||||||
|
|
||||||
|
class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
|
||||||
|
"""Génération du bulletin de BUT au format PDF.
|
||||||
|
|
||||||
|
self.infos est le dict issu de BulletinBUT.bulletin_etud_complet()
|
||||||
|
"""
|
||||||
|
|
||||||
|
list_in_menu = False # spécialisation du BulletinGeneratorStandard, ne pas présenter à l'utilisateur
|
||||||
|
|
||||||
|
def bul_table(self, format="html"):
|
||||||
|
"""Génère la table centrale du bulletin de notes
|
||||||
|
Renvoie:
|
||||||
|
- en HTML: une chaine
|
||||||
|
- en PDF: une liste d'objets PLATYPUS (eg instance de Table).
|
||||||
|
"""
|
||||||
|
formsemestre_id = self.infos["formsemestre_id"]
|
||||||
|
(
|
||||||
|
synth_col_keys,
|
||||||
|
synth_P,
|
||||||
|
synth_pdf_style,
|
||||||
|
synth_col_widths,
|
||||||
|
) = self.but_table_synthese()
|
||||||
|
#
|
||||||
|
table_synthese = gen_tables.GenTable(
|
||||||
|
rows=synth_P,
|
||||||
|
columns_ids=synth_col_keys,
|
||||||
|
pdf_table_style=synth_pdf_style,
|
||||||
|
pdf_col_widths=[synth_col_widths[k] for k in synth_col_keys],
|
||||||
|
preferences=self.preferences,
|
||||||
|
html_class="notes_bulletin",
|
||||||
|
html_class_ignore_default=True,
|
||||||
|
html_with_td_classes=True,
|
||||||
|
)
|
||||||
|
# Ici on ajoutera table des ressources, tables des UE
|
||||||
|
# TODO
|
||||||
|
|
||||||
|
# XXX à modifier pour générer plusieurs tables:
|
||||||
|
return table_synthese.gen(format=format)
|
||||||
|
|
||||||
|
def but_table_synthese(self):
|
||||||
|
"""La table de synthèse; pour chaque UE, liste des ressources et SAÉs avec leurs notes
|
||||||
|
et leurs coefs.
|
||||||
|
Renvoie: colkeys, P, pdf_style, colWidths
|
||||||
|
- colkeys: nom des colonnes de la table (clés)
|
||||||
|
- P : table (liste de dicts de chaines de caracteres)
|
||||||
|
- pdf_style : commandes table Platypus
|
||||||
|
- largeurs de colonnes pour PDF
|
||||||
|
"""
|
||||||
|
col_widths = {
|
||||||
|
"titre": None,
|
||||||
|
"moyenne": 2 * cm,
|
||||||
|
"coef": 2 * cm,
|
||||||
|
}
|
||||||
|
P = [] # elems pour générer table avec gen_table (liste de dicts)
|
||||||
|
col_keys = ["titre", "moyenne"] # noms des colonnes à afficher
|
||||||
|
for ue_acronym, ue in self.infos["ues"].items():
|
||||||
|
# 1er ligne titre UE
|
||||||
|
moy_ue = ue.get("moyenne")
|
||||||
|
t = {
|
||||||
|
"titre": f"{ue_acronym} - {ue['titre']}",
|
||||||
|
"moyenne": moy_ue.get("value", "-") if moy_ue is not None else "-",
|
||||||
|
"_css_row_class": "note_bold",
|
||||||
|
"_pdf_row_markup": ["b"],
|
||||||
|
"_pdf_style": [],
|
||||||
|
}
|
||||||
|
P.append(t)
|
||||||
|
# 2eme ligne titre UE (bonus/malus/ects)
|
||||||
|
t = {
|
||||||
|
"titre": "",
|
||||||
|
"moyenne": f"""Bonus: {ue['bonus']} - Malus: {
|
||||||
|
ue["malus"]} - ECTS: {ue["ECTS"]["acquis"]} / {ue["ECTS"]["total"]}""",
|
||||||
|
"_css_row_class": "note_bold",
|
||||||
|
"_pdf_row_markup": ["b"],
|
||||||
|
"_pdf_style": [
|
||||||
|
(
|
||||||
|
"LINEBELOW",
|
||||||
|
(0, 0),
|
||||||
|
(-1, 0),
|
||||||
|
self.PDF_LINEWIDTH,
|
||||||
|
self.PDF_LINECOLOR,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
}
|
||||||
|
P.append(t)
|
||||||
|
|
||||||
|
# Global pdf style commands:
|
||||||
|
pdf_style = [
|
||||||
|
("VALIGN", (0, 0), (-1, -1), "TOP"),
|
||||||
|
("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu:
|
||||||
|
]
|
||||||
|
return col_keys, P, pdf_style, col_widths
|
@ -72,7 +72,7 @@ def bulletin_but_xml_compat(
|
|||||||
etud: Identite = Identite.query.get_or_404(etudid)
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
results = bulletin_but.ResultatsSemestreBUT(formsemestre)
|
results = bulletin_but.ResultatsSemestreBUT(formsemestre)
|
||||||
nb_inscrits = results.get_inscriptions_counts()[scu.INSCRIT]
|
nb_inscrits = results.get_inscriptions_counts()[scu.INSCRIT]
|
||||||
# etat_inscription = etud.etat_inscription(formsemestre.id)
|
# etat_inscription = etud.inscription_etat(formsemestre.id)
|
||||||
etat_inscription = results.formsemestre.etuds_inscriptions[etudid].etat
|
etat_inscription = results.formsemestre.etuds_inscriptions[etudid].etat
|
||||||
if (not formsemestre.bul_hide_xml) or force_publishing:
|
if (not formsemestre.bul_hide_xml) or force_publishing:
|
||||||
published = 1
|
published = 1
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
et données rattachées (adresses, annotations, ...)
|
et données rattachées (adresses, annotations, ...)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from flask import abort, url_for
|
from flask import abort, url_for
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from sqlalchemy import desc, text
|
||||||
|
|
||||||
from app import db
|
from app import db, log
|
||||||
from app import models
|
from app import models
|
||||||
|
|
||||||
from app.scodoc import notesdb as ndb
|
from app.scodoc import notesdb as ndb
|
||||||
@ -82,6 +84,11 @@ class Identite(db.Model):
|
|||||||
return scu.suppress_accents(s)
|
return scu.suppress_accents(s)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@property
|
||||||
|
def e(self):
|
||||||
|
"terminaison en français: 'ne', '', 'ou '(e)'"
|
||||||
|
return {"M": "", "F": "e"}.get(self.civilite, "(e)")
|
||||||
|
|
||||||
def nom_disp(self) -> str:
|
def nom_disp(self) -> str:
|
||||||
"Nom à afficher"
|
"Nom à afficher"
|
||||||
if self.nom_usuel:
|
if self.nom_usuel:
|
||||||
@ -123,7 +130,7 @@ class Identite(db.Model):
|
|||||||
|
|
||||||
def get_first_email(self, field="email") -> str:
|
def get_first_email(self, field="email") -> str:
|
||||||
"Le mail associé à la première adrese de l'étudiant, ou None"
|
"Le mail associé à la première adrese de l'étudiant, ou None"
|
||||||
return self.adresses[0].email or None if self.adresses.count() > 0 else None
|
return getattr(self.adresses[0], field) if self.adresses.count() > 0 else None
|
||||||
|
|
||||||
def to_dict_scodoc7(self):
|
def to_dict_scodoc7(self):
|
||||||
"""Représentation dictionnaire,
|
"""Représentation dictionnaire,
|
||||||
@ -134,7 +141,7 @@ class Identite(db.Model):
|
|||||||
# ScoDoc7 output_formators: (backward compat)
|
# ScoDoc7 output_formators: (backward compat)
|
||||||
e["etudid"] = self.id
|
e["etudid"] = self.id
|
||||||
e["date_naissance"] = ndb.DateISOtoDMY(e["date_naissance"])
|
e["date_naissance"] = ndb.DateISOtoDMY(e["date_naissance"])
|
||||||
e["ne"] = {"M": "", "F": "ne"}.get(self.civilite, "(e)")
|
e["ne"] = self.e
|
||||||
return {k: e[k] or "" for k in e} # convert_null_outputs_to_empty
|
return {k: e[k] or "" for k in e} # convert_null_outputs_to_empty
|
||||||
|
|
||||||
def to_dict_bul(self, include_urls=True):
|
def to_dict_bul(self, include_urls=True):
|
||||||
@ -153,6 +160,7 @@ class Identite(db.Model):
|
|||||||
"etudid": self.id,
|
"etudid": self.id,
|
||||||
"nom": self.nom_disp(),
|
"nom": self.nom_disp(),
|
||||||
"prenom": self.prenom,
|
"prenom": self.prenom,
|
||||||
|
"nomprenom": self.nomprenom,
|
||||||
}
|
}
|
||||||
if include_urls:
|
if include_urls:
|
||||||
d["fiche_url"] = url_for(
|
d["fiche_url"] = url_for(
|
||||||
@ -172,6 +180,23 @@ class Identite(db.Model):
|
|||||||
]
|
]
|
||||||
return r[0] if r else None
|
return r[0] if r else None
|
||||||
|
|
||||||
|
def inscriptions_courantes(self) -> list: # -> list[FormSemestreInscription]:
|
||||||
|
"""Liste des inscriptions à des semestres _courants_
|
||||||
|
(il est rare qu'il y en ai plus d'une, mais c'est possible).
|
||||||
|
Triées par date de début de semestre décroissante (le plus récent en premier).
|
||||||
|
"""
|
||||||
|
from app.models.formsemestre import FormSemestre, FormSemestreInscription
|
||||||
|
|
||||||
|
return (
|
||||||
|
FormSemestreInscription.query.join(FormSemestreInscription.formsemestre)
|
||||||
|
.filter(
|
||||||
|
FormSemestreInscription.etudid == self.id,
|
||||||
|
text("date_debut < now() and date_fin > now()"),
|
||||||
|
)
|
||||||
|
.order_by(desc(FormSemestre.date_debut))
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
|
||||||
def inscription_courante_date(self, date_debut, date_fin):
|
def inscription_courante_date(self, date_debut, date_fin):
|
||||||
"""La première inscription à un formsemestre incluant la
|
"""La première inscription à un formsemestre incluant la
|
||||||
période [date_debut, date_fin]
|
période [date_debut, date_fin]
|
||||||
@ -183,8 +208,8 @@ class Identite(db.Model):
|
|||||||
]
|
]
|
||||||
return r[0] if r else None
|
return r[0] if r else None
|
||||||
|
|
||||||
def etat_inscription(self, formsemestre_id):
|
def inscription_etat(self, formsemestre_id):
|
||||||
"""etat de l'inscription de cet étudiant au semestre:
|
"""État de l'inscription de cet étudiant au semestre:
|
||||||
False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF
|
False si pas inscrit, ou scu.INSCRIT, DEMISSION, DEF
|
||||||
"""
|
"""
|
||||||
# voir si ce n'est pas trop lent:
|
# voir si ce n'est pas trop lent:
|
||||||
@ -195,6 +220,110 @@ class Identite(db.Model):
|
|||||||
return ins.etat
|
return ins.etat
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def inscription_descr(self) -> dict:
|
||||||
|
"""Description de l'état d'inscription"""
|
||||||
|
inscription_courante = self.inscription_courante()
|
||||||
|
if inscription_courante:
|
||||||
|
titre_sem = inscription_courante.formsemestre.titre_mois()
|
||||||
|
return {
|
||||||
|
"etat_in_cursem": inscription_courante.etat,
|
||||||
|
"inscription_courante": inscription_courante,
|
||||||
|
"inscription": titre_sem,
|
||||||
|
"inscription_str": "Inscrit en " + titre_sem,
|
||||||
|
"situation": self.descr_situation_etud(),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
if self.formsemestre_inscriptions:
|
||||||
|
# cherche l'inscription la plus récente:
|
||||||
|
fin_dernier_sem = max(
|
||||||
|
[
|
||||||
|
inscr.formsemestre.date_debut
|
||||||
|
for inscr in self.formsemestre_inscriptions
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if fin_dernier_sem > datetime.date.today():
|
||||||
|
inscription = "futur"
|
||||||
|
situation = "futur élève"
|
||||||
|
else:
|
||||||
|
inscription = "ancien"
|
||||||
|
situation = "ancien élève"
|
||||||
|
else:
|
||||||
|
inscription = ("non inscrit",)
|
||||||
|
situation = inscription
|
||||||
|
return {
|
||||||
|
"etat_in_cursem": "?",
|
||||||
|
"inscription_courante": None,
|
||||||
|
"inscription": inscription,
|
||||||
|
"inscription_str": inscription,
|
||||||
|
"situation": situation,
|
||||||
|
}
|
||||||
|
|
||||||
|
def descr_situation_etud(self) -> str:
|
||||||
|
"""Chaîne décrivant la situation _actuelle_ de l'étudiant.
|
||||||
|
Exemple:
|
||||||
|
"inscrit en BUT R&T semestre 2 FI (Jan 2022 - Jul 2022) le 16/01/2022"
|
||||||
|
ou
|
||||||
|
"non inscrit"
|
||||||
|
"""
|
||||||
|
inscriptions_courantes = self.inscriptions_courantes()
|
||||||
|
if inscriptions_courantes:
|
||||||
|
inscr = inscriptions_courantes[0]
|
||||||
|
if inscr.etat == scu.INSCRIT:
|
||||||
|
situation = f"inscrit{self.e} en {inscr.formsemestre.titre_mois()}"
|
||||||
|
# Cherche la date d'inscription dans scolar_events:
|
||||||
|
events = models.ScolarEvent.query.filter_by(
|
||||||
|
etudid=self.id,
|
||||||
|
formsemestre_id=inscr.formsemestre.id,
|
||||||
|
event_type="INSCRIPTION",
|
||||||
|
).all()
|
||||||
|
if not events:
|
||||||
|
log(
|
||||||
|
f"*** situation inconsistante pour {self} (inscrit mais pas d'event)"
|
||||||
|
)
|
||||||
|
date_ins = "???" # ???
|
||||||
|
else:
|
||||||
|
date_ins = events[0].event_date
|
||||||
|
situation += date_ins.strftime(" le %d/%m/%Y")
|
||||||
|
else:
|
||||||
|
situation = f"démission de {inscr.formsemestre.titre_mois()}"
|
||||||
|
# Cherche la date de demission dans scolar_events:
|
||||||
|
events = models.ScolarEvent.query.filter_by(
|
||||||
|
etudid=self.id,
|
||||||
|
formsemestre_id=inscr.formsemestre.id,
|
||||||
|
event_type="DEMISSION",
|
||||||
|
).all()
|
||||||
|
if not events:
|
||||||
|
log(
|
||||||
|
f"*** situation inconsistante pour {self} (demission mais pas d'event)"
|
||||||
|
)
|
||||||
|
date_dem = "???" # ???
|
||||||
|
else:
|
||||||
|
date_dem = events[0].event_date
|
||||||
|
situation += date_dem.strftime(" le %d/%m/%Y")
|
||||||
|
else:
|
||||||
|
situation = "non inscrit" + self.e
|
||||||
|
|
||||||
|
return situation
|
||||||
|
|
||||||
|
def photo_html(self, title=None, size="small") -> str:
|
||||||
|
"""HTML img tag for the photo, either in small size (h90)
|
||||||
|
or original size (size=="orig")
|
||||||
|
"""
|
||||||
|
from app.scodoc import sco_photos
|
||||||
|
|
||||||
|
# sco_photo traite des dicts:
|
||||||
|
return sco_photos.etud_photo_html(
|
||||||
|
etud=dict(
|
||||||
|
etudid=self.id,
|
||||||
|
code_nip=self.code_nip,
|
||||||
|
nomprenom=self.nomprenom,
|
||||||
|
nom_disp=self.nom_disp(),
|
||||||
|
photo_filename=self.photo_filename,
|
||||||
|
),
|
||||||
|
title=title,
|
||||||
|
size=size,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def make_etud_args(
|
def make_etud_args(
|
||||||
etudid=None, code_nip=None, use_request=True, raise_exc=False, abort_404=True
|
etudid=None, code_nip=None, use_request=True, raise_exc=False, abort_404=True
|
||||||
|
@ -12,7 +12,6 @@ from app import log
|
|||||||
from app.models import APO_CODE_STR_LEN
|
from app.models import APO_CODE_STR_LEN
|
||||||
from app.models import SHORT_STR_LEN
|
from app.models import SHORT_STR_LEN
|
||||||
from app.models import CODE_STR_LEN
|
from app.models import CODE_STR_LEN
|
||||||
from app.models import UniteEns
|
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
import app.scodoc.sco_utils as scu
|
||||||
from app.models.ues import UniteEns
|
from app.models.ues import UniteEns
|
||||||
@ -23,6 +22,7 @@ from app.scodoc import sco_codes_parcours
|
|||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc.sco_vdi import ApoEtapeVDI
|
from app.scodoc.sco_vdi import ApoEtapeVDI
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
|
from app.scodoc.sco_utils import MONTH_NAMES_ABBREV
|
||||||
|
|
||||||
|
|
||||||
class FormSemestre(db.Model):
|
class FormSemestre(db.Model):
|
||||||
@ -122,6 +122,7 @@ class FormSemestre(db.Model):
|
|||||||
return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>"
|
return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>"
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
|
"dict (compatible ScoDoc7)"
|
||||||
d = dict(self.__dict__)
|
d = dict(self.__dict__)
|
||||||
d.pop("_sa_instance_state", None)
|
d.pop("_sa_instance_state", None)
|
||||||
# ScoDoc7 output_formators: (backward compat)
|
# ScoDoc7 output_formators: (backward compat)
|
||||||
@ -162,8 +163,8 @@ class FormSemestre(db.Model):
|
|||||||
d["periode"] = 2 # typiquement, début en février: S2, S4...
|
d["periode"] = 2 # typiquement, début en février: S2, S4...
|
||||||
d["titre_num"] = self.titre_num()
|
d["titre_num"] = self.titre_num()
|
||||||
d["titreannee"] = self.titre_annee()
|
d["titreannee"] = self.titre_annee()
|
||||||
d["mois_debut"] = f"{self.date_debut.month} {self.date_debut.year}"
|
d["mois_debut"] = self.mois_debut()
|
||||||
d["mois_fin"] = f"{self.date_fin.month} {self.date_fin.year}"
|
d["mois_fin"] = self.mois_fin()
|
||||||
d["titremois"] = "%s %s (%s - %s)" % (
|
d["titremois"] = "%s %s (%s - %s)" % (
|
||||||
d["titre_num"],
|
d["titre_num"],
|
||||||
self.modalite or "",
|
self.modalite or "",
|
||||||
@ -293,6 +294,7 @@ class FormSemestre(db.Model):
|
|||||||
"""chaîne "J. Dupond, X. Martin"
|
"""chaîne "J. Dupond, X. Martin"
|
||||||
ou "Jacques Dupond, Xavier Martin"
|
ou "Jacques Dupond, Xavier Martin"
|
||||||
"""
|
"""
|
||||||
|
# was "nomcomplet"
|
||||||
if not self.responsables:
|
if not self.responsables:
|
||||||
return ""
|
return ""
|
||||||
if abbrev_prenom:
|
if abbrev_prenom:
|
||||||
@ -304,6 +306,14 @@ class FormSemestre(db.Model):
|
|||||||
"2021 - 2022"
|
"2021 - 2022"
|
||||||
return scu.annee_scolaire_repr(self.date_debut.year, self.date_debut.month)
|
return scu.annee_scolaire_repr(self.date_debut.year, self.date_debut.month)
|
||||||
|
|
||||||
|
def mois_debut(self) -> str:
|
||||||
|
"Oct 2021"
|
||||||
|
return f"{MONTH_NAMES_ABBREV[self.date_debut.month - 1]} {self.date_debut.year}"
|
||||||
|
|
||||||
|
def mois_fin(self) -> str:
|
||||||
|
"Jul 2022"
|
||||||
|
return f"{MONTH_NAMES_ABBREV[self.date_fin.month - 1]} {self.date_debut.year}"
|
||||||
|
|
||||||
def session_id(self) -> str:
|
def session_id(self) -> str:
|
||||||
"""identifiant externe de semestre de formation
|
"""identifiant externe de semestre de formation
|
||||||
Exemple: RT-DUT-FI-S1-ANNEE
|
Exemple: RT-DUT-FI-S1-ANNEE
|
||||||
@ -364,7 +374,7 @@ class FormSemestre(db.Model):
|
|||||||
|
|
||||||
def get_abs_count(self, etudid):
|
def get_abs_count(self, etudid):
|
||||||
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
"""Les comptes d'absences de cet étudiant dans ce semestre:
|
||||||
tuple (nb abs non justifiées, nb abs justifiées)
|
tuple (nb abs, nb abs justifiées)
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
|
@ -6,7 +6,8 @@ import flask_sqlalchemy
|
|||||||
|
|
||||||
from app import db
|
from app import db
|
||||||
from app.comp import df_cache
|
from app.comp import df_cache
|
||||||
from app.models import Identite, Module
|
from app.models.etudiants import Identite
|
||||||
|
from app.models.modules import Module
|
||||||
|
|
||||||
import app.scodoc.notesdb as ndb
|
import app.scodoc.notesdb as ndb
|
||||||
from app.scodoc import sco_utils as scu
|
from app.scodoc import sco_utils as scu
|
||||||
|
@ -233,7 +233,10 @@ class GenTable(object):
|
|||||||
colspan_count -= 1
|
colspan_count -= 1
|
||||||
# if colspan_count > 0:
|
# if colspan_count > 0:
|
||||||
# continue # skip cells after a span
|
# continue # skip cells after a span
|
||||||
content = row.get(cid, "") or "" # nota: None converted to ''
|
if pdf_mode:
|
||||||
|
content = row.get(f"_{cid}_pdf", "") or row.get(cid, "") or ""
|
||||||
|
else:
|
||||||
|
content = row.get(cid, "") or "" # nota: None converted to ''
|
||||||
colspan = row.get("_%s_colspan" % cid, 0)
|
colspan = row.get("_%s_colspan" % cid, 0)
|
||||||
if colspan > 1:
|
if colspan > 1:
|
||||||
pdf_style_list.append(
|
pdf_style_list.append(
|
||||||
@ -547,9 +550,16 @@ class GenTable(object):
|
|||||||
omit_hidden_lines=True,
|
omit_hidden_lines=True,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
Pt = [
|
Pt = []
|
||||||
[Paragraph(SU(str(x)), CellStyle) for x in line] for line in data_list
|
for line in data_list:
|
||||||
]
|
Pt.append(
|
||||||
|
[
|
||||||
|
Paragraph(SU(str(x)), CellStyle)
|
||||||
|
if (not isinstance(x, Paragraph))
|
||||||
|
else x
|
||||||
|
for x in line
|
||||||
|
]
|
||||||
|
)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise ScoPDFFormatError(str(exc)) from exc
|
raise ScoPDFFormatError(str(exc)) from exc
|
||||||
pdf_style_list += self.pdf_table_style
|
pdf_style_list += self.pdf_table_style
|
||||||
|
@ -1037,7 +1037,7 @@ def get_abs_count(etudid, sem):
|
|||||||
|
|
||||||
def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
def get_abs_count_in_interval(etudid, date_debut_iso, date_fin_iso):
|
||||||
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
"""Les comptes d'absences de cet étudiant entre ces deux dates, incluses:
|
||||||
tuple (nb abs non justifiées, nb abs justifiées)
|
tuple (nb abs, nb abs justifiées)
|
||||||
Utilise un cache.
|
Utilise un cache.
|
||||||
"""
|
"""
|
||||||
key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso
|
key = str(etudid) + "_" + date_debut_iso + "_" + date_fin_iso
|
||||||
|
@ -35,6 +35,7 @@ import datetime
|
|||||||
|
|
||||||
from flask import g, url_for
|
from flask import g, url_for
|
||||||
from flask_mail import Message
|
from flask_mail import Message
|
||||||
|
from app.models.formsemestre import FormSemestre
|
||||||
|
|
||||||
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
|
||||||
@ -55,27 +56,30 @@ def abs_notify(etudid, date):
|
|||||||
"""
|
"""
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
|
|
||||||
sem = retreive_current_formsemestre(etudid, date)
|
formsemestre = retreive_current_formsemestre(etudid, date)
|
||||||
if not sem:
|
if not formsemestre:
|
||||||
return # non inscrit a la date, pas de notification
|
return # non inscrit a la date, pas de notification
|
||||||
|
|
||||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
nbabs, nbabsjust = sco_abs.get_abs_count_in_interval(
|
||||||
do_abs_notify(sem, etudid, date, nbabs, nbabsjust)
|
etudid, formsemestre.date_debut.isoformat(), formsemestre.date_fin.isoformat()
|
||||||
|
)
|
||||||
|
do_abs_notify(formsemestre, etudid, date, nbabs, nbabsjust)
|
||||||
|
|
||||||
|
|
||||||
def do_abs_notify(sem, etudid, date, nbabs, nbabsjust):
|
def do_abs_notify(formsemestre: FormSemestre, etudid, date, nbabs, nbabsjust):
|
||||||
"""Given new counts of absences, check if notifications are requested and send them."""
|
"""Given new counts of absences, check if notifications are requested and send them."""
|
||||||
# prefs fallback to global pref if sem is None:
|
# prefs fallback to global pref if sem is None:
|
||||||
if sem:
|
if formsemestre:
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
formsemestre_id = formsemestre.id
|
||||||
else:
|
else:
|
||||||
formsemestre_id = None
|
formsemestre_id = None
|
||||||
prefs = sco_preferences.SemPreferences(formsemestre_id=sem["formsemestre_id"])
|
prefs = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
|
||||||
|
|
||||||
destinations = abs_notify_get_destinations(
|
destinations = abs_notify_get_destinations(
|
||||||
sem, prefs, etudid, date, nbabs, nbabsjust
|
formsemestre, prefs, etudid, date, nbabs, nbabsjust
|
||||||
)
|
)
|
||||||
msg = abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust)
|
|
||||||
|
msg = abs_notification_message(formsemestre, prefs, etudid, nbabs, nbabsjust)
|
||||||
if not msg:
|
if not msg:
|
||||||
return # abort
|
return # abort
|
||||||
|
|
||||||
@ -131,19 +135,19 @@ def abs_notify_send(destinations, etudid, msg, nbabs, nbabsjust, formsemestre_id
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def abs_notify_get_destinations(sem, prefs, etudid, date, nbabs, nbabsjust):
|
def abs_notify_get_destinations(
|
||||||
|
formsemestre: FormSemestre, prefs, etudid, date, nbabs, nbabsjust
|
||||||
|
) -> set:
|
||||||
"""Returns set of destination emails to be notified"""
|
"""Returns set of destination emails to be notified"""
|
||||||
formsemestre_id = sem["formsemestre_id"]
|
|
||||||
|
|
||||||
destinations = [] # list of email address to notify
|
destinations = [] # list of email address to notify
|
||||||
|
|
||||||
if abs_notify_is_above_threshold(etudid, nbabs, nbabsjust, formsemestre_id):
|
if abs_notify_is_above_threshold(etudid, nbabs, nbabsjust, formsemestre.id):
|
||||||
if sem and prefs["abs_notify_respsem"]:
|
if prefs["abs_notify_respsem"]:
|
||||||
# notifie chaque responsable du semestre
|
# notifie chaque responsable du semestre
|
||||||
for responsable_id in sem["responsables"]:
|
for responsable in formsemestre.responsables:
|
||||||
u = sco_users.user_info(responsable_id)
|
if responsable.email:
|
||||||
if u["email"]:
|
destinations.append(responsable.email)
|
||||||
destinations.append(u["email"])
|
|
||||||
if prefs["abs_notify_chief"] and prefs["email_chefdpt"]:
|
if prefs["abs_notify_chief"] and prefs["email_chefdpt"]:
|
||||||
destinations.append(prefs["email_chefdpt"])
|
destinations.append(prefs["email_chefdpt"])
|
||||||
if prefs["abs_notify_email"]:
|
if prefs["abs_notify_email"]:
|
||||||
@ -156,7 +160,7 @@ def abs_notify_get_destinations(sem, prefs, etudid, date, nbabs, nbabsjust):
|
|||||||
# Notification (à chaque fois) des resp. de modules ayant des évaluations
|
# Notification (à chaque fois) des resp. de modules ayant des évaluations
|
||||||
# à cette date
|
# à cette date
|
||||||
# nb: on pourrait prevoir d'utiliser un autre format de message pour ce cas
|
# nb: on pourrait prevoir d'utiliser un autre format de message pour ce cas
|
||||||
if sem and prefs["abs_notify_respeval"]:
|
if prefs["abs_notify_respeval"]:
|
||||||
mods = mod_with_evals_at_date(date, etudid)
|
mods = mod_with_evals_at_date(date, etudid)
|
||||||
for mod in mods:
|
for mod in mods:
|
||||||
u = sco_users.user_info(mod["responsable_id"])
|
u = sco_users.user_info(mod["responsable_id"])
|
||||||
@ -232,7 +236,9 @@ def user_nbdays_since_last_notif(email_addr, etudid):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
|
def abs_notification_message(
|
||||||
|
formsemestre: FormSemestre, prefs, etudid, nbabs, nbabsjust
|
||||||
|
):
|
||||||
"""Mime notification message based on template.
|
"""Mime notification message based on template.
|
||||||
returns a Message instance
|
returns a Message instance
|
||||||
or None if sending should be canceled (empty template).
|
or None if sending should be canceled (empty template).
|
||||||
@ -242,7 +248,7 @@ def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
|
|||||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||||
|
|
||||||
# Variables accessibles dans les balises du template: %(nom_variable)s :
|
# Variables accessibles dans les balises du template: %(nom_variable)s :
|
||||||
values = sco_bulletins.make_context_dict(sem, etud)
|
values = sco_bulletins.make_context_dict(formsemestre, etud)
|
||||||
|
|
||||||
values["nbabs"] = nbabs
|
values["nbabs"] = nbabs
|
||||||
values["nbabsjust"] = nbabsjust
|
values["nbabsjust"] = nbabsjust
|
||||||
@ -264,9 +270,11 @@ def abs_notification_message(sem, prefs, etudid, nbabs, nbabsjust):
|
|||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def retreive_current_formsemestre(etudid, cur_date):
|
def retreive_current_formsemestre(etudid: int, cur_date) -> FormSemestre:
|
||||||
"""Get formsemestre dans lequel etudid est (ou était) inscrit a la date indiquée
|
"""Get formsemestre dans lequel etudid est (ou était) inscrit a la date indiquée
|
||||||
date est une chaine au format ISO (yyyy-mm-dd)
|
date est une chaine au format ISO (yyyy-mm-dd)
|
||||||
|
|
||||||
|
Result: FormSemestre ou None si pas inscrit à la date indiquée
|
||||||
"""
|
"""
|
||||||
req = """SELECT i.formsemestre_id
|
req = """SELECT i.formsemestre_id
|
||||||
FROM notes_formsemestre_inscription i, notes_formsemestre sem
|
FROM notes_formsemestre_inscription i, notes_formsemestre sem
|
||||||
@ -278,8 +286,8 @@ def retreive_current_formsemestre(etudid, cur_date):
|
|||||||
if not r:
|
if not r:
|
||||||
return None
|
return None
|
||||||
# s'il y a plusieurs semestres, prend le premier (rarissime et non significatif):
|
# s'il y a plusieurs semestres, prend le premier (rarissime et non significatif):
|
||||||
sem = sco_formsemestre.get_formsemestre(r[0]["formsemestre_id"])
|
formsemestre = FormSemestre.query.get(r[0]["formsemestre_id"])
|
||||||
return sem
|
return formsemestre
|
||||||
|
|
||||||
|
|
||||||
def mod_with_evals_at_date(date_abs, etudid):
|
def mod_with_evals_at_date(date_abs, etudid):
|
||||||
|
@ -28,30 +28,19 @@
|
|||||||
"""Génération des bulletins de notes
|
"""Génération des bulletins de notes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from app.models import formsemestre
|
|
||||||
import time
|
|
||||||
import pprint
|
|
||||||
import email
|
import email
|
||||||
from email.mime.multipart import MIMEMultipart
|
import time
|
||||||
from email.mime.text import MIMEText
|
|
||||||
from email.mime.base import MIMEBase
|
|
||||||
from email.header import Header
|
|
||||||
from reportlab.lib.colors import Color
|
|
||||||
import urllib
|
|
||||||
|
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
from flask import url_for
|
from flask import render_template, url_for
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_mail import Message
|
|
||||||
from app.models.moduleimpls import ModuleImplInscription
|
|
||||||
|
|
||||||
import app.scodoc.sco_utils as scu
|
from app import email
|
||||||
from app.scodoc.sco_utils import ModuleType
|
|
||||||
import app.scodoc.notesdb as ndb
|
|
||||||
from app import log
|
from app import log
|
||||||
|
from app.but import bulletin_but
|
||||||
from app.comp import res_sem
|
from app.comp import res_sem
|
||||||
from app.comp.res_common import NotesTableCompat
|
from app.comp.res_common import NotesTableCompat
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre, Identite, ModuleImplInscription
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
@ -60,9 +49,9 @@ from app.scodoc import sco_abs
|
|||||||
from app.scodoc import sco_abs_views
|
from app.scodoc import sco_abs_views
|
||||||
from app.scodoc import sco_bulletins_generator
|
from app.scodoc import sco_bulletins_generator
|
||||||
from app.scodoc import sco_bulletins_json
|
from app.scodoc import sco_bulletins_json
|
||||||
|
from app.scodoc import sco_bulletins_pdf
|
||||||
from app.scodoc import sco_bulletins_xml
|
from app.scodoc import sco_bulletins_xml
|
||||||
from app.scodoc import sco_codes_parcours
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_cache
|
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
from app.scodoc import sco_evaluation_db
|
from app.scodoc import sco_evaluation_db
|
||||||
from app.scodoc import sco_formations
|
from app.scodoc import sco_formations
|
||||||
@ -73,7 +62,9 @@ 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
|
||||||
from app import email
|
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
|
# ----- CLASSES DE BULLETINS DE NOTES
|
||||||
from app.scodoc import sco_bulletins_standard
|
from app.scodoc import sco_bulletins_standard
|
||||||
@ -85,33 +76,20 @@ from app.scodoc import sco_bulletins_legacy
|
|||||||
from app.scodoc import sco_bulletins_ucac # format expérimental UCAC Cameroun
|
from app.scodoc import sco_bulletins_ucac # format expérimental UCAC Cameroun
|
||||||
|
|
||||||
|
|
||||||
def make_context_dict(sem, etud):
|
def make_context_dict(formsemestre: FormSemestre, etud: dict) -> dict:
|
||||||
"""Construit dictionnaire avec valeurs pour substitution des textes
|
"""Construit dictionnaire avec valeurs pour substitution des textes
|
||||||
(preferences bul_pdf_*)
|
(preferences bul_pdf_*)
|
||||||
"""
|
"""
|
||||||
C = sem.copy()
|
C = formsemestre.get_infos_dict()
|
||||||
C["responsable"] = " ,".join(
|
C["responsable"] = formsemestre.responsables_str()
|
||||||
[
|
C["anneesem"] = C["annee"] # backward compat
|
||||||
sco_users.user_info(responsable_id)["prenomnom"]
|
|
||||||
for responsable_id in sem["responsables"]
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
annee_debut = sem["date_debut"].split("/")[2]
|
|
||||||
annee_fin = sem["date_fin"].split("/")[2]
|
|
||||||
if annee_debut != annee_fin:
|
|
||||||
annee = "%s - %s" % (annee_debut, annee_fin)
|
|
||||||
else:
|
|
||||||
annee = annee_debut
|
|
||||||
C["anneesem"] = annee
|
|
||||||
C.update(etud)
|
C.update(etud)
|
||||||
# copie preferences
|
# copie preferences
|
||||||
# XXX devrait acceder directement à un dict de preferences, à revoir
|
|
||||||
for name in sco_preferences.get_base_preferences().prefs_name:
|
for name in sco_preferences.get_base_preferences().prefs_name:
|
||||||
C[name] = sco_preferences.get_preference(name, sem["formsemestre_id"])
|
C[name] = sco_preferences.get_preference(name, formsemestre.id)
|
||||||
|
|
||||||
# ajoute groupes et group_0, group_1, ...
|
# ajoute groupes et group_0, group_1, ...
|
||||||
sco_groups.etud_add_group_infos(etud, sem)
|
sco_groups.etud_add_group_infos(etud, formsemestre.id)
|
||||||
C["groupes"] = etud["groupes"]
|
C["groupes"] = etud["groupes"]
|
||||||
n = 0
|
n = 0
|
||||||
for partition_id in etud["partitions"]:
|
for partition_id in etud["partitions"]:
|
||||||
@ -132,7 +110,8 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
Le contenu du dictionnaire dépend des options (rangs, ...)
|
Le contenu du dictionnaire dépend des options (rangs, ...)
|
||||||
et de la version choisie (short, long, selectedevals).
|
et de la version choisie (short, long, selectedevals).
|
||||||
|
|
||||||
Cette fonction est utilisée pour les bulletins HTML et PDF, mais pas ceux en XML.
|
Cette fonction est utilisée pour les bulletins CLASSIQUES (DUT, ...)
|
||||||
|
en HTML et PDF, mais pas ceux en XML.
|
||||||
"""
|
"""
|
||||||
from app.scodoc import sco_abs
|
from app.scodoc import sco_abs
|
||||||
|
|
||||||
@ -190,39 +169,23 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
show_mention=prefs["bul_show_mention"],
|
show_mention=prefs["bul_show_mention"],
|
||||||
)
|
)
|
||||||
|
|
||||||
if dpv:
|
|
||||||
I["decision_sem"] = dpv["decisions"][0]["decision_sem"]
|
|
||||||
else:
|
|
||||||
I["decision_sem"] = ""
|
|
||||||
I.update(infos)
|
I.update(infos)
|
||||||
|
|
||||||
I["etud_etat_html"] = _get_etud_etat_html(
|
I["etud_etat_html"] = _get_etud_etat_html(
|
||||||
formsemestre.etuds_inscriptions[etudid].etat
|
formsemestre.etuds_inscriptions[etudid].etat
|
||||||
)
|
)
|
||||||
I["etud_etat"] = nt.get_etud_etat(etudid)
|
I["etud_etat"] = nt.get_etud_etat(etudid)
|
||||||
I["filigranne"] = ""
|
I["filigranne"] = sco_bulletins_pdf.get_filigranne(
|
||||||
|
I["etud_etat"], prefs, decision_sem=I["decision_sem"]
|
||||||
|
)
|
||||||
I["demission"] = ""
|
I["demission"] = ""
|
||||||
if I["etud_etat"] == "D":
|
if I["etud_etat"] == scu.DEMISSION:
|
||||||
I["demission"] = "(Démission)"
|
I["demission"] = "(Démission)"
|
||||||
I["filigranne"] = "Démission"
|
|
||||||
elif I["etud_etat"] == sco_codes_parcours.DEF:
|
elif I["etud_etat"] == sco_codes_parcours.DEF:
|
||||||
I["demission"] = "(Défaillant)"
|
I["demission"] = "(Défaillant)"
|
||||||
I["filigranne"] = "Défaillant"
|
|
||||||
elif (prefs["bul_show_temporary"] and not I["decision_sem"]) or prefs[
|
|
||||||
"bul_show_temporary_forced"
|
|
||||||
]:
|
|
||||||
I["filigranne"] = prefs["bul_temporary_txt"]
|
|
||||||
|
|
||||||
# --- Appreciations
|
# --- Appreciations
|
||||||
cnx = ndb.GetDBConnexion()
|
I.update(get_appreciations_list(formsemestre_id, etudid))
|
||||||
apprecs = sco_etud.appreciations_list(
|
|
||||||
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
|
|
||||||
)
|
|
||||||
I["appreciations_list"] = apprecs
|
|
||||||
I["appreciations_txt"] = [x["date"] + ": " + x["comment"] for x in apprecs]
|
|
||||||
I["appreciations"] = I[
|
|
||||||
"appreciations_txt"
|
|
||||||
] # deprecated / keep it for backward compat in templates
|
|
||||||
|
|
||||||
# --- Notes
|
# --- Notes
|
||||||
ues = nt.get_ues_stat_dict()
|
ues = nt.get_ues_stat_dict()
|
||||||
@ -316,7 +279,7 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
else:
|
else:
|
||||||
u["cur_moy_ue_txt"] = "bonus appliqué sur les UEs"
|
u["cur_moy_ue_txt"] = "bonus appliqué sur les UEs"
|
||||||
else:
|
else:
|
||||||
u["cur_moy_ue_txt"] = "bonus de %.3g points" % x
|
u["cur_moy_ue_txt"] = f"bonus de {fmt_note(x)} points"
|
||||||
if nt.bonus_ues is not None:
|
if nt.bonus_ues is not None:
|
||||||
u["cur_moy_ue_txt"] += " (+ues)"
|
u["cur_moy_ue_txt"] += " (+ues)"
|
||||||
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
|
u["moy_ue_txt"] = scu.fmt_note(ue_status["moy"])
|
||||||
@ -407,13 +370,28 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
|||||||
I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
|
I["matieres_modules"].update(_sort_mod_by_matiere(modules, nt, etudid))
|
||||||
|
|
||||||
#
|
#
|
||||||
C = make_context_dict(I["sem"], I["etud"])
|
C = make_context_dict(formsemestre, I["etud"])
|
||||||
C.update(I)
|
C.update(I)
|
||||||
#
|
#
|
||||||
# log( 'C = \n%s\n' % pprint.pformat(C) ) # tres pratique pour voir toutes les infos dispo
|
# log( 'C = \n%s\n' % pprint.pformat(C) ) # tres pratique pour voir toutes les infos dispo
|
||||||
return C
|
return C
|
||||||
|
|
||||||
|
|
||||||
|
def get_appreciations_list(formsemestre_id: int, etudid: int) -> dict:
|
||||||
|
"""Appréciations pour cet étudiant dans ce semestre"""
|
||||||
|
cnx = ndb.GetDBConnexion()
|
||||||
|
apprecs = sco_etud.appreciations_list(
|
||||||
|
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
|
||||||
|
)
|
||||||
|
d = {
|
||||||
|
"appreciations_list": apprecs,
|
||||||
|
"appreciations_txt": [x["date"] + ": " + x["comment"] for x in apprecs],
|
||||||
|
}
|
||||||
|
# deprecated / keep it for backward compat in templates:
|
||||||
|
d["appreciations"] = d["appreciations_txt"]
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def _get_etud_etat_html(etat: str) -> str:
|
def _get_etud_etat_html(etat: str) -> str:
|
||||||
"""chaine html représentant l'état (backward compat sco7)"""
|
"""chaine html représentant l'état (backward compat sco7)"""
|
||||||
if etat == scu.INSCRIT: # "I"
|
if etat == scu.INSCRIT: # "I"
|
||||||
@ -691,6 +669,7 @@ def etud_descr_situation_semestre(
|
|||||||
descr_defaillance : "Défaillant" ou vide si non défaillant.
|
descr_defaillance : "Défaillant" ou vide si non défaillant.
|
||||||
decision_jury : "Validé", "Ajourné", ... (code semestre)
|
decision_jury : "Validé", "Ajourné", ... (code semestre)
|
||||||
descr_decision_jury : "Décision jury: Validé" (une phrase)
|
descr_decision_jury : "Décision jury: Validé" (une phrase)
|
||||||
|
decision_sem :
|
||||||
decisions_ue : noms (acronymes) des UE validées, séparées par des virgules.
|
decisions_ue : noms (acronymes) des UE validées, séparées par des virgules.
|
||||||
descr_decisions_ue : ' UE acquises: UE1, UE2', ou vide si pas de dec. ou si pas show_uevalid
|
descr_decisions_ue : ' UE acquises: UE1, UE2', ou vide si pas de dec. ou si pas show_uevalid
|
||||||
descr_mention : 'Mention Bien', ou vide si pas de mention ou si pas show_mention
|
descr_mention : 'Mention Bien', ou vide si pas de mention ou si pas show_mention
|
||||||
@ -700,7 +679,7 @@ def etud_descr_situation_semestre(
|
|||||||
|
|
||||||
# --- Situation et décisions jury
|
# --- Situation et décisions jury
|
||||||
|
|
||||||
# demission/inscription ?
|
# démission/inscription ?
|
||||||
events = sco_etud.scolar_events_list(
|
events = sco_etud.scolar_events_list(
|
||||||
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
|
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
|
||||||
)
|
)
|
||||||
@ -767,11 +746,15 @@ def etud_descr_situation_semestre(
|
|||||||
infos["situation"] += " " + infos["descr_defaillance"]
|
infos["situation"] += " " + infos["descr_defaillance"]
|
||||||
|
|
||||||
dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=[etudid])
|
dpv = sco_pvjury.dict_pvjury(formsemestre_id, etudids=[etudid])
|
||||||
|
if dpv:
|
||||||
|
infos["decision_sem"] = dpv["decisions"][0]["decision_sem"]
|
||||||
|
else:
|
||||||
|
infos["decision_sem"] = ""
|
||||||
|
|
||||||
if not show_decisions:
|
if not show_decisions:
|
||||||
return infos, dpv
|
return infos, dpv
|
||||||
|
|
||||||
# Decisions de jury:
|
# Décisions de jury:
|
||||||
pv = dpv["decisions"][0]
|
pv = dpv["decisions"][0]
|
||||||
dec = ""
|
dec = ""
|
||||||
if pv["decision_sem_descr"]:
|
if pv["decision_sem_descr"]:
|
||||||
@ -810,24 +793,21 @@ def etud_descr_situation_semestre(
|
|||||||
def formsemestre_bulletinetud(
|
def formsemestre_bulletinetud(
|
||||||
etudid=None,
|
etudid=None,
|
||||||
formsemestre_id=None,
|
formsemestre_id=None,
|
||||||
format="html",
|
format=None,
|
||||||
version="long",
|
version="long",
|
||||||
xml_with_decisions=False,
|
xml_with_decisions=False,
|
||||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
||||||
prefer_mail_perso=False,
|
prefer_mail_perso=False,
|
||||||
):
|
):
|
||||||
"page bulletin de notes"
|
"page bulletin de notes"
|
||||||
try:
|
format = format or "html"
|
||||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
etud: Identite = Identite.query.get_or_404(etudid)
|
||||||
etudid = etud["etudid"]
|
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
|
||||||
except:
|
if not formsemestre:
|
||||||
sco_etud.log_unknown_etud()
|
raise ScoValueError(f"semestre {formsemestre_id} inconnu !")
|
||||||
raise ScoValueError("étudiant inconnu")
|
|
||||||
# API, donc erreurs admises en ScoValueError
|
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
|
|
||||||
|
|
||||||
bulletin = do_formsemestre_bulletinetud(
|
bulletin = do_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre,
|
||||||
etudid,
|
etudid,
|
||||||
format=format,
|
format=format,
|
||||||
version=version,
|
version=version,
|
||||||
@ -836,52 +816,22 @@ def formsemestre_bulletinetud(
|
|||||||
prefer_mail_perso=prefer_mail_perso,
|
prefer_mail_perso=prefer_mail_perso,
|
||||||
)[0]
|
)[0]
|
||||||
if format not in {"html", "pdfmail"}:
|
if format not in {"html", "pdfmail"}:
|
||||||
filename = scu.bul_filename(sem, etud, format)
|
filename = scu.bul_filename(formsemestre, etud, format)
|
||||||
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
|
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
|
||||||
|
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
|
||||||
H = [
|
H = [
|
||||||
_formsemestre_bulletinetud_header_html(
|
_formsemestre_bulletinetud_header_html(etud, formsemestre, format, version),
|
||||||
etud, etudid, sem, formsemestre_id, format, version
|
|
||||||
),
|
|
||||||
bulletin,
|
bulletin,
|
||||||
|
render_template(
|
||||||
|
"bul_foot.html",
|
||||||
|
etud=etud,
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
inscription_courante=etud.inscription_courante(),
|
||||||
|
inscription_str=etud.inscription_descr()["inscription_str"],
|
||||||
|
),
|
||||||
|
html_sco_header.sco_footer(),
|
||||||
]
|
]
|
||||||
|
|
||||||
H.append("""<p>Situation actuelle: """)
|
|
||||||
if etud["inscription_formsemestre_id"]:
|
|
||||||
H.append(
|
|
||||||
f"""<a class="stdlink" href="{url_for(
|
|
||||||
"notes.formsemestre_status",
|
|
||||||
scodoc_dept=g.scodoc_dept,
|
|
||||||
formsemestre_id=etud["inscription_formsemestre_id"])
|
|
||||||
}">"""
|
|
||||||
)
|
|
||||||
H.append(etud["inscriptionstr"])
|
|
||||||
if etud["inscription_formsemestre_id"]:
|
|
||||||
H.append("""</a>""")
|
|
||||||
H.append("""</p>""")
|
|
||||||
if sem["modalite"] == "EXT":
|
|
||||||
H.append(
|
|
||||||
"""<p><a
|
|
||||||
href="formsemestre_ext_edit_ue_validations?formsemestre_id=%s&etudid=%s"
|
|
||||||
class="stdlink">
|
|
||||||
Editer les validations d'UE dans ce semestre extérieur
|
|
||||||
</a></p>"""
|
|
||||||
% (formsemestre_id, etudid)
|
|
||||||
)
|
|
||||||
# Place du diagramme radar
|
|
||||||
H.append(
|
|
||||||
"""<form id="params">
|
|
||||||
<input type="hidden" name="etudid" id="etudid" value="%s"/>
|
|
||||||
<input type="hidden" name="formsemestre_id" id="formsemestre_id" value="%s"/>
|
|
||||||
</form>"""
|
|
||||||
% (etudid, formsemestre_id)
|
|
||||||
)
|
|
||||||
H.append('<div id="radar_bulletin"></div>')
|
|
||||||
|
|
||||||
# --- Pied de page
|
|
||||||
H.append(html_sco_header.sco_footer())
|
|
||||||
|
|
||||||
return "".join(H)
|
return "".join(H)
|
||||||
|
|
||||||
|
|
||||||
@ -896,23 +846,24 @@ def can_send_bulletin_by_mail(formsemestre_id):
|
|||||||
|
|
||||||
|
|
||||||
def do_formsemestre_bulletinetud(
|
def do_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre: FormSemestre,
|
||||||
etudid,
|
etudid: int,
|
||||||
version="long", # short, long, selectedevals
|
version="long", # short, long, selectedevals
|
||||||
format="html",
|
format=None,
|
||||||
nohtml=False,
|
nohtml=False,
|
||||||
xml_with_decisions=False, # force decisions dans XML
|
xml_with_decisions=False, # force décisions dans XML
|
||||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
force_publishing=False, # force publication meme si semestre non publié sur "portail"
|
||||||
prefer_mail_perso=False, # mails envoyes sur adresse perso si non vide
|
prefer_mail_perso=False, # mails envoyés sur adresse perso si non vide
|
||||||
):
|
):
|
||||||
"""Génère le bulletin au format demandé.
|
"""Génère le bulletin au format demandé.
|
||||||
Retourne: (bul, filigranne)
|
Retourne: (bul, filigranne)
|
||||||
où bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
|
où 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").
|
||||||
"""
|
"""
|
||||||
|
format = format or "html"
|
||||||
if format == "xml":
|
if format == "xml":
|
||||||
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
|
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre.id,
|
||||||
etudid,
|
etudid,
|
||||||
xml_with_decisions=xml_with_decisions,
|
xml_with_decisions=xml_with_decisions,
|
||||||
force_publishing=force_publishing,
|
force_publishing=force_publishing,
|
||||||
@ -923,7 +874,7 @@ def do_formsemestre_bulletinetud(
|
|||||||
|
|
||||||
elif format == "json":
|
elif format == "json":
|
||||||
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
|
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre.id,
|
||||||
etudid,
|
etudid,
|
||||||
xml_with_decisions=xml_with_decisions,
|
xml_with_decisions=xml_with_decisions,
|
||||||
force_publishing=force_publishing,
|
force_publishing=force_publishing,
|
||||||
@ -931,8 +882,13 @@ def do_formsemestre_bulletinetud(
|
|||||||
)
|
)
|
||||||
return bul, ""
|
return bul, ""
|
||||||
|
|
||||||
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
|
if formsemestre.formation.is_apc():
|
||||||
etud = I["etud"]
|
etud = Identite.query.get(etudid)
|
||||||
|
r = bulletin_but.BulletinBUT(formsemestre)
|
||||||
|
I = r.bulletin_etud_complet(etud)
|
||||||
|
else:
|
||||||
|
I = formsemestre_bulletinetud_dict(formsemestre.id, etudid)
|
||||||
|
etud = I["etud"]
|
||||||
|
|
||||||
if format == "html":
|
if format == "html":
|
||||||
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||||
@ -958,7 +914,7 @@ def do_formsemestre_bulletinetud(
|
|||||||
elif format == "pdfmail":
|
elif format == "pdfmail":
|
||||||
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
|
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
|
||||||
# check permission
|
# check permission
|
||||||
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 !")
|
||||||
|
|
||||||
if nohtml:
|
if nohtml:
|
||||||
@ -987,7 +943,7 @@ def do_formsemestre_bulletinetud(
|
|||||||
) + htm
|
) + htm
|
||||||
return h, I["filigranne"]
|
return h, I["filigranne"]
|
||||||
#
|
#
|
||||||
mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr)
|
mail_bulletin(formsemestre.id, I, pdfdata, filename, recipient_addr)
|
||||||
emaillink = '<a class="stdlink" href="mailto:%s">%s</a>' % (
|
emaillink = '<a class="stdlink" href="mailto:%s">%s</a>' % (
|
||||||
recipient_addr,
|
recipient_addr,
|
||||||
recipient_addr,
|
recipient_addr,
|
||||||
@ -1055,17 +1011,15 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _formsemestre_bulletinetud_header_html(
|
def _formsemestre_bulletinetud_header_html_old_XXX(
|
||||||
etud,
|
etud: Identite,
|
||||||
etudid,
|
formsemestre: FormSemestre,
|
||||||
sem,
|
|
||||||
formsemestre_id=None,
|
|
||||||
format=None,
|
format=None,
|
||||||
version=None,
|
version=None,
|
||||||
):
|
):
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title="Bulletin de %(nomprenom)s" % etud,
|
page_title=f"Bulletin de {etud.nomprenom}",
|
||||||
javascripts=[
|
javascripts=[
|
||||||
"js/bulletin.js",
|
"js/bulletin.js",
|
||||||
"libjs/d3.v3.min.js",
|
"libjs/d3.v3.min.js",
|
||||||
@ -1073,33 +1027,27 @@ def _formsemestre_bulletinetud_header_html(
|
|||||||
],
|
],
|
||||||
cssstyles=["css/radar_bulletin.css"],
|
cssstyles=["css/radar_bulletin.css"],
|
||||||
),
|
),
|
||||||
"""<table class="bull_head"><tr><td>
|
f"""<table class="bull_head"><tr><td>
|
||||||
<h2><a class="discretelink" href="%s">%s</a></h2>
|
<h2><a class="discretelink" href="{
|
||||||
"""
|
|
||||||
% (
|
|
||||||
url_for(
|
url_for(
|
||||||
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud["etudid"]
|
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id
|
||||||
),
|
)}">{etud.nomprenom}</a></h2>
|
||||||
etud["nomprenom"],
|
|
||||||
),
|
<form name="f" method="GET" action="{request.base_url}">
|
||||||
"""
|
Bulletin <span class="bull_liensemestre"><a href="{
|
||||||
<form name="f" method="GET" action="%s">"""
|
url_for("notes.formsemestre_status",
|
||||||
% request.base_url,
|
scodoc_dept=g.scodoc_dept,
|
||||||
f"""Bulletin <span class="bull_liensemestre"><a href="{
|
formsemestre_id=formsemestre.id)}
|
||||||
url_for("notes.formsemestre_status",
|
">{formsemestre.titre_mois()}</a></span>
|
||||||
scodoc_dept=g.scodoc_dept,
|
<br/>
|
||||||
formsemestre_id=sem["formsemestre_id"])}
|
<table><tr>
|
||||||
">{sem["titremois"]}</a></span>
|
<td>établi le {time.strftime("%d/%m/%Y à %Hh%M")} (notes sur 20)</td>
|
||||||
<br/>"""
|
<td><span class="rightjust">
|
||||||
% sem,
|
<input type="hidden" name="formsemestre_id" value="{formsemestre.id}"></input>
|
||||||
"""<table><tr>""",
|
<input type="hidden" name="etudid" value="{etud.id}"></input>
|
||||||
"""<td>établi le %s (notes sur 20)</td>""" % time.strftime("%d/%m/%Y à %Hh%M"),
|
<input type="hidden" name="format" value="{format}"></input>
|
||||||
"""<td><span class="rightjust">
|
<select name="version" onchange="document.f.submit()" class="noprint">
|
||||||
<input type="hidden" name="formsemestre_id" value="%s"></input>"""
|
""",
|
||||||
% formsemestre_id,
|
|
||||||
"""<input type="hidden" name="etudid" value="%s"></input>""" % etudid,
|
|
||||||
"""<input type="hidden" name="format" value="%s"></input>""" % format,
|
|
||||||
"""<select name="version" onchange="document.f.submit()" class="noprint">""",
|
|
||||||
]
|
]
|
||||||
for (v, e) in (
|
for (v, e) in (
|
||||||
("short", "Version courte"),
|
("short", "Version courte"),
|
||||||
@ -1114,143 +1062,12 @@ def _formsemestre_bulletinetud_header_html(
|
|||||||
H.append("""</select></td>""")
|
H.append("""</select></td>""")
|
||||||
# Menu
|
# Menu
|
||||||
endpoint = "notes.formsemestre_bulletinetud"
|
endpoint = "notes.formsemestre_bulletinetud"
|
||||||
|
menu_autres_operations = make_menu_autres_operations(
|
||||||
menuBul = [
|
formsemestre, etud, endpoint, version
|
||||||
{
|
)
|
||||||
"title": "Réglages bulletins",
|
|
||||||
"endpoint": "notes.formsemestre_edit_options",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
# "target_url": url_for(
|
|
||||||
# "notes.formsemestre_bulletinetud",
|
|
||||||
# scodoc_dept=g.scodoc_dept,
|
|
||||||
# formsemestre_id=formsemestre_id,
|
|
||||||
# etudid=etudid,
|
|
||||||
# ),
|
|
||||||
},
|
|
||||||
"enabled": (current_user.id in sem["responsables"])
|
|
||||||
or current_user.has_permission(Permission.ScoImplement),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": 'Version papier (pdf, format "%s")'
|
|
||||||
% sco_bulletins_generator.bulletin_get_class_name_displayed(
|
|
||||||
formsemestre_id
|
|
||||||
),
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
"version": version,
|
|
||||||
"format": "pdf",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Envoi par mail à %s" % etud["email"],
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
"version": version,
|
|
||||||
"format": "pdfmail",
|
|
||||||
},
|
|
||||||
# possible slt si on a un mail...
|
|
||||||
"enabled": etud["email"] and can_send_bulletin_by_mail(formsemestre_id),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Envoi par mail à %s (adr. personnelle)" % etud["emailperso"],
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
"version": version,
|
|
||||||
"format": "pdfmail",
|
|
||||||
"prefer_mail_perso": 1,
|
|
||||||
},
|
|
||||||
# possible slt si on a un mail...
|
|
||||||
"enabled": etud["emailperso"]
|
|
||||||
and can_send_bulletin_by_mail(formsemestre_id),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Version json",
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
"version": version,
|
|
||||||
"format": "json",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Version XML",
|
|
||||||
"endpoint": endpoint,
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
"version": version,
|
|
||||||
"format": "xml",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Ajouter une appréciation",
|
|
||||||
"endpoint": "notes.appreciation_add_form",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": (
|
|
||||||
(current_user.id in sem["responsables"])
|
|
||||||
or (current_user.has_permission(Permission.ScoEtudInscrit))
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Enregistrer un semestre effectué ailleurs",
|
|
||||||
"endpoint": "notes.formsemestre_ext_create_form",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": current_user.has_permission(Permission.ScoImplement),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Enregistrer une validation d'UE antérieure",
|
|
||||||
"endpoint": "notes.formsemestre_validate_previous_ue",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Enregistrer note d'une UE externe",
|
|
||||||
"endpoint": "notes.external_ue_create_form",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Entrer décisions jury",
|
|
||||||
"endpoint": "notes.formsemestre_validation_etud_form",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"title": "Editer PV jury",
|
|
||||||
"endpoint": "notes.formsemestre_pvjury_pdf",
|
|
||||||
"args": {
|
|
||||||
"formsemestre_id": formsemestre_id,
|
|
||||||
"etudid": etudid,
|
|
||||||
},
|
|
||||||
"enabled": True,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
H.append("""<td class="bulletin_menubar"><div class="bulletin_menubar">""")
|
H.append("""<td class="bulletin_menubar"><div class="bulletin_menubar">""")
|
||||||
H.append(htmlutils.make_menu("Autres opérations", menuBul, alone=True))
|
H.append(menu_autres_operations)
|
||||||
H.append("""</div></td>""")
|
H.append("""</div></td>""")
|
||||||
H.append(
|
H.append(
|
||||||
'<td> <a href="%s">%s</a></td>'
|
'<td> <a href="%s">%s</a></td>'
|
||||||
@ -1258,8 +1075,8 @@ def _formsemestre_bulletinetud_header_html(
|
|||||||
url_for(
|
url_for(
|
||||||
"notes.formsemestre_bulletinetud",
|
"notes.formsemestre_bulletinetud",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
formsemestre_id=formsemestre_id,
|
formsemestre_id=formsemestre.id,
|
||||||
etudid=etudid,
|
etudid=etud.id,
|
||||||
format="pdf",
|
format="pdf",
|
||||||
version=version,
|
version=version,
|
||||||
),
|
),
|
||||||
@ -1272,8 +1089,8 @@ def _formsemestre_bulletinetud_header_html(
|
|||||||
"""</form></span></td><td class="bull_photo"><a href="%s">%s</a>
|
"""</form></span></td><td class="bull_photo"><a href="%s">%s</a>
|
||||||
"""
|
"""
|
||||||
% (
|
% (
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id),
|
||||||
sco_photos.etud_photo_html(etud, title="fiche de " + etud["nom"]),
|
sco_photos.etud_photo_html(etud, title="fiche de " + etud.nomprenom),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
H.append(
|
H.append(
|
||||||
@ -1283,3 +1100,177 @@ def _formsemestre_bulletinetud_header_html(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return "".join(H)
|
return "".join(H)
|
||||||
|
|
||||||
|
|
||||||
|
def make_menu_autres_operations(
|
||||||
|
formsemestre: FormSemestre, etud: Identite, endpoint: str, version: str
|
||||||
|
) -> str:
|
||||||
|
etud_email = etud.get_first_email() or ""
|
||||||
|
etud_perso = etud.get_first_email("emailperso") or ""
|
||||||
|
menu_items = [
|
||||||
|
{
|
||||||
|
"title": "Réglages bulletins",
|
||||||
|
"endpoint": "notes.formsemestre_edit_options",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
# "target_url": url_for(
|
||||||
|
# "notes.formsemestre_bulletinetud",
|
||||||
|
# scodoc_dept=g.scodoc_dept,
|
||||||
|
# formsemestre_id=formsemestre_id,
|
||||||
|
# etudid=etudid,
|
||||||
|
# ),
|
||||||
|
},
|
||||||
|
"enabled": formsemestre.can_be_edited_by(current_user),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": 'Version papier (pdf, format "%s")'
|
||||||
|
% sco_bulletins_generator.bulletin_get_class_name_displayed(
|
||||||
|
formsemestre.id
|
||||||
|
),
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
"version": version,
|
||||||
|
"format": "pdf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": f"Envoi par mail à {etud_email}",
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
"version": version,
|
||||||
|
"format": "pdfmail",
|
||||||
|
},
|
||||||
|
# possible slt si on a un mail...
|
||||||
|
"enabled": etud_email and can_send_bulletin_by_mail(formsemestre.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": f"Envoi par mail à {etud_perso} (adr. personnelle)",
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
"version": version,
|
||||||
|
"format": "pdfmail",
|
||||||
|
"prefer_mail_perso": 1,
|
||||||
|
},
|
||||||
|
# possible slt si on a un mail...
|
||||||
|
"enabled": etud_perso and can_send_bulletin_by_mail(formsemestre.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Version json",
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
"version": version,
|
||||||
|
"format": "json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Version XML",
|
||||||
|
"endpoint": endpoint,
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
"version": version,
|
||||||
|
"format": "xml",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Ajouter une appréciation",
|
||||||
|
"endpoint": "notes.appreciation_add_form",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": (
|
||||||
|
formsemestre.can_be_edited_by(current_user)
|
||||||
|
or current_user.has_permission(Permission.ScoEtudInscrit)
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Enregistrer un semestre effectué ailleurs",
|
||||||
|
"endpoint": "notes.formsemestre_ext_create_form",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": current_user.has_permission(Permission.ScoImplement),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Enregistrer une validation d'UE antérieure",
|
||||||
|
"endpoint": "notes.formsemestre_validate_previous_ue",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Enregistrer note d'une UE externe",
|
||||||
|
"endpoint": "notes.external_ue_create_form",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Entrer décisions jury",
|
||||||
|
"endpoint": "notes.formsemestre_validation_etud_form",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": sco_permissions_check.can_validate_sem(formsemestre.id),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Éditer PV jury",
|
||||||
|
"endpoint": "notes.formsemestre_pvjury_pdf",
|
||||||
|
"args": {
|
||||||
|
"formsemestre_id": formsemestre.id,
|
||||||
|
"etudid": etud.id,
|
||||||
|
},
|
||||||
|
"enabled": True,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
return htmlutils.make_menu("Autres opérations", menu_items, alone=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _formsemestre_bulletinetud_header_html(
|
||||||
|
etud,
|
||||||
|
formsemestre: FormSemestre,
|
||||||
|
format=None,
|
||||||
|
version=None,
|
||||||
|
):
|
||||||
|
H = [
|
||||||
|
html_sco_header.sco_header(
|
||||||
|
page_title=f"Bulletin de {etud.nomprenom}",
|
||||||
|
javascripts=[
|
||||||
|
"js/bulletin.js",
|
||||||
|
"libjs/d3.v3.min.js",
|
||||||
|
"js/radar_bulletin.js",
|
||||||
|
],
|
||||||
|
cssstyles=["css/radar_bulletin.css"],
|
||||||
|
),
|
||||||
|
render_template(
|
||||||
|
"bul_head.html",
|
||||||
|
etud=etud,
|
||||||
|
format=format,
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
menu_autres_operations=make_menu_autres_operations(
|
||||||
|
etud=etud,
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
endpoint="notes.formsemestre_bulletinetud",
|
||||||
|
version=version,
|
||||||
|
),
|
||||||
|
scu=scu,
|
||||||
|
time=time,
|
||||||
|
version=version,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
return "\n".join(H)
|
||||||
|
@ -63,48 +63,14 @@ from app.scodoc import sco_pdf
|
|||||||
from app.scodoc.sco_pdf import PDFLOCK
|
from app.scodoc.sco_pdf import PDFLOCK
|
||||||
import sco_version
|
import sco_version
|
||||||
|
|
||||||
# Liste des types des classes de générateurs de bulletins PDF:
|
|
||||||
BULLETIN_CLASSES = collections.OrderedDict()
|
|
||||||
|
|
||||||
|
class BulletinGenerator:
|
||||||
def register_bulletin_class(klass):
|
|
||||||
BULLETIN_CLASSES[klass.__name__] = klass
|
|
||||||
|
|
||||||
|
|
||||||
def bulletin_class_descriptions():
|
|
||||||
return [x.description for x in BULLETIN_CLASSES.values()]
|
|
||||||
|
|
||||||
|
|
||||||
def bulletin_class_names():
|
|
||||||
return list(BULLETIN_CLASSES.keys())
|
|
||||||
|
|
||||||
|
|
||||||
def bulletin_default_class_name():
|
|
||||||
return bulletin_class_names()[0]
|
|
||||||
|
|
||||||
|
|
||||||
def bulletin_get_class(class_name):
|
|
||||||
return BULLETIN_CLASSES[class_name]
|
|
||||||
|
|
||||||
|
|
||||||
def bulletin_get_class_name_displayed(formsemestre_id):
|
|
||||||
"""Le nom du générateur utilisé, en clair"""
|
|
||||||
from app.scodoc import sco_preferences
|
|
||||||
|
|
||||||
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
|
|
||||||
try:
|
|
||||||
gen_class = bulletin_get_class(bul_class_name)
|
|
||||||
return gen_class.description
|
|
||||||
except:
|
|
||||||
return "invalide ! (voir paramètres)"
|
|
||||||
|
|
||||||
|
|
||||||
class BulletinGenerator(object):
|
|
||||||
"Virtual superclass for PDF bulletin generators" ""
|
"Virtual superclass for PDF bulletin generators" ""
|
||||||
# Here some helper methods
|
# Here some helper methods
|
||||||
# see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods
|
# see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods
|
||||||
supported_formats = [] # should list supported formats, eg [ 'html', 'pdf' ]
|
supported_formats = [] # should list supported formats, eg [ 'html', 'pdf' ]
|
||||||
description = "superclass for bulletins" # description for user interface
|
description = "superclass for bulletins" # description for user interface
|
||||||
|
list_in_menu = True # la classe doit-elle est montrée dans le menu de config ?
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -151,7 +117,7 @@ class BulletinGenerator(object):
|
|||||||
def get_filename(self):
|
def get_filename(self):
|
||||||
"""Build a filename to be proposed to the web client"""
|
"""Build a filename to be proposed to the web client"""
|
||||||
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"])
|
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"])
|
||||||
return scu.bul_filename(sem, self.infos["etud"], "pdf")
|
return scu.bul_filename_old(sem, self.infos["etud"], "pdf")
|
||||||
|
|
||||||
def generate(self, format="", stand_alone=True):
|
def generate(self, format="", stand_alone=True):
|
||||||
"""Return bulletin in specified format"""
|
"""Return bulletin in specified format"""
|
||||||
@ -270,9 +236,14 @@ def make_formsemestre_bulletinetud(
|
|||||||
|
|
||||||
formsemestre_id = infos["formsemestre_id"]
|
formsemestre_id = infos["formsemestre_id"]
|
||||||
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
|
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
|
||||||
try:
|
|
||||||
|
gen_class = None
|
||||||
|
if infos.get("type") == "BUT" and format.startswith("pdf"):
|
||||||
|
gen_class = bulletin_get_class(bul_class_name + "BUT")
|
||||||
|
if gen_class is None:
|
||||||
gen_class = bulletin_get_class(bul_class_name)
|
gen_class = bulletin_get_class(bul_class_name)
|
||||||
except:
|
|
||||||
|
if gen_class is None:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"Type de bulletin PDF invalide (paramètre: %s)" % bul_class_name
|
"Type de bulletin PDF invalide (paramètre: %s)" % bul_class_name
|
||||||
)
|
)
|
||||||
@ -313,3 +284,52 @@ def make_formsemestre_bulletinetud(
|
|||||||
filename = bul_generator.get_filename()
|
filename = bul_generator.get_filename()
|
||||||
|
|
||||||
return data, filename
|
return data, filename
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
|
||||||
|
# Liste des types des classes de générateurs de bulletins PDF:
|
||||||
|
BULLETIN_CLASSES = collections.OrderedDict()
|
||||||
|
|
||||||
|
|
||||||
|
def register_bulletin_class(klass):
|
||||||
|
BULLETIN_CLASSES[klass.__name__] = klass
|
||||||
|
|
||||||
|
|
||||||
|
def bulletin_class_descriptions():
|
||||||
|
return [
|
||||||
|
BULLETIN_CLASSES[class_name].description
|
||||||
|
for class_name in BULLETIN_CLASSES
|
||||||
|
if BULLETIN_CLASSES[class_name].list_in_menu
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def bulletin_class_names() -> list[str]:
|
||||||
|
"Liste les noms des classes de bulletins à présenter à l'utilisateur"
|
||||||
|
return [
|
||||||
|
class_name
|
||||||
|
for class_name in BULLETIN_CLASSES
|
||||||
|
if BULLETIN_CLASSES[class_name].list_in_menu
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def bulletin_default_class_name():
|
||||||
|
return bulletin_class_names()[0]
|
||||||
|
|
||||||
|
|
||||||
|
def bulletin_get_class(class_name: str) -> BulletinGenerator:
|
||||||
|
"""La class de génération de bulletin de ce nom,
|
||||||
|
ou None si pas trouvée
|
||||||
|
"""
|
||||||
|
return BULLETIN_CLASSES.get(class_name)
|
||||||
|
|
||||||
|
|
||||||
|
def bulletin_get_class_name_displayed(formsemestre_id):
|
||||||
|
"""Le nom du générateur utilisé, en clair"""
|
||||||
|
from app.scodoc import sco_preferences
|
||||||
|
|
||||||
|
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
|
||||||
|
gen_class = bulletin_get_class(bul_class_name)
|
||||||
|
if gen_class is None:
|
||||||
|
return "invalide ! (voir paramètres)"
|
||||||
|
return gen_class.description
|
||||||
|
@ -138,7 +138,7 @@ def formsemestre_bulletinetud_published_dict(
|
|||||||
if not published:
|
if not published:
|
||||||
return d # stop !
|
return d # stop !
|
||||||
|
|
||||||
etat_inscription = etud.etat_inscription(formsemestre.id)
|
etat_inscription = etud.inscription_etat(formsemestre.id)
|
||||||
if etat_inscription != scu.INSCRIT:
|
if etat_inscription != scu.INSCRIT:
|
||||||
d.update(dict_decision_jury(etudid, formsemestre_id, with_decisions=True))
|
d.update(dict_decision_jury(etudid, formsemestre_id, with_decisions=True))
|
||||||
return d
|
return d
|
||||||
|
@ -61,12 +61,10 @@ from reportlab.platypus.doctemplate import BaseDocTemplate
|
|||||||
from flask import g, request
|
from flask import g, request
|
||||||
|
|
||||||
from app import log, ScoValueError
|
from app import log, ScoValueError
|
||||||
from app.comp import res_sem
|
|
||||||
from app.comp.res_common import NotesTableCompat
|
|
||||||
from app.models import FormSemestre
|
from app.models import FormSemestre
|
||||||
|
|
||||||
from app.scodoc import sco_cache
|
from app.scodoc import sco_cache
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_codes_parcours
|
||||||
from app.scodoc import sco_pdf
|
from app.scodoc import sco_pdf
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
@ -190,7 +188,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
|
|||||||
i = 1
|
i = 1
|
||||||
for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
|
for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
|
||||||
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
|
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre,
|
||||||
etud.id,
|
etud.id,
|
||||||
format="pdfpart",
|
format="pdfpart",
|
||||||
version=version,
|
version=version,
|
||||||
@ -239,8 +237,9 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
|
|||||||
filigrannes = {}
|
filigrannes = {}
|
||||||
i = 1
|
i = 1
|
||||||
for sem in etud["sems"]:
|
for sem in etud["sems"]:
|
||||||
|
formsemestre = FormSemestre.query.get(sem["formsemestre_id"])
|
||||||
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
|
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
|
||||||
sem["formsemestre_id"],
|
formsemestre,
|
||||||
etudid,
|
etudid,
|
||||||
format="pdfpart",
|
format="pdfpart",
|
||||||
version=version,
|
version=version,
|
||||||
@ -275,3 +274,16 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return pdfdoc, filename
|
return pdfdoc, filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_filigranne(etud_etat: str, prefs, decision_sem=None) -> str:
|
||||||
|
"""Texte à placer en "filigranne" sur le bulletin pdf"""
|
||||||
|
if etud_etat == scu.DEMISSION:
|
||||||
|
return "Démission"
|
||||||
|
elif etud_etat == sco_codes_parcours.DEF:
|
||||||
|
return "Défaillant"
|
||||||
|
elif (prefs["bul_show_temporary"] and not decision_sem) or prefs[
|
||||||
|
"bul_show_temporary_forced"
|
||||||
|
]:
|
||||||
|
return prefs["bul_temporary_txt"]
|
||||||
|
return ""
|
||||||
|
@ -66,7 +66,8 @@ from app.scodoc import sco_groups
|
|||||||
from app.scodoc import sco_evaluations
|
from app.scodoc import sco_evaluations
|
||||||
from app.scodoc import gen_tables
|
from app.scodoc import gen_tables
|
||||||
|
|
||||||
# Important: Le nom de la classe ne doit pas changer (bien le choisir), car il sera stocké en base de données (dans les préférences)
|
# Important: Le nom de la classe ne doit pas changer (bien le choisir),
|
||||||
|
# car il sera stocké en base de données (dans les préférences)
|
||||||
class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
||||||
description = "standard ScoDoc (version 2011)" # la description doit être courte: elle apparait dans le menu de paramètrage ScoDoc
|
description = "standard ScoDoc (version 2011)" # la description doit être courte: elle apparait dans le menu de paramètrage ScoDoc
|
||||||
supported_formats = ["html", "pdf"]
|
supported_formats = ["html", "pdf"]
|
||||||
@ -264,11 +265,11 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
|
|
||||||
def build_bulletin_table(self):
|
def build_bulletin_table(self):
|
||||||
"""Génère la table centrale du bulletin de notes
|
"""Génère la table centrale du bulletin de notes
|
||||||
Renvoie: colkeys, P, pdf_style, colWidths
|
Renvoie: col_keys, P, pdf_style, col_widths
|
||||||
- colkeys: nom des colonnes de la table (clés)
|
- col_keys: nom des colonnes de la table (clés)
|
||||||
- table (liste de dicts de chaines de caracteres)
|
- table: liste de dicts de chaines de caractères
|
||||||
- style (commandes table Platypus)
|
- pdf_style: commandes table Platypus
|
||||||
- largeurs de colonnes pour PDF
|
- col_widths: largeurs de colonnes pour PDF
|
||||||
"""
|
"""
|
||||||
I = self.infos
|
I = self.infos
|
||||||
P = [] # elems pour générer table avec gen_table (liste de dicts)
|
P = [] # elems pour générer table avec gen_table (liste de dicts)
|
||||||
@ -287,25 +288,25 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
with_col_coef = prefs["bul_show_coef"] or prefs["bul_show_ue_coef"]
|
with_col_coef = prefs["bul_show_coef"] or prefs["bul_show_ue_coef"]
|
||||||
with_col_ects = prefs["bul_show_ects"]
|
with_col_ects = prefs["bul_show_ects"]
|
||||||
|
|
||||||
colkeys = ["titre", "module"] # noms des colonnes à afficher
|
col_keys = ["titre", "module"] # noms des colonnes à afficher
|
||||||
if with_col_rang:
|
if with_col_rang:
|
||||||
colkeys += ["rang"]
|
col_keys += ["rang"]
|
||||||
if with_col_minmax:
|
if with_col_minmax:
|
||||||
colkeys += ["min"]
|
col_keys += ["min"]
|
||||||
if with_col_moypromo:
|
if with_col_moypromo:
|
||||||
colkeys += ["moy"]
|
col_keys += ["moy"]
|
||||||
if with_col_minmax:
|
if with_col_minmax:
|
||||||
colkeys += ["max"]
|
col_keys += ["max"]
|
||||||
colkeys += ["note"]
|
col_keys += ["note"]
|
||||||
if with_col_coef:
|
if with_col_coef:
|
||||||
colkeys += ["coef"]
|
col_keys += ["coef"]
|
||||||
if with_col_ects:
|
if with_col_ects:
|
||||||
colkeys += ["ects"]
|
col_keys += ["ects"]
|
||||||
if with_col_abs:
|
if with_col_abs:
|
||||||
colkeys += ["abs"]
|
col_keys += ["abs"]
|
||||||
colidx = {} # { nom_colonne : indice à partir de 0 } (pour styles platypus)
|
colidx = {} # { nom_colonne : indice à partir de 0 } (pour styles platypus)
|
||||||
i = 0
|
i = 0
|
||||||
for k in colkeys:
|
for k in col_keys:
|
||||||
colidx[k] = i
|
colidx[k] = i
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
@ -313,7 +314,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
bul_pdf_mod_colwidth = float(prefs["bul_pdf_mod_colwidth"]) * cm
|
bul_pdf_mod_colwidth = float(prefs["bul_pdf_mod_colwidth"]) * cm
|
||||||
else:
|
else:
|
||||||
bul_pdf_mod_colwidth = None
|
bul_pdf_mod_colwidth = None
|
||||||
colWidths = {
|
col_widths = {
|
||||||
"titre": None,
|
"titre": None,
|
||||||
"module": bul_pdf_mod_colwidth,
|
"module": bul_pdf_mod_colwidth,
|
||||||
"min": 1.5 * cm,
|
"min": 1.5 * cm,
|
||||||
@ -541,7 +542,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
|
|||||||
("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu:
|
("BOX", (0, 0), (-1, -1), 0.4, blue), # ajoute cadre extérieur bleu:
|
||||||
]
|
]
|
||||||
#
|
#
|
||||||
return colkeys, P, pdf_style, colWidths
|
return col_keys, P, pdf_style, col_widths
|
||||||
|
|
||||||
def _list_modules(
|
def _list_modules(
|
||||||
self,
|
self,
|
||||||
|
@ -512,8 +512,8 @@ def module_edit(module_id=None):
|
|||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
mat_names = ["%s / %s" % (mat.ue.acronyme, mat.titre or "") for mat in matieres]
|
mat_names = ["%s / %s" % (mat.ue.acronyme, mat.titre or "") for mat in matieres]
|
||||||
ue_mat_ids = ["%s!%s" % (mat.ue.id, mat.id) for mat in matieres]
|
|
||||||
|
|
||||||
|
ue_mat_ids = ["%s!%s" % (mat.ue.id, mat.id) for mat in matieres]
|
||||||
module["ue_matiere_id"] = "%s!%s" % (module["ue_id"], module["matiere_id"])
|
module["ue_matiere_id"] = "%s!%s" % (module["ue_id"], module["matiere_id"])
|
||||||
|
|
||||||
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
semestres_indices = list(range(1, parcours.NB_SEM + 1))
|
||||||
@ -748,8 +748,11 @@ def module_edit(module_id=None):
|
|||||||
else:
|
else:
|
||||||
# l'UE de rattachement peut changer
|
# l'UE de rattachement peut changer
|
||||||
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
|
||||||
|
x, y = tf[2]["ue_matiere_id"].split("!")
|
||||||
|
tf[2]["ue_id"] = int(x)
|
||||||
|
tf[2]["matiere_id"] = int(y)
|
||||||
old_ue_id = a_module.ue.id
|
old_ue_id = a_module.ue.id
|
||||||
new_ue_id = int(tf[2]["ue_id"])
|
new_ue_id = tf[2]["ue_id"]
|
||||||
if (old_ue_id != new_ue_id) and in_use:
|
if (old_ue_id != new_ue_id) and in_use:
|
||||||
new_ue = UniteEns.query.get_or_404(new_ue_id)
|
new_ue = UniteEns.query.get_or_404(new_ue_id)
|
||||||
if new_ue.semestre_idx != a_module.ue.semestre_idx:
|
if new_ue.semestre_idx != a_module.ue.semestre_idx:
|
||||||
|
@ -954,13 +954,13 @@ def _ue_table_ues(
|
|||||||
|
|
||||||
if cur_ue_semestre_id != ue["semestre_id"]:
|
if cur_ue_semestre_id != ue["semestre_id"]:
|
||||||
cur_ue_semestre_id = ue["semestre_id"]
|
cur_ue_semestre_id = ue["semestre_id"]
|
||||||
# if iue > 0:
|
|
||||||
# H.append("</ul>")
|
|
||||||
if ue["semestre_id"] == sco_codes_parcours.UE_SEM_DEFAULT:
|
if ue["semestre_id"] == sco_codes_parcours.UE_SEM_DEFAULT:
|
||||||
lab = "Pas d'indication de semestre:"
|
lab = "Pas d'indication de semestre:"
|
||||||
else:
|
else:
|
||||||
lab = "Semestre %s:" % ue["semestre_id"]
|
lab = "Semestre %s:" % ue["semestre_id"]
|
||||||
H.append('<div class="ue_list_tit_sem">%s</div>' % lab)
|
H.append(
|
||||||
|
'<div class="ue_list_div"><div class="ue_list_tit_sem">%s</div>' % lab
|
||||||
|
)
|
||||||
H.append('<ul class="notes_ue_list">')
|
H.append('<ul class="notes_ue_list">')
|
||||||
H.append('<li class="notes_ue_list">')
|
H.append('<li class="notes_ue_list">')
|
||||||
if iue != 0 and editable:
|
if iue != 0 and editable:
|
||||||
@ -1028,7 +1028,9 @@ def _ue_table_ues(
|
|||||||
H.append(
|
H.append(
|
||||||
f"""</ul><ul><li><a href="{url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
|
f"""</ul><ul><li><a href="{url_for('notes.ue_create', scodoc_dept=g.scodoc_dept,
|
||||||
formation_id=ue['formation_id'], semestre_idx=ue['semestre_id'])
|
formation_id=ue['formation_id'], semestre_idx=ue['semestre_id'])
|
||||||
}">Ajouter une UE dans le semestre {ue['semestre_id'] or ''}</a></li></ul>"""
|
}">Ajouter une UE dans le semestre {ue['semestre_id'] or ''}</a></li></ul>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
iue += 1
|
iue += 1
|
||||||
|
|
||||||
|
@ -33,8 +33,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
|
||||||
from flask import url_for, g, request
|
from flask import url_for, g
|
||||||
from flask_mail import Message
|
|
||||||
|
|
||||||
from app import email
|
from app import email
|
||||||
from app import log
|
from app import log
|
||||||
@ -46,7 +45,6 @@ from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
|||||||
from app.scodoc import safehtml
|
from app.scodoc import safehtml
|
||||||
from app.scodoc import sco_preferences
|
from app.scodoc import sco_preferences
|
||||||
from app.scodoc.scolog import logdb
|
from app.scodoc.scolog import logdb
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
|
||||||
|
|
||||||
|
|
||||||
def format_etud_ident(etud):
|
def format_etud_ident(etud):
|
||||||
@ -860,7 +858,7 @@ def list_scolog(etudid):
|
|||||||
return cursor.dictfetchall()
|
return cursor.dictfetchall()
|
||||||
|
|
||||||
|
|
||||||
def fill_etuds_info(etuds, add_admission=True):
|
def fill_etuds_info(etuds: list[dict], add_admission=True):
|
||||||
"""etuds est une liste d'etudiants (mappings)
|
"""etuds est une liste d'etudiants (mappings)
|
||||||
Pour chaque etudiant, ajoute ou formatte les champs
|
Pour chaque etudiant, ajoute ou formatte les champs
|
||||||
-> informations pour fiche etudiant ou listes diverses
|
-> informations pour fiche etudiant ou listes diverses
|
||||||
@ -977,7 +975,10 @@ def etud_inscriptions_infos(etudid: int, ne="") -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def descr_situation_etud(etudid: int, ne="") -> str:
|
def descr_situation_etud(etudid: int, ne="") -> str:
|
||||||
"""chaîne décrivant la situation actuelle de l'étudiant"""
|
"""Chaîne décrivant la situation actuelle de l'étudiant
|
||||||
|
XXX Obsolete, utiliser Identite.descr_situation_etud() dans
|
||||||
|
les nouveaux codes
|
||||||
|
"""
|
||||||
from app.scodoc import sco_formsemestre
|
from app.scodoc import sco_formsemestre
|
||||||
|
|
||||||
cnx = ndb.GetDBConnexion()
|
cnx = ndb.GetDBConnexion()
|
||||||
|
@ -180,7 +180,9 @@ def search_etud_in_dept(expnom=""):
|
|||||||
e["_nomprenom_target"] = target
|
e["_nomprenom_target"] = target
|
||||||
e["inscription_target"] = target
|
e["inscription_target"] = target
|
||||||
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
e["_nomprenom_td_attrs"] = 'id="%s" class="etudinfo"' % (e["etudid"])
|
||||||
sco_groups.etud_add_group_infos(e, e["cursem"])
|
sco_groups.etud_add_group_infos(
|
||||||
|
e, e["cursem"]["formsemestre_id"] if e["cursem"] else None
|
||||||
|
)
|
||||||
|
|
||||||
tab = GenTable(
|
tab = GenTable(
|
||||||
columns_ids=("nomprenom", "code_nip", "inscription", "groupes"),
|
columns_ids=("nomprenom", "code_nip", "inscription", "groupes"),
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import url_for
|
from flask import render_template, url_for
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
from app import log
|
from app import log
|
||||||
@ -411,7 +411,7 @@ def formsemestre_status_menubar(sem):
|
|||||||
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
|
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Editer les PV et archiver les résultats",
|
"title": "Éditer les PV et archiver les résultats",
|
||||||
"endpoint": "notes.formsemestre_archive",
|
"endpoint": "notes.formsemestre_archive",
|
||||||
"args": {"formsemestre_id": formsemestre_id},
|
"args": {"formsemestre_id": formsemestre_id},
|
||||||
"enabled": sco_permissions_check.can_edit_pv(formsemestre_id),
|
"enabled": sco_permissions_check.can_edit_pv(formsemestre_id),
|
||||||
@ -445,6 +445,7 @@ def retreive_formsemestre_from_request() -> int:
|
|||||||
"""Cherche si on a de quoi déduire le semestre affiché à partir des
|
"""Cherche si on a de quoi déduire le semestre affiché à partir des
|
||||||
arguments de la requête:
|
arguments de la requête:
|
||||||
formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id
|
formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id
|
||||||
|
Returns None si pas défini.
|
||||||
"""
|
"""
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
args = request.args
|
args = request.args
|
||||||
@ -505,34 +506,17 @@ def formsemestre_page_title():
|
|||||||
return ""
|
return ""
|
||||||
try:
|
try:
|
||||||
formsemestre_id = int(formsemestre_id)
|
formsemestre_id = int(formsemestre_id)
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id).copy()
|
formsemestre = FormSemestre.query.get(formsemestre_id)
|
||||||
except:
|
except:
|
||||||
log("can't find formsemestre_id %s" % formsemestre_id)
|
log("can't find formsemestre_id %s" % formsemestre_id)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
fill_formsemestre(sem)
|
h = render_template(
|
||||||
|
"formsemestre_page_title.html",
|
||||||
h = f"""<div class="formsemestre_page_title">
|
formsemestre=formsemestre,
|
||||||
<div class="infos">
|
scu=scu,
|
||||||
<span class="semtitle"><a class="stdlink" title="{sem['session_id']}"
|
sem_menu_bar=formsemestre_status_menubar(formsemestre.to_dict()),
|
||||||
href="{url_for('notes.formsemestre_status',
|
)
|
||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=sem['formsemestre_id'])}"
|
|
||||||
>{sem['titre']}</a><a
|
|
||||||
title="{sem['etape_apo_str']}">{sem['num_sem']}</a>{sem['modalitestr']}</span><span
|
|
||||||
class="dates"><a
|
|
||||||
title="du {sem['date_debut']} au {sem['date_fin']} "
|
|
||||||
>{sem['mois_debut']} - {sem['mois_fin']}</a></span><span
|
|
||||||
class="resp"><a title="{sem['nomcomplet']}">{sem['resp']}</a></span><span
|
|
||||||
class="nbinscrits"><a class="discretelink"
|
|
||||||
href="{url_for("scolar.groups_view",
|
|
||||||
scodoc_dept=g.scodoc_dept, formsemestre_id=sem['formsemestre_id'])}"
|
|
||||||
>{sem['nbinscrits']} inscrits</a></span><span
|
|
||||||
class="lock">{sem['locklink']}</span><span
|
|
||||||
class="eye">{sem['eyelink']}</span>
|
|
||||||
</div>
|
|
||||||
{formsemestre_status_menubar(sem)}
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
|
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ def get_group_infos(group_id, etat=None): # was _getlisteetud
|
|||||||
t["etath"] = t["etat"]
|
t["etath"] = t["etat"]
|
||||||
# Add membership for all partitions, 'partition_id' : group
|
# Add membership for all partitions, 'partition_id' : group
|
||||||
for etud in members: # long: comment eviter ces boucles ?
|
for etud in members: # long: comment eviter ces boucles ?
|
||||||
etud_add_group_infos(etud, sem)
|
etud_add_group_infos(etud, sem["formsemestre_id"])
|
||||||
|
|
||||||
if group["group_name"] != None:
|
if group["group_name"] != None:
|
||||||
group_tit = "%s %s" % (group["partition_name"], group["group_name"])
|
group_tit = "%s %s" % (group["partition_name"], group["group_name"])
|
||||||
@ -413,12 +413,12 @@ def formsemestre_get_etud_groupnames(formsemestre_id, attr="group_name"):
|
|||||||
return R
|
return R
|
||||||
|
|
||||||
|
|
||||||
def etud_add_group_infos(etud, sem, sep=" "):
|
def etud_add_group_infos(etud, formsemestre_id, sep=" "):
|
||||||
"""Add informations on partitions and group memberships to etud (a dict with an etudid)"""
|
"""Add informations on partitions and group memberships to etud (a dict with an etudid)"""
|
||||||
etud[
|
etud[
|
||||||
"partitions"
|
"partitions"
|
||||||
] = collections.OrderedDict() # partition_id : group + partition_name
|
] = collections.OrderedDict() # partition_id : group + partition_name
|
||||||
if not sem:
|
if not formsemestre_id:
|
||||||
etud["groupes"] = ""
|
etud["groupes"] = ""
|
||||||
return etud
|
return etud
|
||||||
|
|
||||||
@ -430,7 +430,7 @@ def etud_add_group_infos(etud, sem, sep=" "):
|
|||||||
and p.formsemestre_id = %(formsemestre_id)s
|
and p.formsemestre_id = %(formsemestre_id)s
|
||||||
ORDER BY p.numero
|
ORDER BY p.numero
|
||||||
""",
|
""",
|
||||||
{"etudid": etud["etudid"], "formsemestre_id": sem["formsemestre_id"]},
|
{"etudid": etud["etudid"], "formsemestre_id": formsemestre_id},
|
||||||
)
|
)
|
||||||
|
|
||||||
for info in infos:
|
for info in infos:
|
||||||
@ -439,13 +439,13 @@ def etud_add_group_infos(etud, sem, sep=" "):
|
|||||||
|
|
||||||
# resume textuel des groupes:
|
# resume textuel des groupes:
|
||||||
etud["groupes"] = sep.join(
|
etud["groupes"] = sep.join(
|
||||||
[g["group_name"] for g in infos if g["group_name"] != None]
|
[gr["group_name"] for gr in infos if gr["group_name"] is not None]
|
||||||
)
|
)
|
||||||
etud["partitionsgroupes"] = sep.join(
|
etud["partitionsgroupes"] = sep.join(
|
||||||
[
|
[
|
||||||
g["partition_name"] + ":" + g["group_name"]
|
gr["partition_name"] + ":" + gr["group_name"]
|
||||||
for g in infos
|
for gr in infos
|
||||||
if g["group_name"] != None
|
if gr["group_name"] is not None
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -203,7 +203,7 @@ def sco_import_generate_excel_sample(
|
|||||||
for field in titles:
|
for field in titles:
|
||||||
if field == "groupes":
|
if field == "groupes":
|
||||||
sco_groups.etud_add_group_infos(
|
sco_groups.etud_add_group_infos(
|
||||||
etud, groups_infos.formsemestre, sep=";"
|
etud, groups_infos.formsemestre_id, sep=";"
|
||||||
)
|
)
|
||||||
l.append(etud["partitionsgroupes"])
|
l.append(etud["partitionsgroupes"])
|
||||||
else:
|
else:
|
||||||
|
@ -196,7 +196,10 @@ def do_inscrit(sem, etudids, inscrit_groupes=False):
|
|||||||
if len(etud["sems"]) < 2:
|
if len(etud["sems"]) < 2:
|
||||||
continue
|
continue
|
||||||
prev_formsemestre = etud["sems"][1]
|
prev_formsemestre = etud["sems"][1]
|
||||||
sco_groups.etud_add_group_infos(etud, prev_formsemestre)
|
sco_groups.etud_add_group_infos(
|
||||||
|
etud,
|
||||||
|
prev_formsemestre["formsemestre_id"] if prev_formsemestre else None,
|
||||||
|
)
|
||||||
|
|
||||||
cursem_groups_by_name = dict(
|
cursem_groups_by_name = dict(
|
||||||
[
|
[
|
||||||
|
@ -215,7 +215,9 @@ def ficheEtud(etudid=None):
|
|||||||
info["modifadresse"] = ""
|
info["modifadresse"] = ""
|
||||||
|
|
||||||
# Groupes:
|
# Groupes:
|
||||||
sco_groups.etud_add_group_infos(info, info["cursem"])
|
sco_groups.etud_add_group_infos(
|
||||||
|
info, info["cursem"]["formsemestre_id"] if info["cursem"] else None
|
||||||
|
)
|
||||||
|
|
||||||
# Parcours de l'étudiant
|
# Parcours de l'étudiant
|
||||||
if info["sems"]:
|
if info["sems"]:
|
||||||
|
@ -175,7 +175,7 @@ def etud_photo_is_local(etud: dict, size="small"):
|
|||||||
return photo_pathname(etud["photo_filename"], size=size)
|
return photo_pathname(etud["photo_filename"], size=size)
|
||||||
|
|
||||||
|
|
||||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small"):
|
def etud_photo_html(etud: dict = None, etudid=None, title=None, size="small"):
|
||||||
"""HTML img tag for the photo, either in small size (h90)
|
"""HTML img tag for the photo, either in small size (h90)
|
||||||
or original size (size=="orig")
|
or original size (size=="orig")
|
||||||
"""
|
"""
|
||||||
@ -351,7 +351,8 @@ def copy_portal_photo_to_fs(etud):
|
|||||||
"""Copy the photo from portal (distant website) to local fs.
|
"""Copy the photo from portal (distant website) to local fs.
|
||||||
Returns rel. path or None if copy failed, with a diagnostic message
|
Returns rel. path or None if copy failed, with a diagnostic message
|
||||||
"""
|
"""
|
||||||
sco_etud.format_etud_ident(etud)
|
if "nomprenom" not in etud:
|
||||||
|
sco_etud.format_etud_ident(etud)
|
||||||
url = photo_portal_url(etud)
|
url = photo_portal_url(etud)
|
||||||
if not url:
|
if not url:
|
||||||
return None, "%(nomprenom)s: pas de code NIP" % etud
|
return None, "%(nomprenom)s: pas de code NIP" % etud
|
||||||
|
@ -2138,7 +2138,7 @@ class BasePreferences(object):
|
|||||||
return form
|
return form
|
||||||
|
|
||||||
|
|
||||||
class SemPreferences(object):
|
class SemPreferences:
|
||||||
"""Preferences for a formsemestre"""
|
"""Preferences for a formsemestre"""
|
||||||
|
|
||||||
def __init__(self, formsemestre_id=None):
|
def __init__(self, formsemestre_id=None):
|
||||||
@ -2294,9 +2294,8 @@ def doc_preferences():
|
|||||||
return "\n".join([" | ".join(x) for x in L])
|
return "\n".join([" | ".join(x) for x in L])
|
||||||
|
|
||||||
|
|
||||||
def bulletin_option_affichage(formsemestre_id: int) -> dict:
|
def bulletin_option_affichage(formsemestre_id: int, prefs: SemPreferences) -> dict:
|
||||||
"dict avec les options d'affichages (préférences) pour ce semestre"
|
"dict avec les options d'affichages (préférences) pour ce semestre"
|
||||||
prefs = SemPreferences(formsemestre_id)
|
|
||||||
fields = (
|
fields = (
|
||||||
"bul_show_abs",
|
"bul_show_abs",
|
||||||
"bul_show_abs_modules",
|
"bul_show_abs_modules",
|
||||||
|
@ -608,7 +608,7 @@ def is_valid_filename(filename):
|
|||||||
return VALID_EXP.match(filename)
|
return VALID_EXP.match(filename)
|
||||||
|
|
||||||
|
|
||||||
def bul_filename(sem, etud, format):
|
def bul_filename_old(sem: dict, etud: dict, format):
|
||||||
"""Build a filename for this bulletin"""
|
"""Build a filename for this bulletin"""
|
||||||
dt = time.strftime("%Y-%m-%d")
|
dt = time.strftime("%Y-%m-%d")
|
||||||
filename = f"bul-{sem['titre_num']}-{dt}-{etud['nom']}.{format}"
|
filename = f"bul-{sem['titre_num']}-{dt}-{etud['nom']}.{format}"
|
||||||
@ -616,6 +616,14 @@ def bul_filename(sem, etud, format):
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def bul_filename(formsemestre, etud, format):
|
||||||
|
"""Build a filename for this bulletin"""
|
||||||
|
dt = time.strftime("%Y-%m-%d")
|
||||||
|
filename = f"bul-{formsemestre.titre_num()}-{dt}-{etud.nom}.{format}"
|
||||||
|
filename = make_filename(filename)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def flash_errors(form):
|
def flash_errors(form):
|
||||||
"""Flashes form errors (version sommaire)"""
|
"""Flashes form errors (version sommaire)"""
|
||||||
for field, errors in form.errors.items():
|
for field, errors in form.errors.items():
|
||||||
|
@ -14,16 +14,25 @@
|
|||||||
}
|
}
|
||||||
main{
|
main{
|
||||||
--couleurPrincipale: rgb(240,250,255);
|
--couleurPrincipale: rgb(240,250,255);
|
||||||
--couleurFondTitresUE: rgb(206,255,235);
|
--couleurFondTitresUE: #b6ebff;
|
||||||
--couleurFondTitresRes: rgb(125, 170, 255);
|
--couleurFondTitresRes: #f8c844;
|
||||||
--couleurFondTitresSAE: rgb(211, 255, 255);
|
--couleurFondTitresSAE: #c6ffab;
|
||||||
--couleurSecondaire: #fec;
|
--couleurSecondaire: #fec;
|
||||||
--couleurIntense: #c09;
|
--couleurIntense: rgb(4, 16, 159);;
|
||||||
--couleurSurlignage: rgba(232, 255, 132, 0.47);
|
--couleurSurlignage: rgba(255, 253, 110, 0.49);
|
||||||
max-width: 1000px;
|
max-width: 1000px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.releve a, .releve a:visited {
|
||||||
|
color: navy;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.releve a:hover {
|
||||||
|
color: red;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.ready .wait{display: none;}
|
.ready .wait{display: none;}
|
||||||
.ready main{display: block;}
|
.ready main{display: block;}
|
||||||
h2{
|
h2{
|
||||||
@ -97,7 +106,8 @@ section>div:nth-child(1){
|
|||||||
.hide_coef .synthese em,
|
.hide_coef .synthese em,
|
||||||
.hide_coef .eval>em,
|
.hide_coef .eval>em,
|
||||||
.hide_date_inscr .dateInscription,
|
.hide_date_inscr .dateInscription,
|
||||||
.hide_ects .ects{
|
.hide_ects .ects,
|
||||||
|
.hide_rangs .rang{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,14 +161,19 @@ section>div:nth-child(1){
|
|||||||
column-gap: 4px;
|
column-gap: 4px;
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
.infoSemestre>div:nth-child(1){
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
.infoSemestre>div>div:nth-child(even){
|
.infoSemestre>div>div:nth-child(even){
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.photo {
|
||||||
|
border: none;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
.rang{
|
.rang{
|
||||||
text-decoration: underline var(--couleurIntense);
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.ue .rang{
|
||||||
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
.decision{
|
.decision{
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
@ -186,6 +201,9 @@ section>div:nth-child(1){
|
|||||||
.synthese h3{
|
.synthese h3{
|
||||||
background: var(--couleurFondTitresUE);
|
background: var(--couleurFondTitresUE);
|
||||||
}
|
}
|
||||||
|
.synthese .ue>div{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
.synthese em,
|
.synthese em,
|
||||||
.eval em{
|
.eval em{
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
@ -206,7 +224,6 @@ section>div:nth-child(1){
|
|||||||
scroll-margin-top: 60px;
|
scroll-margin-top: 60px;
|
||||||
}
|
}
|
||||||
.module, .ue {
|
.module, .ue {
|
||||||
background: var(--couleurSecondaire);
|
|
||||||
color: #000;
|
color: #000;
|
||||||
padding: 4px 32px;
|
padding: 4px 32px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -218,6 +235,15 @@ section>div:nth-child(1){
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
.ue {
|
||||||
|
background: var(--couleurFondTitresRes);
|
||||||
|
}
|
||||||
|
.module {
|
||||||
|
background: var(--couleurFondTitresRes);
|
||||||
|
}
|
||||||
|
.module h3 {
|
||||||
|
background: var(--couleurFondTitresRes);
|
||||||
|
}
|
||||||
.module::before, .ue::before {
|
.module::before, .ue::before {
|
||||||
content:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='26px' height='26px' fill='white'><path d='M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z' /></svg>");
|
content:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='26px' height='26px' fill='white'><path d='M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z' /></svg>");
|
||||||
width: 26px;
|
width: 26px;
|
||||||
@ -308,6 +334,14 @@ h3{
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
section{
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.syntheseModule, .eval {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
/*.absences{
|
/*.absences{
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto auto;
|
grid-template-columns: auto auto;
|
||||||
|
@ -1702,7 +1702,7 @@ ul.notes_ue_list {
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
padding-top: 1em;
|
/* padding-top: 1em; */
|
||||||
padding-bottom: 1em;
|
padding-bottom: 1em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
@ -1767,9 +1767,25 @@ ul.notes_module_list {
|
|||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.ue_list_div {
|
||||||
|
border: 3px solid rgb(35, 0, 160);
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
div.ue_list_tit_sem {
|
div.ue_list_tit_sem {
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
color: orangered;
|
||||||
|
display: list-item; /* This has to be "list-item" */
|
||||||
|
list-style-type: disc; /* See https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type */
|
||||||
|
list-style-position: inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.sco_tag_checkbox {
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notes_ue_list a.stdlink {
|
.notes_ue_list a.stdlink {
|
||||||
@ -1947,7 +1963,20 @@ table.notes_recapcomplet a:hover {
|
|||||||
div.notes_bulletin {
|
div.notes_bulletin {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
div.bull_head {
|
||||||
|
display: grid;
|
||||||
|
justify-content: space-between;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
}
|
||||||
|
div.bull_photo {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
span.bulletin_menubar_but {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 2em;
|
||||||
|
margin-right: 2em;
|
||||||
|
}
|
||||||
table.notes_bulletin {
|
table.notes_bulletin {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 2px solid rgb(100,100,240);
|
border: 2px solid rgb(100,100,240);
|
||||||
@ -2087,12 +2116,6 @@ a.bull_link:hover {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.bull_head {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
td.bull_photo {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.bulletin_menubar {
|
div.bulletin_menubar {
|
||||||
padding-left: 25px;
|
padding-left: 25px;
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 34 KiB |
@ -41,7 +41,7 @@ class releveBUT extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
set showData(data) {
|
set showData(data) {
|
||||||
this.showInformations(data);
|
// this.showInformations(data);
|
||||||
this.showSemestre(data);
|
this.showSemestre(data);
|
||||||
this.showSynthese(data);
|
this.showSynthese(data);
|
||||||
this.showEvaluations(data);
|
this.showEvaluations(data);
|
||||||
@ -68,13 +68,7 @@ class releveBUT extends HTMLElement {
|
|||||||
<div>
|
<div>
|
||||||
<div class="wait"></div>
|
<div class="wait"></div>
|
||||||
<main class="releve">
|
<main class="releve">
|
||||||
<!--------------------------->
|
|
||||||
<!-- Info. étudiant -->
|
|
||||||
<!--------------------------->
|
|
||||||
<section class=etudiant>
|
|
||||||
<img class=studentPic src="" alt="Photo de l'étudiant" width=100 height=120>
|
|
||||||
<div class=infoEtudiant></div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!--------------------------------------------------------------------------------------->
|
<!--------------------------------------------------------------------------------------->
|
||||||
<!-- Zone spéciale pour que les IUT puisse ajouter des infos locales sur la passerelle -->
|
<!-- Zone spéciale pour que les IUT puisse ajouter des infos locales sur la passerelle -->
|
||||||
@ -85,8 +79,8 @@ class releveBUT extends HTMLElement {
|
|||||||
<!-- Semestre -->
|
<!-- Semestre -->
|
||||||
<!--------------------------->
|
<!--------------------------->
|
||||||
<section>
|
<section>
|
||||||
<h2>Semestre </h2>
|
<h2 id="identite_etudiant"></h2>
|
||||||
<div class=flex>
|
<div>
|
||||||
<div class=infoSemestre></div>
|
<div class=infoSemestre></div>
|
||||||
<div>
|
<div>
|
||||||
<div class=decision></div>
|
<div class=decision></div>
|
||||||
@ -103,7 +97,7 @@ class releveBUT extends HTMLElement {
|
|||||||
<section>
|
<section>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<h2>Synthèse</h2>
|
<h2>Unités d'enseignement</h2>
|
||||||
<em>La moyenne des ressources dans une UE dépend des poids donnés aux évaluations.</em>
|
<em>La moyenne des ressources dans une UE dépend des poids donnés aux évaluations.</em>
|
||||||
</div>
|
</div>
|
||||||
<div class=CTA_Liste>
|
<div class=CTA_Liste>
|
||||||
@ -132,7 +126,7 @@ class releveBUT extends HTMLElement {
|
|||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div>
|
<div>
|
||||||
<h2>SAÉ</h2>
|
<h2>Situations d'apprentissage et d'évaluation (SAÉ)</h2>
|
||||||
<div class=CTA_Liste>
|
<div class=CTA_Liste>
|
||||||
Liste <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
Liste <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
<path d="M18 15l-6-6-6 6" />
|
<path d="M18 15l-6-6-6 6" />
|
||||||
@ -198,7 +192,8 @@ class releveBUT extends HTMLElement {
|
|||||||
/* Information sur le semestre */
|
/* Information sur le semestre */
|
||||||
/*******************************/
|
/*******************************/
|
||||||
showSemestre(data) {
|
showSemestre(data) {
|
||||||
this.shadow.querySelector("h2").innerHTML += data.semestre.numero;
|
|
||||||
|
this.shadow.querySelector("#identite_etudiant").innerHTML = ` ${data.etudiant.nomprenom} `;
|
||||||
this.shadow.querySelector(".dateInscription").innerHTML += this.ISOToDate(data.semestre.inscription);
|
this.shadow.querySelector(".dateInscription").innerHTML += this.ISOToDate(data.semestre.inscription);
|
||||||
let output = `
|
let output = `
|
||||||
<div>
|
<div>
|
||||||
@ -212,7 +207,9 @@ class releveBUT extends HTMLElement {
|
|||||||
<div class=enteteSemestre>Absences</div>
|
<div class=enteteSemestre>Absences</div>
|
||||||
<div class=enteteSemestre>N.J. ${data.semestre.absences?.injustifie ?? "-"}</div>
|
<div class=enteteSemestre>N.J. ${data.semestre.absences?.injustifie ?? "-"}</div>
|
||||||
<div style="grid-column: 2">Total ${data.semestre.absences?.total ?? "-"}</div>
|
<div style="grid-column: 2">Total ${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>
|
||||||
|
`;
|
||||||
/*${data.semestre.groupes.map(groupe => {
|
/*${data.semestre.groupes.map(groupe => {
|
||||||
return `
|
return `
|
||||||
<div>
|
<div>
|
||||||
@ -254,6 +251,7 @@ class releveBUT extends HTMLElement {
|
|||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<div class=moyenne>Moyenne : ${dataUE.moyenne?.value || "-"}</div>
|
<div class=moyenne>Moyenne : ${dataUE.moyenne?.value || "-"}</div>
|
||||||
|
<div class=rang>Rang : ${dataUE.moyenne?.rang} / ${dataUE.moyenne?.total}</div>
|
||||||
<div class=info>
|
<div class=info>
|
||||||
Bonus : ${dataUE.bonus || 0} -
|
Bonus : ${dataUE.bonus || 0} -
|
||||||
Malus : ${dataUE.malus || 0}
|
Malus : ${dataUE.malus || 0}
|
||||||
|
34
app/templates/bul_foot.html
Normal file
34
app/templates/bul_foot.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{# -*- mode: jinja-html -*- #}
|
||||||
|
{# Pied des bulletins HTML #}
|
||||||
|
|
||||||
|
<p>Situation actuelle:
|
||||||
|
{% if inscription_courante %}
|
||||||
|
<a class="stdlink" href="{{url_for(
|
||||||
|
"notes.formsemestre_status",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=inscription_courante.formsemestre_id)
|
||||||
|
}}">{{inscription_str}}</a>
|
||||||
|
{% else %}
|
||||||
|
{{inscription_str}}
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% if formsemestre.modalite == "EXT" %}
|
||||||
|
<p><a href="{{
|
||||||
|
url_for('notes.formsemestre_ext_edit_ue_validations',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
etudid=etud.id)}}"
|
||||||
|
class="stdlink">
|
||||||
|
Éditer les validations d'UE dans ce semestre extérieur
|
||||||
|
</a></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# Place du diagramme radar #}
|
||||||
|
<form id="params">
|
||||||
|
<input type="hidden" name="etudid" id="etudid" value="{{etud.id}}"/>
|
||||||
|
<input type="hidden" name="formsemestre_id" id="formsemestre_id" value="{{formsemestre.id}}"/>
|
||||||
|
</form>
|
||||||
|
<div id="radar_bulletin"></div>
|
||||||
|
|
||||||
|
|
57
app/templates/bul_head.html
Normal file
57
app/templates/bul_head.html
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{# -*- mode: jinja-html -*- #}
|
||||||
|
{# L'en-tête des bulletins HTML #}
|
||||||
|
{# was _formsemestre_bulletinetud_header_html #}
|
||||||
|
|
||||||
|
<div class="bull_head">
|
||||||
|
<div class="bull_head_text">
|
||||||
|
{% if not is_apc %}
|
||||||
|
<h2><a class="discretelink" href="{{
|
||||||
|
url_for(
|
||||||
|
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid,
|
||||||
|
)}}">{{etud.nomprenom}}</a></h2>
|
||||||
|
{% endif %}
|
||||||
|
<form name="f" method="GET" action="{{request.base_url}}">
|
||||||
|
<input type="hidden" name="formsemestre_id" value="{{formsemestre.id}}"></input>
|
||||||
|
<input type="hidden" name="etudid" value="{{etud.id}}"></input>
|
||||||
|
<input type="hidden" name="format" value="{{format}}"></input>
|
||||||
|
Bulletin
|
||||||
|
<span class="bull_liensemestre"><a href="{{
|
||||||
|
url_for("notes.formsemestre_status",
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id)}}">{{formsemestre.titre_mois()
|
||||||
|
}}</a></span>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<em>établi le {{time.strftime("%d/%m/%Y à %Hh%M")}} (notes sur 20)</em>
|
||||||
|
<span class="rightjust">
|
||||||
|
<select name="version" onchange="document.f.submit()" class="noprint">
|
||||||
|
{% for (v, e) in (
|
||||||
|
("short", "Version courte"),
|
||||||
|
("selectedevals", "Version intermédiaire"),
|
||||||
|
("long", "Version complète"),
|
||||||
|
) %}
|
||||||
|
<option value="{{v}}" {% if (v == version) %}selected{% endif %}>{{e}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</span>
|
||||||
|
<span class="bulletin_menubar">
|
||||||
|
<span class="bulletin_menubar_but">{{menu_autres_operations|safe}}</span>
|
||||||
|
<a href="{{url_for(
|
||||||
|
'notes.formsemestre_bulletinetud',
|
||||||
|
scodoc_dept=g.scodoc_dept,
|
||||||
|
formsemestre_id=formsemestre.id,
|
||||||
|
etudid=etud.id,
|
||||||
|
format='pdf',
|
||||||
|
version=version,
|
||||||
|
)}}">{{scu.ICON_PDF|safe}}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% if not is_apc %}
|
||||||
|
<div class="bull_photo"><a href="{{
|
||||||
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id)
|
||||||
|
}}">{{etud.photo_html(title="fiche de " + etud["nom"])|safe}}</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
@ -7,8 +7,13 @@
|
|||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
|
|
||||||
|
{% include 'bul_head.html' %}
|
||||||
|
|
||||||
<releve-but></releve-but>
|
<releve-but></releve-but>
|
||||||
<script src="/ScoDoc/static/js/releve-but.js"></script>
|
<script src="/ScoDoc/static/js/releve-but.js"></script>
|
||||||
|
|
||||||
|
{% include 'bul_foot.html' %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let dataSrc = "{{bul_url|safe}}";
|
let dataSrc = "{{bul_url|safe}}";
|
||||||
fetch(dataSrc)
|
fetch(dataSrc)
|
||||||
|
50
app/templates/formsemestre_page_title.html
Normal file
50
app/templates/formsemestre_page_title.html
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
{# -*- mode: jinja-html -*- #}
|
||||||
|
{# Element HTML decrivant un semestre (barre de menu et infos) #}
|
||||||
|
{# was formsemestre_page_title #}
|
||||||
|
|
||||||
|
<div class="formsemestre_page_title">
|
||||||
|
<div class="infos">
|
||||||
|
<span class="semtitle"><a class="stdlink"
|
||||||
|
title="{{formsemestre.session_id}}"
|
||||||
|
href="{{url_for('notes.formsemestre_status',
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)}}"
|
||||||
|
>TATO {{formsemestre.titre}}</a>
|
||||||
|
{%- if formsemestre.semestre_id != -1 -%}
|
||||||
|
<a
|
||||||
|
title="{{formsemestre.etapes_apo_str()
|
||||||
|
}}">, {{
|
||||||
|
formsemestre.formation.get_parcours().SESSION_NAME}}
|
||||||
|
{{formsemestre.semestre_id}}</a>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- if formsemestre.modalite %} en {{formsemestre.modalite}}
|
||||||
|
{%- endif %}</span><span
|
||||||
|
class="dates"><a
|
||||||
|
title="du {{formsemestre.date_debut.strftime('%d/%m/%Y')}}
|
||||||
|
au {{formsemestre.date_fin.strftime('%d/%m/%Y')}} "
|
||||||
|
>{{formsemestre.mois_debut()}} - {{formsemestre.mois_fin()}}</a></span><span
|
||||||
|
class="resp"><a title="{{formsemestre.responsables_str(abbrev_prenom=False)}}">{{formsemestre.responsables_str()}}</a></span><span
|
||||||
|
class="nbinscrits"><a class="discretelink"
|
||||||
|
href="{{url_for('scolar.groups_view',
|
||||||
|
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre.id)
|
||||||
|
}}"
|
||||||
|
>{{formsemestre.etuds_inscriptions|length}} inscrits</a></span><span
|
||||||
|
class="lock">
|
||||||
|
{%-if formsemestre.etat -%}
|
||||||
|
<a href="{{ url_for( 'notes.formsemestre_change_lock',
|
||||||
|
scodoc_dept=scodoc_dept, formsemestre_id=formsemestre.id )}}">{{
|
||||||
|
scu.icontag("lock_img", border="0", title="Semestre verrouillé")|safe
|
||||||
|
}}</a>
|
||||||
|
{%- endif -%}
|
||||||
|
</span><span class="eye"><a href="{{
|
||||||
|
url_for('notes.formsemestre_change_publication_bul',
|
||||||
|
scodoc_dept=scodoc_dept, formsemestre_id=formsemestre.id )
|
||||||
|
}}">{%-
|
||||||
|
if formsemestre.bul_hide_xml -%}}
|
||||||
|
{{scu.icontag("hide_img", border="0", title="Bulletins NON publiés")|safe}}
|
||||||
|
{%- else -%}
|
||||||
|
{{scu.icontag("eye_img", border="0", title="Bulletins publiés")|safe}}
|
||||||
|
{%- endif -%}
|
||||||
|
</a></span>
|
||||||
|
</div>
|
||||||
|
{{sem_menu_bar|safe}}
|
||||||
|
</div>
|
@ -50,27 +50,29 @@ def close_dept_db_connection(arg):
|
|||||||
class ScoData:
|
class ScoData:
|
||||||
"""Classe utilisée pour passer des valeurs aux vues (templates)"""
|
"""Classe utilisée pour passer des valeurs aux vues (templates)"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, etud=None, formsemestre=None):
|
||||||
# Champs utilisés par toutes les pages ScoDoc (sidebar, en-tête)
|
# Champs utilisés par toutes les pages ScoDoc (sidebar, en-tête)
|
||||||
self.Permission = Permission
|
self.Permission = Permission
|
||||||
self.scu = scu
|
self.scu = scu
|
||||||
self.SCOVERSION = sco_version.SCOVERSION
|
self.SCOVERSION = sco_version.SCOVERSION
|
||||||
# -- Informations étudiant courant, si sélectionné:
|
# -- Informations étudiant courant, si sélectionné:
|
||||||
etudid = g.get("etudid", None)
|
if etud is None:
|
||||||
if not etudid:
|
etudid = g.get("etudid", None)
|
||||||
if request.method == "GET":
|
if etudid is None:
|
||||||
etudid = request.args.get("etudid", None)
|
if request.method == "GET":
|
||||||
elif request.method == "POST":
|
etudid = request.args.get("etudid", None)
|
||||||
etudid = request.form.get("etudid", None)
|
elif request.method == "POST":
|
||||||
|
etudid = request.form.get("etudid", None)
|
||||||
if etudid:
|
if etudid is not None:
|
||||||
|
etud = Identite.query.get_or_404(etudid)
|
||||||
|
self.etud = etud
|
||||||
|
if etud is not None:
|
||||||
# Infos sur l'étudiant courant
|
# Infos sur l'étudiant courant
|
||||||
self.etud = Identite.query.get_or_404(etudid)
|
|
||||||
ins = self.etud.inscription_courante()
|
ins = self.etud.inscription_courante()
|
||||||
if ins:
|
if ins:
|
||||||
self.etud_cur_sem = ins.formsemestre
|
self.etud_cur_sem = ins.formsemestre
|
||||||
self.nbabs, self.nbabsjust = sco_abs.get_abs_count_in_interval(
|
self.nbabs, self.nbabsjust = sco_abs.get_abs_count_in_interval(
|
||||||
etudid,
|
etud.id,
|
||||||
self.etud_cur_sem.date_debut.isoformat(),
|
self.etud_cur_sem.date_debut.isoformat(),
|
||||||
self.etud_cur_sem.date_fin.isoformat(),
|
self.etud_cur_sem.date_fin.isoformat(),
|
||||||
)
|
)
|
||||||
@ -80,17 +82,22 @@ class ScoData:
|
|||||||
else:
|
else:
|
||||||
self.etud = None
|
self.etud = None
|
||||||
# --- Informations sur semestre courant, si sélectionné
|
# --- Informations sur semestre courant, si sélectionné
|
||||||
formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request()
|
if formsemestre is None:
|
||||||
if formsemestre_id is None:
|
formsemestre_id = (
|
||||||
|
sco_formsemestre_status.retreive_formsemestre_from_request()
|
||||||
|
)
|
||||||
|
if formsemestre_id is not None:
|
||||||
|
formsemestre = FormSemestre.query.get_or_404(formsemestre_id)
|
||||||
|
if formsemestre is None:
|
||||||
self.sem = None
|
self.sem = None
|
||||||
self.sem_menu_bar = None
|
self.sem_menu_bar = None
|
||||||
else:
|
else:
|
||||||
self.sem = FormSemestre.query.get_or_404(formsemestre_id)
|
self.sem = formsemestre
|
||||||
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
|
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
|
||||||
self.sem.to_dict()
|
self.sem.to_dict()
|
||||||
)
|
)
|
||||||
# --- Préférences
|
# --- Préférences
|
||||||
self.prefs = sco_preferences.SemPreferences(formsemestre_id)
|
self.prefs = sco_preferences.SemPreferences(formsemestre.id)
|
||||||
|
|
||||||
|
|
||||||
from app.views import scodoc, notes, scolar, absences, users, pn_modules, refcomp
|
from app.views import scodoc, notes, scolar, absences, users, pn_modules, refcomp
|
||||||
|
@ -32,6 +32,7 @@ Emmanuel Viennet, 2021
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
import time
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
@ -276,7 +277,7 @@ sco_publish(
|
|||||||
def formsemestre_bulletinetud(
|
def formsemestre_bulletinetud(
|
||||||
etudid=None,
|
etudid=None,
|
||||||
formsemestre_id=None,
|
formsemestre_id=None,
|
||||||
format="html",
|
format=None,
|
||||||
version="long",
|
version="long",
|
||||||
xml_with_decisions=False,
|
xml_with_decisions=False,
|
||||||
force_publishing=False,
|
force_publishing=False,
|
||||||
@ -284,6 +285,7 @@ def formsemestre_bulletinetud(
|
|||||||
code_nip=None,
|
code_nip=None,
|
||||||
code_ine=None,
|
code_ine=None,
|
||||||
):
|
):
|
||||||
|
format = format or "html"
|
||||||
if not formsemestre_id:
|
if not formsemestre_id:
|
||||||
flask.abort(404, "argument manquant: formsemestre_id")
|
flask.abort(404, "argument manquant: formsemestre_id")
|
||||||
if not isinstance(formsemestre_id, int):
|
if not isinstance(formsemestre_id, int):
|
||||||
@ -311,12 +313,16 @@ def formsemestre_bulletinetud(
|
|||||||
if format == "json":
|
if format == "json":
|
||||||
r = bulletin_but.BulletinBUT(formsemestre)
|
r = bulletin_but.BulletinBUT(formsemestre)
|
||||||
return jsonify(
|
return jsonify(
|
||||||
r.bulletin_etud(etud, formsemestre, force_publishing=force_publishing)
|
r.bulletin_etud(
|
||||||
|
etud,
|
||||||
|
formsemestre,
|
||||||
|
force_publishing=force_publishing,
|
||||||
|
version=version,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
elif format == "html":
|
elif format == "html":
|
||||||
return render_template(
|
return render_template(
|
||||||
"but/bulletin.html",
|
"but/bulletin.html",
|
||||||
title=f"Bul. {etud.nom} - BUT",
|
|
||||||
bul_url=url_for(
|
bul_url=url_for(
|
||||||
"notes.formsemestre_bulletinetud",
|
"notes.formsemestre_bulletinetud",
|
||||||
scodoc_dept=g.scodoc_dept,
|
scodoc_dept=g.scodoc_dept,
|
||||||
@ -324,8 +330,21 @@ def formsemestre_bulletinetud(
|
|||||||
etudid=etudid,
|
etudid=etudid,
|
||||||
format="json",
|
format="json",
|
||||||
force_publishing=1, # pour ScoDoc lui même
|
force_publishing=1, # pour ScoDoc lui même
|
||||||
|
version=version,
|
||||||
),
|
),
|
||||||
sco=ScoData(),
|
etud=etud,
|
||||||
|
formsemestre=formsemestre,
|
||||||
|
inscription_courante=etud.inscription_courante(),
|
||||||
|
inscription_str=etud.inscription_descr()["inscription_str"],
|
||||||
|
is_apc=formsemestre.formation.is_apc(),
|
||||||
|
menu_autres_operations=sco_bulletins.make_menu_autres_operations(
|
||||||
|
formsemestre, etud, "notes.formsemestre_bulletinetud", version
|
||||||
|
),
|
||||||
|
sco=ScoData(etud=etud),
|
||||||
|
scu=scu,
|
||||||
|
time=time,
|
||||||
|
title=f"Bul. {etud.nom} - BUT",
|
||||||
|
version=version,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not (etudid or code_nip or code_ine):
|
if not (etudid or code_nip or code_ine):
|
||||||
@ -1929,7 +1948,7 @@ def formsemestre_bulletins_mailetuds(
|
|||||||
nb_send = 0
|
nb_send = 0
|
||||||
for etudid in etudids:
|
for etudid in etudids:
|
||||||
h, _ = sco_bulletins.do_formsemestre_bulletinetud(
|
h, _ = sco_bulletins.do_formsemestre_bulletinetud(
|
||||||
formsemestre_id,
|
formsemestre,
|
||||||
etudid,
|
etudid,
|
||||||
version=version,
|
version=version,
|
||||||
prefer_mail_perso=prefer_mail_perso,
|
prefer_mail_perso=prefer_mail_perso,
|
||||||
|
@ -513,7 +513,7 @@ def etud_info(etudid=None, format="xml"):
|
|||||||
|
|
||||||
sem = etud["cursem"]
|
sem = etud["cursem"]
|
||||||
if sem:
|
if sem:
|
||||||
sco_groups.etud_add_group_infos(etud, sem)
|
sco_groups.etud_add_group_infos(etud, sem["formsemestre_id"] if sem else None)
|
||||||
d["insemestre"] = [
|
d["insemestre"] = [
|
||||||
{
|
{
|
||||||
"current": "1",
|
"current": "1",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# -*- mode: python -*-
|
# -*- mode: python -*-
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
SCOVERSION = "9.1.72"
|
SCOVERSION = "9.2a-72"
|
||||||
|
|
||||||
SCONAME = "ScoDoc"
|
SCONAME = "ScoDoc"
|
||||||
|
|
||||||
|
15
scodoc.py
15
scodoc.py
@ -33,6 +33,7 @@ from app.models.evaluations import Evaluation
|
|||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.views import notes, scolar
|
from app.views import notes, scolar
|
||||||
import tools
|
import tools
|
||||||
|
from tools.fakedatabase import create_test_api_database
|
||||||
|
|
||||||
from config import RunningConfig
|
from config import RunningConfig
|
||||||
|
|
||||||
@ -84,6 +85,7 @@ def make_shell_context():
|
|||||||
|
|
||||||
|
|
||||||
# ctx.push()
|
# ctx.push()
|
||||||
|
# admin = User.query.filter_by(user_name="admin").first()
|
||||||
# login_user(admin)
|
# login_user(admin)
|
||||||
|
|
||||||
|
|
||||||
@ -492,6 +494,19 @@ def clear_cache(sanitize): # clear-cache
|
|||||||
formation.sanitize_old_formation()
|
formation.sanitize_old_formation()
|
||||||
|
|
||||||
|
|
||||||
|
@app.cli.command()
|
||||||
|
def init_test_database():
|
||||||
|
"""Initialise les objets en base pour les tests API
|
||||||
|
(à appliquer sur SCODOC_TEST ou SCODOC_DEV)
|
||||||
|
"""
|
||||||
|
click.echo("Initialisation base de test API...")
|
||||||
|
# import app as mapp # le package app
|
||||||
|
|
||||||
|
ctx = app.test_request_context()
|
||||||
|
ctx.push()
|
||||||
|
create_test_api_database.init_test_database()
|
||||||
|
|
||||||
|
|
||||||
def recursive_help(cmd, parent=None):
|
def recursive_help(cmd, parent=None):
|
||||||
ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent)
|
ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent)
|
||||||
print(cmd.get_help(ctx))
|
print(cmd.get_help(ctx))
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<formation id="220" titre="BUT R&amp;T" version="1" formation_code="V1RET" dept_id="5" acronyme="BUT R&amp;T" titre_officiel="Bachelor technologique réseaux et télécommunications" type_parcours="700" formation_id="220">
|
||||||
|
<ue acronyme="RT1.1" numero="1" titre="Administrer les réseaux et l’Internet" type="0" ue_code="UCOD11" ects="12.0" is_external="0" code_apogee="" coefficient="0.0" semestre_idx="1" color="#B80004" reference="1896">
|
||||||
|
<matiere titre="Administrer les réseaux et l’Internet" numero="1">
|
||||||
|
<module titre="Initiation aux réseaux informatiques" abbrev="Init aux réseaux informatiques" code="R101" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="10" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="12.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="4.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Se sensibiliser à l&apos;hygiène informatique et à la cybersécurité" abbrev="Hygiène informatique" code="SAE11" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="10" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="1896" coef="16.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Principe et architecture des réseaux" abbrev="" code="R102" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="20" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="12.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Réseaux locaux et équipements actifs" abbrev="Réseaux locaux" code="R103" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="30" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="8.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Fondamentaux des systèmes électroniques" abbrev="" code="R104" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="40" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="8.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="5.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Architecture des systèmes numériques et informatiques" abbrev="" code="R106" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="60" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="10.0"/>
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue acronyme="RT2.1" numero="2" titre="Connecter les entreprises et les usagers" type="0" ue_code="UCOD12" ects="8.0" is_external="0" code_apogee="" coefficient="0.0" semestre_idx="1" color="#F97B3D" reference="1897">
|
||||||
|
<matiere titre="Connecter les entreprises et les usagers" numero="1">
|
||||||
|
<module titre="S&apos;initier aux réseaux informatiques" abbrev="" code="SAE12" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="20" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="1896" coef="33.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Découvrir un dispositif de tranmission" abbrev="" code="SAE13" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="30" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="1897" coef="33.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Support de transmission pour les réseaux locaux" abbrev="Support de transmission" code="R105" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="50" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1897" coef="5.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Anglais général et init vocabulaire technique" abbrev="" code="R110" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="100" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="3.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="5.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="5.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Expression-culture-Communication Pro." abbrev="" code="R111" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="110" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="3.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="5.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Mathématiques du signal" abbrev="" code="R113" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="130" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="5.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="8.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Mathématiques des transmissions" abbrev="" code="R114" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="140" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="4.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="8.0"/>
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
<ue acronyme="RT3.1" numero="3" titre="Créer des outils et applications informatiques pour les R&amp;T" type="0" ue_code="UCOD13" ects="10.0" is_external="0" code_apogee="" coefficient="0.0" semestre_idx="1" color="#FEB40B" reference="1898">
|
||||||
|
<matiere titre="Créer des outils et applications informatiques pour les R&amp;T" numero="1">
|
||||||
|
<module titre="Se présenter sur Internet" abbrev="" code="SAE14" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="40" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="1898" coef="16.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Traiter des données" abbrev="" code="SAE15" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="50" code_apogee="" module_type="3">
|
||||||
|
<coefficients ue_reference="1898" coef="26.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Portofolio" abbrev="" code="SAE16" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="60" code_apogee="" module_type="3"/>
|
||||||
|
<module titre="Fondamentaux de la programmation" abbrev="" code="R107" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="70" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1898" coef="22.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Base des systèmes d&apos;exploitation" abbrev="" code="R108" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="80" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="6.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="7.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Introduction aux technologies Web" abbrev="" code="R109" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="90" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1898" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="PPP" abbrev="" code="R112" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="120" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1896" coef="2.0"/>
|
||||||
|
<coefficients ue_reference="1897" coef="3.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
<module titre="Gestion de projets" abbrev="" code="R115" heures_cours="0.0" heures_td="0.0" heures_tp="0.0" coefficient="1.0" ects="" semestre_id="1" numero="150" code_apogee="" module_type="2">
|
||||||
|
<coefficients ue_reference="1897" coef="2.0"/>
|
||||||
|
<coefficients ue_reference="1898" coef="4.0"/>
|
||||||
|
</module>
|
||||||
|
</matiere>
|
||||||
|
</ue>
|
||||||
|
</formation>
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""XXX OBSOLETE
|
||||||
|
|
||||||
Scenario: préparation base de données pour tests Selenium
|
Scenario: préparation base de données pour tests Selenium
|
||||||
|
|
||||||
S'utilise comme un test avec pytest, mais n'est pas un test !
|
S'utilise comme un test avec pytest, mais n'est pas un test !
|
||||||
|
139
tools/fakedatabase/create_test_api_database.py
Normal file
139
tools/fakedatabase/create_test_api_database.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
"""Initialise une base pour les tests de l'API ScoDoc 9
|
||||||
|
|
||||||
|
Création des départements, formations, semestres, étudiants, groupes...
|
||||||
|
|
||||||
|
utilisation:
|
||||||
|
1) modifier le .env pour indiquer
|
||||||
|
SCODOC_DATABASE_URI="postgresql:///SCO_TEST_API"
|
||||||
|
|
||||||
|
2) En tant qu'utilisateur scodoc, lancer:
|
||||||
|
tools/create_database.sh SCO_TEST_API
|
||||||
|
flask db upgrade
|
||||||
|
flask sco-db-init --erase
|
||||||
|
flask init-test-database
|
||||||
|
|
||||||
|
3) relancer ScoDoc:
|
||||||
|
flask run --host 0.0.0.0
|
||||||
|
|
||||||
|
4) lancer client de test (ou vérifier dans le navigateur)
|
||||||
|
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
import random
|
||||||
|
|
||||||
|
random.seed(12345678) # tests reproductibles
|
||||||
|
|
||||||
|
from flask_login import login_user
|
||||||
|
|
||||||
|
from app import auth
|
||||||
|
from app import models
|
||||||
|
from app import db
|
||||||
|
from app.scodoc import sco_formations
|
||||||
|
from tools.fakeportal.gen_nomprenoms import nomprenom
|
||||||
|
|
||||||
|
# La formation à utiliser:
|
||||||
|
FORMATION_XML_FILENAME = "tests/ressources/formations/scodoc_formation_RT_BUT_RT_v1.xml"
|
||||||
|
|
||||||
|
|
||||||
|
def init_departement(acronym):
|
||||||
|
"Create dept, and switch context into it."
|
||||||
|
import app as mapp
|
||||||
|
|
||||||
|
dept = models.Departement(acronym=acronym)
|
||||||
|
db.session.add(dept)
|
||||||
|
mapp.set_sco_dept(acronym)
|
||||||
|
db.session.commit()
|
||||||
|
return dept
|
||||||
|
|
||||||
|
|
||||||
|
def import_formation() -> models.Formation:
|
||||||
|
"""Import formation from XML.
|
||||||
|
Returns formation_id
|
||||||
|
"""
|
||||||
|
with open(FORMATION_XML_FILENAME) as f:
|
||||||
|
doc = f.read()
|
||||||
|
# --- Création de la formation
|
||||||
|
f = sco_formations.formation_import_xml(doc)
|
||||||
|
return models.Formation.query.get(f[0])
|
||||||
|
|
||||||
|
|
||||||
|
def create_user(dept):
|
||||||
|
"""créé les utilisaterurs nécessaires aux tests"""
|
||||||
|
user = auth.models.User(
|
||||||
|
user_name="test", nom="Doe", prenom="John", dept=dept.acronym
|
||||||
|
)
|
||||||
|
db.session.add(user)
|
||||||
|
db.session.commit()
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def create_fake_etud():
|
||||||
|
"""Créé un faux étudiant et l'insère dans la base"""
|
||||||
|
civilite = random.choice(("M", "F", "X"))
|
||||||
|
nom, prenom = nomprenom(civilite)
|
||||||
|
etud = models.Identite(civilite=civilite, nom=nom, prenom=prenom)
|
||||||
|
db.session.add(etud)
|
||||||
|
db.session.commit()
|
||||||
|
return etud
|
||||||
|
|
||||||
|
|
||||||
|
def create_etuds(nb=16):
|
||||||
|
"create nb etuds"
|
||||||
|
return [create_fake_etud() for _ in range(nb)]
|
||||||
|
|
||||||
|
|
||||||
|
def create_formsemestre(formation, user, semestre_idx=1):
|
||||||
|
"""Create formsemestre and moduleimpls"""
|
||||||
|
formsemestre = models.FormSemestre(
|
||||||
|
dept_id=formation.dept_id,
|
||||||
|
semestre_id=semestre_idx,
|
||||||
|
titre="Semestre test",
|
||||||
|
date_debut=datetime.datetime(2021, 9, 1),
|
||||||
|
date_fin=datetime.datetime(2022, 1, 31),
|
||||||
|
modalite="FI",
|
||||||
|
formation=formation,
|
||||||
|
)
|
||||||
|
db.session.add(formsemestre)
|
||||||
|
db.session.commit()
|
||||||
|
# Crée un modulimpl par module de ce semestre:
|
||||||
|
for module in formation.modules.filter_by(semestre_id=semestre_idx):
|
||||||
|
modimpl = models.ModuleImpl(
|
||||||
|
module_id=module.id, formsemestre_id=formsemestre.id, responsable_id=user.id
|
||||||
|
)
|
||||||
|
db.session.add(modimpl)
|
||||||
|
db.session.commit()
|
||||||
|
return formsemestre
|
||||||
|
|
||||||
|
|
||||||
|
def inscrit_etudiants(etuds, formsemestre):
|
||||||
|
"""Inscrit les etudiants aux semestres et à tous ses modules"""
|
||||||
|
for etud in etuds:
|
||||||
|
ins = models.FormSemestreInscription(
|
||||||
|
etudid=etud.id, formsemestre_id=formsemestre.id, etat="I"
|
||||||
|
)
|
||||||
|
db.session.add(ins)
|
||||||
|
for modimpl in formsemestre.modimpls:
|
||||||
|
insmod = models.ModuleImplInscription(
|
||||||
|
etudid=etud.id, moduleimpl_id=modimpl.id
|
||||||
|
)
|
||||||
|
db.session.add(insmod)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def init_test_database():
|
||||||
|
dept = init_departement("TAPI")
|
||||||
|
user = create_user(dept)
|
||||||
|
login_user(user)
|
||||||
|
|
||||||
|
etuds = create_etuds()
|
||||||
|
formation = import_formation()
|
||||||
|
formsemestre = create_formsemestre(formation, user)
|
||||||
|
inscrit_etudiants(etuds, formsemestre)
|
||||||
|
# à compléter
|
||||||
|
# - groupes
|
||||||
|
# - absences
|
||||||
|
# - notes
|
||||||
|
# - décisions de jury
|
||||||
|
# ...
|
Loading…
Reference in New Issue
Block a user