diff --git a/app/but/bulletin_but.py b/app/but/bulletin_but.py
index 2e556908fc..74d167eba0 100644
--- a/app/but/bulletin_but.py
+++ b/app/but/bulletin_but.py
@@ -531,10 +531,6 @@ class BulletinBUT:
] = 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
diff --git a/app/but/bulletin_but_pdf.py b/app/but/bulletin_but_pdf.py
index ce215c14fb..5e89ab7062 100644
--- a/app/but/bulletin_but_pdf.py
+++ b/app/but/bulletin_but_pdf.py
@@ -12,8 +12,8 @@ La génération du bulletin PDF suit le chemin suivant:
bul_dict = bulletin_but.BulletinBUT(formsemestre).bulletin_etud_complet(etud)
-- sco_bulletins_generator.make_formsemestre_bulletinetud(infos)
-- instance de BulletinGeneratorStandardBUT(infos)
+- sco_bulletins_generator.make_formsemestre_bulletin_etud()
+- instance de BulletinGeneratorStandardBUT
- BulletinGeneratorStandardBUT.generate(format="pdf")
sco_bulletins_generator.BulletinGenerator.generate()
.generate_pdf()
@@ -42,7 +42,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
multi_pages = True # plusieurs pages par bulletins
small_fontsize = "8"
- def bul_table(self, format="html"):
+ def bul_table(self, fmt="html"):
"""Génère la table centrale du bulletin de notes
Renvoie:
- en HTML: une chaine
@@ -71,7 +71,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
html_class_ignore_default=True,
html_with_td_classes=True,
)
- table_objects = table.gen(format=format)
+ table_objects = table.gen(format=fmt)
objects += table_objects
# objects += [KeepInFrame(0, 0, table_objects, mode="shrink")]
if i != 2:
diff --git a/app/but/bulletin_but_xml_compat.py b/app/but/bulletin_but_xml_compat.py
index 45668ac59f..75dade0a18 100644
--- a/app/but/bulletin_but_xml_compat.py
+++ b/app/but/bulletin_but_xml_compat.py
@@ -40,7 +40,7 @@ from xml.etree.ElementTree import Element
from app import log
from app.but import bulletin_but
-from app.models import FormSemestre, Identite
+from app.models import BulAppreciations, FormSemestre, Identite
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc import codes_cursus
@@ -315,16 +315,13 @@ def bulletin_but_xml_compat(
else:
doc.append(Element("decision", code="", etat="DEM"))
# --- Appreciations
- cnx = ndb.GetDBConnexion()
- apprecs = sco_etud.appreciations_list(
- cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
- )
- for appr in apprecs:
+ appreciations = BulAppreciations.get_appreciations_list(formsemestre.id, etudid)
+ for appreciation in appreciations:
x_appr = Element(
"appreciation",
- date=ndb.DateDMYtoISO(appr["date"]),
+ date=appreciation.date.isoformat() if appreciation.date else "",
)
- x_appr.text = quote_xml_attr(appr["comment"])
+ x_appr.text = quote_xml_attr(appreciation.comment_safe())
doc.append(x_appr)
if is_appending:
diff --git a/app/models/etudiants.py b/app/models/etudiants.py
index 5c4ea31e79..e00f846507 100644
--- a/app/models/etudiants.py
+++ b/app/models/etudiants.py
@@ -296,26 +296,27 @@ class Identite(db.Model):
from app.scodoc import sco_photos
d = {
+ "boursier": self.boursier or "",
+ "civilite_etat_civil": self.civilite_etat_civil,
"civilite": self.civilite,
"code_ine": self.code_ine or "",
"code_nip": self.code_nip or "",
"date_naissance": self.date_naissance.strftime("%d/%m/%Y")
if self.date_naissance
else "",
- "dept_id": self.dept_id,
"dept_acronym": self.departement.acronym,
+ "dept_id": self.dept_id,
+ "dept_naissance": self.dept_naissance or "",
"email": self.get_first_email() or "",
"emailperso": self.get_first_email("emailperso"),
+ "etat_civil": self.etat_civil,
"etudid": self.id,
- "nom": self.nom_disp(),
- "prenom": self.prenom or "",
- "nomprenom": self.nomprenom or "",
"lieu_naissance": self.lieu_naissance or "",
- "dept_naissance": self.dept_naissance or "",
"nationalite": self.nationalite or "",
- "boursier": self.boursier or "",
- "civilite_etat_civil": self.civilite_etat_civil,
+ "nom": self.nom_disp(),
+ "nomprenom": self.nomprenom or "",
"prenom_etat_civil": self.prenom_etat_civil,
+ "prenom": self.prenom or "",
}
if include_urls and has_request_context():
# test request context so we can use this func in tests under the flask shell
diff --git a/app/models/notes.py b/app/models/notes.py
index 2024a43683..61eb177338 100644
--- a/app/models/notes.py
+++ b/app/models/notes.py
@@ -5,6 +5,7 @@
import sqlalchemy as sa
from app import db
+from app.scodoc import safehtml
import app.scodoc.sco_utils as scu
@@ -26,6 +27,31 @@ class BulAppreciations(db.Model):
author = db.Column(db.Text) # le pseudo (user_name), sans contrainte
comment = db.Column(db.Text) # texte libre
+ @classmethod
+ def get_appreciations_list(
+ cls, formsemestre_id: int, etudid: int
+ ) -> list["BulAppreciations"]:
+ "Liste des appréciations pour cet étudiant dans ce semestre"
+ return (
+ BulAppreciations.query.filter_by(
+ etudid=etudid, formsemestre_id=formsemestre_id
+ )
+ .order_by(BulAppreciations.date)
+ .all()
+ )
+
+ @classmethod
+ def summarize(cls, appreciations: list["BulAppreciations"]) -> list[str]:
+ "Liste de chaines résumant une liste d'appréciations, pour bulletins"
+ return [
+ f"{x.date.strftime('%d/%m/%Y') if x.date else ''}: {x.comment or ''}"
+ for x in appreciations
+ ]
+
+ def comment_safe(self) -> str:
+ "Le comment, safe pour inclusion dans HTML (None devient '')"
+ return safehtml.html_to_safe_html(self.comment or "")
+
class NotesNotes(db.Model):
"""Une note"""
diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py
index ce5f65090b..ba27c9b96c 100644
--- a/app/scodoc/sco_bulletins.py
+++ b/app/scodoc/sco_bulletins.py
@@ -224,9 +224,6 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
elif I["etud_etat"] == codes_cursus.DEF:
I["demission"] = "(Défaillant)"
- # --- Appreciations
- I.update(get_appreciations_list(formsemestre_id, etudid))
-
# --- Notes
ues = nt.get_ues_stat_dict()
modimpls = nt.get_modimpls_dict()
@@ -417,21 +414,6 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
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:
"""chaine html représentant l'état (backward compat sco7)"""
if etat == scu.INSCRIT:
@@ -1035,16 +1017,18 @@ def do_formsemestre_bulletinetud(
bul_dict = formsemestre_bulletinetud_dict(formsemestre.id, etud.id)
if format == "html":
- htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
- bul_dict, version=version, format="html"
+ htm, _ = sco_bulletins_generator.make_formsemestre_bulletin_etud(
+ bul_dict, etud=etud, formsemestre=formsemestre, version=version, fmt="html"
)
return htm, bul_dict["filigranne"]
elif format == "pdf" or format == "pdfpart":
- bul, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
+ bul, filename = sco_bulletins_generator.make_formsemestre_bulletin_etud(
bul_dict,
+ etud=etud,
+ formsemestre=formsemestre,
version=version,
- format="pdf",
+ fmt="pdf",
stand_alone=(format != "pdfpart"),
with_img_signatures_pdf=with_img_signatures_pdf,
)
@@ -1062,8 +1046,8 @@ def do_formsemestre_bulletinetud(
if not can_send_bulletin_by_mail(formsemestre.id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
- pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
- bul_dict, version=version, format="pdf"
+ pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletin_etud(
+ bul_dict, etud=etud, formsemestre=formsemestre, version=version, fmt="pdf"
)
if prefer_mail_perso:
diff --git a/app/scodoc/sco_bulletins_example.py b/app/scodoc/sco_bulletins_example.py
index 83ad85c3da..663fc8d0de 100644
--- a/app/scodoc/sco_bulletins_example.py
+++ b/app/scodoc/sco_bulletins_example.py
@@ -47,14 +47,14 @@ class BulletinGeneratorExample(sco_bulletins_standard.BulletinGeneratorStandard)
# En général, on veut définir un format de table spécial, sans changer le reste (titre, pied de page).
# Si on veut changer le reste, surcharger les méthodes:
# .bul_title_pdf(self) : partie haute du bulletin
- # .bul_part_below(self, format='') : infos sous la table
+ # .bul_part_below(self, fmt='') : infos sous la table
# .bul_signatures_pdf(self) : signatures
- def bul_table(self, format=""):
+ def bul_table(self, fmt=""):
"""Défini la partie centrale de notre bulletin PDF.
Doit renvoyer une liste d'objets PLATYPUS
"""
- assert format == "pdf" # garde fou
+ assert fmt == "pdf" # garde fou
return [
Paragraph(
sco_pdf.SU(
diff --git a/app/scodoc/sco_bulletins_generator.py b/app/scodoc/sco_bulletins_generator.py
index b122d7a46b..499833e198 100644
--- a/app/scodoc/sco_bulletins_generator.py
+++ b/app/scodoc/sco_bulletins_generator.py
@@ -31,8 +31,8 @@ class BulletinGenerator:
description
supported_formats = [ 'pdf', 'html' ]
.bul_title_pdf()
- .bul_table(format)
- .bul_part_below(format)
+ .bul_table(fmt)
+ .bul_part_below(fmt)
.bul_signatures_pdf()
.__init__ et .generate(format) methodes appelees par le client (sco_bulletin)
@@ -62,6 +62,7 @@ from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
from flask import request
from flask_login import current_user
+from app.models import FormSemestre, Identite
from app.scodoc import sco_utils as scu
from app.scodoc.sco_exceptions import NoteProcessError
from app import log
@@ -85,9 +86,11 @@ class BulletinGenerator:
self,
bul_dict,
authuser=None,
- version="long",
+ etud: Identite = None,
filigranne=None,
+ formsemestre: FormSemestre = None,
server_name=None,
+ version="long",
with_img_signatures_pdf: bool = True,
):
from app.scodoc import sco_preferences
@@ -98,9 +101,11 @@ class BulletinGenerator:
self.infos = bul_dict # legacy code compat
# authuser nécessaire pour version HTML qui contient liens dépendants de l'utilisateur
self.authuser = authuser
- self.version = version
+ self.etud: Identite = etud
self.filigranne = filigranne
+ self.formsemestre: FormSemestre = formsemestre
self.server_name = server_name
+ self.version = version
self.with_img_signatures_pdf = with_img_signatures_pdf
# Store preferences for convenience:
formsemestre_id = self.bul_dict["formsemestre_id"]
@@ -151,9 +156,9 @@ class BulletinGenerator:
"""Return bulletin as an HTML string"""
H = ['
']
# table des notes:
- H.append(self.bul_table(format="html")) # pylint: disable=no-member
+ H.append(self.bul_table(fmt="html")) # pylint: disable=no-member
# infos sous la table:
- H.append(self.bul_part_below(format="html")) # pylint: disable=no-member
+ H.append(self.bul_part_below(fmt="html")) # pylint: disable=no-member
H.append("
")
return "\n".join(H)
@@ -169,7 +174,7 @@ class BulletinGenerator:
nomprenom = self.bul_dict["etud"]["nomprenom"]
etat_civil = self.bul_dict["etud"]["etat_civil"]
marque_debut_bulletin = sco_pdf.DebutBulletin(
- self.bul_dict["etat_civil"],
+ etat_civil,
filigranne=self.bul_dict["filigranne"],
footer_content=f"""ScoDoc - Bulletin de {nomprenom} - {time.strftime("%d/%m/%Y %H:%M")}""",
)
@@ -179,9 +184,9 @@ class BulletinGenerator:
index_obj_debut = len(story)
# table des notes
- story += self.bul_table(format="pdf") # pylint: disable=no-member
+ story += self.bul_table(fmt="pdf") # pylint: disable=no-member
# infos sous la table
- story += self.bul_part_below(format="pdf") # pylint: disable=no-member
+ story += self.bul_part_below(fmt="pdf") # pylint: disable=no-member
# signatures
story += self.bul_signatures_pdf() # pylint: disable=no-member
if self.scale_table_in_page:
@@ -249,10 +254,12 @@ class BulletinGenerator:
# ---------------------------------------------------------------------------
-def make_formsemestre_bulletinetud(
+def make_formsemestre_bulletin_etud(
bul_dict,
+ etud: Identite = None,
+ formsemestre: FormSemestre = None,
version=None, # short, long, selectedevals
- format="pdf", # html, pdf
+ fmt="pdf", # html, pdf
stand_alone=True,
with_img_signatures_pdf: bool = True,
):
@@ -277,7 +284,7 @@ def make_formsemestre_bulletinetud(
# si pas trouvé (modifs locales bizarres ,), ré-essaye avec la valeur par défaut
bulletin_default_class_name(),
):
- if bul_dict.get("type") == "BUT" and format.startswith("pdf"):
+ if bul_dict.get("type") == "BUT" and fmt.startswith("pdf"):
gen_class = bulletin_get_class(bul_class_name + "BUT")
if gen_class is None:
gen_class = bulletin_get_class(bul_class_name)
@@ -290,28 +297,32 @@ def make_formsemestre_bulletinetud(
bul_generator = gen_class(
bul_dict,
authuser=current_user,
- version=version,
+ etud=etud,
filigranne=bul_dict["filigranne"],
+ formsemestre=formsemestre,
server_name=request.url_root,
+ version=version,
with_img_signatures_pdf=with_img_signatures_pdf,
)
- if format not in bul_generator.supported_formats:
+ if fmt not in bul_generator.supported_formats:
# use standard generator
log(
"Bulletin format %s not supported by %s, using %s"
- % (format, bul_class_name, bulletin_default_class_name())
+ % (fmt, bul_class_name, bulletin_default_class_name())
)
bul_class_name = bulletin_default_class_name()
gen_class = bulletin_get_class(bul_class_name)
bul_generator = gen_class(
bul_dict,
authuser=current_user,
- version=version,
+ etud=etud,
filigranne=bul_dict["filigranne"],
+ formsemestre=formsemestre,
server_name=request.url_root,
+ version=version,
with_img_signatures_pdf=with_img_signatures_pdf,
)
- data = bul_generator.generate(format=format, stand_alone=stand_alone)
+ data = bul_generator.generate(format=fmt, stand_alone=stand_alone)
finally:
PDFLOCK.release()
diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py
index fc4d4b427f..a4ee2ed3ec 100644
--- a/app/scodoc/sco_bulletins_json.py
+++ b/app/scodoc/sco_bulletins_json.py
@@ -37,7 +37,7 @@ from app import db, ScoDocJSONEncoder
from app.comp import res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import but_validations
-from app.models import Evaluation, Matiere, UniteEns
+from app.models import BulAppreciations, Evaluation, Matiere, UniteEns
from app.models.etudiants import Identite
from app.models.formsemestre import FormSemestre
@@ -303,18 +303,14 @@ def formsemestre_bulletinetud_published_dict(
d.update(dict_decision_jury(etud, formsemestre, with_decisions=xml_with_decisions))
# --- Appréciations
- cnx = ndb.GetDBConnexion()
- apprecs = sco_etud.appreciations_list(
- cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
- )
- d["appreciation"] = []
- for app in apprecs:
- d["appreciation"].append(
- dict(
- comment=quote_xml_attr(app["comment"]),
- date=ndb.DateDMYtoISO(app["date"]),
- )
- )
+ appreciations = BulAppreciations.get_appreciations_list(formsemestre.id, etudid)
+ d["appreciation"] = [
+ {
+ "comment": quote_xml_attr(appreciation["comment"]),
+ "date": appreciation.date.isoformat() if appreciation.date else "",
+ }
+ for appreciation in appreciations
+ ]
#
return d
diff --git a/app/scodoc/sco_bulletins_legacy.py b/app/scodoc/sco_bulletins_legacy.py
index 280c6f7b49..419a243776 100644
--- a/app/scodoc/sco_bulletins_legacy.py
+++ b/app/scodoc/sco_bulletins_legacy.py
@@ -34,10 +34,14 @@
CE FORMAT N'EVOLUERA PLUS ET EST CONSIDERE COMME OBSOLETE.
"""
+
+from flask import g, url_for
+
from reportlab.lib.colors import Color, blue
from reportlab.lib.units import cm, mm
from reportlab.platypus import Paragraph, Spacer, Table
+from app.models import BulAppreciations
from app.scodoc import sco_bulletins_generator
from app.scodoc import sco_bulletins_pdf
from app.scodoc import sco_formsemestre
@@ -65,14 +69,14 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
) # impose un espace vertical entre le titre et la table qui suit
return objects
- def bul_table(self, format="html"):
+ def bul_table(self, fmt="html"):
"""Table bulletin"""
- if format == "pdf":
+ if fmt == "pdf":
return self.bul_table_pdf()
- elif format == "html":
+ elif fmt == "html":
return self.bul_table_html()
else:
- raise ValueError("invalid bulletin format (%s)" % format)
+ raise ValueError(f"invalid bulletin format ({fmt})")
def bul_table_pdf(self):
"""Génère la table centrale du bulletin de notes
@@ -239,16 +243,16 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
# ---------------
return "\n".join(H)
- def bul_part_below(self, format="html"):
+ def bul_part_below(self, fmt="html"):
"""Génère les informations placées sous la table de notes
(absences, appréciations, décisions de jury...)
"""
- if format == "pdf":
+ if fmt == "pdf":
return self.bul_part_below_pdf()
- elif format == "html":
+ elif fmt == "html":
return self.bul_part_below_html()
else:
- raise ValueError("invalid bulletin format (%s)" % format)
+ raise ValueError("invalid bulletin format (%s)" % fmt)
def bul_part_below_pdf(self):
"""
@@ -277,11 +281,17 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
)
# ----- APPRECIATIONS
- if self.infos.get("appreciations_list", False):
+ appreciations = BulAppreciations.get_appreciations_list(
+ self.formsemestre.id, self.etud.id
+ )
+ if appreciations:
objects.append(Spacer(1, 3 * mm))
objects.append(
Paragraph(
- SU("Appréciation : " + "\n".join(self.infos["appreciations_txt"])),
+ SU(
+ "Appréciation : "
+ + "\n".join(BulAppreciations.summarize(appreciations))
+ ),
self.CellStyle,
)
)
@@ -325,24 +335,40 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
authuser.has_permission(Permission.ScoEtudInscrit)
)
H.append('')
- if I["appreciations_list"]:
+ appreciations = BulAppreciations.get_appreciations_list(
+ self.formsemestre.id, self.etud.id
+ )
+ if appreciations:
H.append("
Appréciations
")
- for app in I["appreciations_list"]:
+ for appreciation in appreciations:
if can_edit_app:
- mlink = (
- '
modifier supprimer'
- % (app["id"], app["id"])
- )
+ mlink = f"""
modifier
+
supprimer"""
else:
mlink = ""
H.append(
- '
%s%s%s
'
- % (app["date"], app["comment"], mlink)
+ f"""
+ {
+ appreciation.date.strftime("%d/%m/%y") if appreciation.date else ""
+ }
+ {appreciation.comment_safe()}
+ {mlink}
+
"""
)
if can_edit_app:
H.append(
- '
Ajouter une appréciation
'
- % self.infos
+ f"""
+ Ajouter une appréciation
+
"""
)
H.append("
")
# ---------------
diff --git a/app/scodoc/sco_bulletins_standard.py b/app/scodoc/sco_bulletins_standard.py
index 0039dba5ef..dd3e8d7d67 100644
--- a/app/scodoc/sco_bulletins_standard.py
+++ b/app/scodoc/sco_bulletins_standard.py
@@ -51,6 +51,7 @@ from reportlab.lib.colors import Color, blue
from reportlab.lib.units import cm, mm
from reportlab.platypus import KeepTogether, Paragraph, Spacer, Table
+from app.models import BulAppreciations
import app.scodoc.sco_utils as scu
from app.scodoc import (
gen_tables,
@@ -92,7 +93,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
) # impose un espace vertical entre le titre et la table qui suit
return objects
- def bul_table(self, format="html"):
+ def bul_table(self, fmt="html"):
"""Génère la table centrale du bulletin de notes
Renvoie:
- en HTML: une chaine
@@ -112,9 +113,9 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
html_with_td_classes=True,
)
- return T.gen(format=format)
+ return T.gen(format=fmt)
- def bul_part_below(self, format="html"):
+ def bul_part_below(self, fmt="html"):
"""Génère les informations placées sous la table de notes
(absences, appréciations, décisions de jury...)
Renvoie:
@@ -156,45 +157,53 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
# ---- APPRECIATIONS
# le dir. des etud peut ajouter des appreciations,
# mais aussi le chef (perm. ScoEtudInscrit)
- can_edit_app = (self.authuser.id in self.infos["responsables"]) or (
+ can_edit_app = (self.formsemestre.est_responsable(self.authuser)) or (
self.authuser.has_permission(Permission.ScoEtudInscrit)
)
H.append('')
- for app in self.infos["appreciations_list"]:
+ appreciations = BulAppreciations.get_appreciations_list(
+ self.formsemestre.id, self.etud.id
+ )
+ for appreciation in appreciations:
if can_edit_app:
mlink = f"""
modifier
supprimer"""
else:
mlink = ""
H.append(
- f"""
{app["date"]}{
- app["comment"]}{mlink}
-
"""
+ f"""
+ {
+ appreciation.date.strftime("%d/%m/%Y")
+ if appreciation.date else ""}
+ {appreciation.comment_safe()}
+ {mlink}
+
+ """
)
if can_edit_app:
H.append(
f"""
Ajouter une appréciation
"""
% self.infos
)
H.append("
")
# Appréciations sur PDF:
- if self.infos.get("appreciations_list", False):
+ if appreciations:
story.append(Spacer(1, 3 * mm))
try:
story.append(
Paragraph(
SU(
"Appréciation : "
- + "\n".join(self.infos["appreciations_txt"])
+ + "\n".join(BulAppreciations.summarize(appreciations))
),
self.CellStyle,
)
@@ -221,14 +230,14 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
H.append('' + field + "
")
# -----
- if format == "pdf":
+ if fmt == "pdf":
if self.scale_table_in_page:
# le scaling (pour tenir sur une page) semble incompatible avec
# le KeepTogether()
return story
else:
return [KeepTogether(story)]
- elif format == "html":
+ elif fmt == "html":
return "\n".join(H)
def bul_signatures_pdf(self):
diff --git a/app/scodoc/sco_bulletins_xml.py b/app/scodoc/sco_bulletins_xml.py
index 4d96009b93..3eafc5cabf 100644
--- a/app/scodoc/sco_bulletins_xml.py
+++ b/app/scodoc/sco_bulletins_xml.py
@@ -50,8 +50,7 @@ import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app import log
from app.but.bulletin_but_xml_compat import bulletin_but_xml_compat
-from app.models.evaluations import Evaluation
-from app.models.formsemestre import FormSemestre
+from app.models import BulAppreciations, Evaluation, FormSemestre
from app.scodoc import sco_assiduites
from app.scodoc import codes_cursus
from app.scodoc import sco_edit_ue
@@ -415,16 +414,13 @@ def make_xml_formsemestre_bulletinetud(
else:
doc.append(Element("decision", code="", etat="DEM"))
# --- Appreciations
- cnx = ndb.GetDBConnexion()
- apprecs = sco_etud.appreciations_list(
- cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
- )
- for appr in apprecs:
+ appreciations = BulAppreciations.get_appreciations_list(formsemestre.id, etudid)
+ for appreciation in appreciations:
x_appr = Element(
"appreciation",
- date=ndb.DateDMYtoISO(appr["date"]),
+ date=appreciation.date.isoformat() if appreciation.date else "",
)
- x_appr.text = quote_xml_attr(appr["comment"])
+ x_appr.text = quote_xml_attr(appreciation.comment_safe())
doc.append(x_appr)
if is_appending:
diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py
index 2fb1f52da7..9365ef7c5a 100644
--- a/app/scodoc/sco_etud.py
+++ b/app/scodoc/sco_etud.py
@@ -741,31 +741,6 @@ def add_annotations_to_etud_list(etuds):
etud["annotations_str"] = ", ".join(l)
-# -------- APPRECIATIONS (sur bulletins) -------------------
-# Les appreciations sont dans la table postgres notes_appreciations
-_appreciationsEditor = ndb.EditableTable(
- "notes_appreciations",
- "id",
- (
- "id",
- "date",
- "etudid",
- "formsemestre_id",
- "author",
- "comment",
- "author",
- ),
- sortkey="date desc",
- convert_null_outputs_to_empty=True,
- output_formators={"comment": safehtml.html_to_safe_html, "date": ndb.DateISOtoDMY},
-)
-
-appreciations_create = _appreciationsEditor.create
-appreciations_delete = _appreciationsEditor.delete
-appreciations_list = _appreciationsEditor.list
-appreciations_edit = _appreciationsEditor.edit
-
-
# -------- Noms des Lycées à partir du code
def read_etablissements():
filename = os.path.join(scu.SCO_TOOLS_DIR, scu.CONFIG.ETABL_FILENAME)
diff --git a/app/templates/bul_foot.j2 b/app/templates/bul_foot.j2
index e2085cb752..e06718f133 100644
--- a/app/templates/bul_foot.j2
+++ b/app/templates/bul_foot.j2
@@ -23,9 +23,9 @@
app.comment}}{% if can_edit_appreciations %}modifier
+ scodoc_dept=g.scodoc_dept, appreciation_id=app.id)}}">modifier
supprimer{% endif %}
+ scodoc_dept=g.scodoc_dept, appreciation_id=app.id, suppress=1)}}">supprimer{% endif %}
{% endfor %}
diff --git a/app/views/notes.py b/app/views/notes.py
index 3b3ee2a6ee..11c25dbe58 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -58,6 +58,7 @@ from app.but.forms import jury_but_forms
from app.comp import jury, res_sem
from app.comp.res_compat import NotesTableCompat
from app.models import (
+ BulAppreciations,
Evaluation,
Formation,
ScolarAutorisationInscription,
@@ -316,14 +317,14 @@ def formsemestre_bulletinetud(
if formsemestre.formation.is_apc() and format == "html":
return render_template(
"but/bulletin.j2",
- appreciations=models.BulAppreciations.query.filter_by(
- etudid=etudid, formsemestre_id=formsemestre.id
- ).order_by(models.BulAppreciations.date),
+ appreciations=BulAppreciations.get_appreciations_list(
+ formsemestre.id, etud.id
+ ),
bul_url=url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
- etudid=etudid,
+ etudid=etud.id,
format="json",
force_publishing=1, # pour ScoDoc lui même
version=version,
@@ -2039,64 +2040,72 @@ sco_publish(
def appreciation_add_form(
etudid=None,
formsemestre_id=None,
- id=None, # si id, edit
+ appreciation_id=None, # si id, edit
suppress=False, # si true, supress id
):
"form ajout ou edition d'une appreciation"
- cnx = ndb.GetDBConnexion()
- if id: # edit mode
- apps = sco_etud.appreciations_list(cnx, args={"id": id})
- if not apps:
+ if appreciation_id: # edit mode
+ appreciation = db.session.get(BulAppreciations, appreciation_id)
+ if appreciation is None:
raise ScoValueError("id d'appreciation invalide !")
- app = apps[0]
- formsemestre_id = app["formsemestre_id"]
- etudid = app["etudid"]
+ formsemestre_id = appreciation.formsemestre_id
+ etudid = appreciation.etudid
+ etud: Identite = Identite.query.filter_by(
+ id=etudid, dept_id=g.scodoc_dept_id
+ ).first_or_404()
vals = scu.get_request_args()
if "edit" in vals:
edit = int(vals["edit"])
- elif id:
+ elif appreciation_id:
edit = 1
else:
edit = 0
- sem = sco_formsemestre.get_formsemestre(formsemestre_id)
+ formsemestre: FormSemestre = FormSemestre.query.get_or_404(formsemestre_id)
# check custom access permission
- can_edit_app = (current_user.id in sem["responsables"]) or (
+ can_edit_app = formsemestre.est_responsable(current_user) or (
current_user.has_permission(Permission.ScoEtudInscrit)
)
if not can_edit_app:
raise AccessDenied("vous n'avez pas le droit d'ajouter une appreciation")
#
- bull_url = "formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s" % (
- formsemestre_id,
- etudid,
+ bul_url = url_for(
+ "notes.formsemestre_bulletinetud",
+ scodoc_dept=g.scodoc_dept,
+ formsemestre_id=formsemestre_id,
+ etudid=etudid,
)
+
if suppress:
- sco_etud.appreciations_delete(cnx, id)
- logdb(cnx, method="appreciation_suppress", etudid=etudid, msg="")
- return flask.redirect(bull_url)
+ db.session.delete(appreciation)
+ Scolog.logdb(
+ method="appreciation_suppress",
+ etudid=etudid,
+ )
+ db.session.commit()
+ flash("appréciation supprimée")
+ return flask.redirect(bul_url)
#
- etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
- if id:
- a = "Edition"
+ if appreciation_id:
+ action = "Édition"
else:
- a = "Ajout"
+ action = "Ajout"
H = [
- html_sco_header.sco_header()
- + "%s d'une appréciation sur %s
" % (a, etud["nomprenom"])
+ html_sco_header.sco_header(),
+ f"""{action} d'une appréciation sur {etud.nomprenom}
""",
]
F = html_sco_header.sco_footer()
descr = [
("edit", {"input_type": "hidden", "default": edit}),
("etudid", {"input_type": "hidden"}),
("formsemestre_id", {"input_type": "hidden"}),
- ("id", {"input_type": "hidden"}),
+ ("appreciation_id", {"input_type": "hidden"}),
("comment", {"title": "", "input_type": "textarea", "rows": 4, "cols": 60}),
]
- if id:
+ if appreciation_id:
initvalues = {
"etudid": etudid,
"formsemestre_id": formsemestre_id,
- "comment": app["comment"],
+ "comment": appreciation.comment,
}
else:
initvalues = {}
@@ -2111,31 +2120,33 @@ def appreciation_add_form(
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] + F
elif tf[0] == -1:
- return flask.redirect(bull_url)
+ return flask.redirect(bul_url)
else:
- args = {
- "etudid": etudid,
- "formsemestre_id": formsemestre_id,
- "author": current_user.user_name,
- "comment": tf[2]["comment"],
- }
if edit:
- args["id"] = id
- sco_etud.appreciations_edit(cnx, args)
+ appreciation.author = (current_user.user_name,)
+ appreciation.comment = tf[2]["comment"].strip()
+ flash("appréciation modifiée")
else: # nouvelle
- sco_etud.appreciations_create(cnx, args)
+ appreciation = BulAppreciations(
+ etudid=etudid,
+ formsemestre_id=formsemestre_id,
+ author=current_user.user_name,
+ comment=tf[2]["comment"].strip(),
+ )
+ flash("appréciation ajoutée")
+ db.session.add(appreciation)
# log
- logdb(
- cnx,
+ Scolog.logdb(
method="appreciation_add",
etudid=etudid,
- msg=tf[2]["comment"],
+ msg=appreciation.comment_safe(),
)
+ db.session.commit()
# ennuyeux mais necessaire (pour le PDF seulement)
sco_cache.invalidate_formsemestre(
pdfonly=True, formsemestre_id=formsemestre_id
) # > appreciation_add
- return flask.redirect(bull_url)
+ return flask.redirect(bul_url)
# --- FORMULAIRE POUR VALIDATION DES UE ET SEMESTRES