forked from ScoDoc/ScoDoc
Update opolka/ScoDoc from ScoDoc/ScoDoc #2
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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"""
|
||||
|
@ -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:
|
||||
|
@ -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(
|
||||
|
@ -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 = ['<div class="notes_bulletin">']
|
||||
# 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("</div>")
|
||||
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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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('<div class="bull_appreciations">')
|
||||
if I["appreciations_list"]:
|
||||
appreciations = BulAppreciations.get_appreciations_list(
|
||||
self.formsemestre.id, self.etud.id
|
||||
)
|
||||
if appreciations:
|
||||
H.append("<p><b>Appréciations</b></p>")
|
||||
for app in I["appreciations_list"]:
|
||||
for appreciation in appreciations:
|
||||
if can_edit_app:
|
||||
mlink = (
|
||||
'<a class="stdlink" href="appreciation_add_form?id=%s">modifier</a> <a class="stdlink" href="appreciation_add_form?id=%s&suppress=1">supprimer</a>'
|
||||
% (app["id"], app["id"])
|
||||
)
|
||||
mlink = f"""<a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dpt, appreciation_id=appreciation.id)
|
||||
}">modifier</a>
|
||||
<a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dpt, appreciation_id=appreciation.id, suppress=1)
|
||||
}">supprimer</a>"""
|
||||
else:
|
||||
mlink = ""
|
||||
H.append(
|
||||
'<p><span class="bull_appreciations_date">%s</span>%s<span class="bull_appreciations_link">%s</span></p>'
|
||||
% (app["date"], app["comment"], mlink)
|
||||
f"""<p>
|
||||
<span class="bull_appreciations_date">{
|
||||
appreciation.date.strftime("%d/%m/%y") if appreciation.date else ""
|
||||
}</span>
|
||||
{appreciation.comment_safe()}
|
||||
<span class="bull_appreciations_link">{mlink}</span>
|
||||
</p>"""
|
||||
)
|
||||
if can_edit_app:
|
||||
H.append(
|
||||
'<p><a class="stdlink" href="appreciation_add_form?etudid=%(etudid)s&formsemestre_id=%(formsemestre_id)s">Ajouter une appréciation</a></p>'
|
||||
% self.infos
|
||||
f"""<p>
|
||||
<a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form',
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=self.etud.id,
|
||||
formsemestre_id=self.formsemestre.id)
|
||||
}">Ajouter une appréciation</a>
|
||||
</p>"""
|
||||
)
|
||||
H.append("</div>")
|
||||
# ---------------
|
||||
|
@ -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('<div class="bull_appreciations">')
|
||||
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"""<a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dept, id=app['id'])
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dept, appreciation_id=appreciation.id)
|
||||
}">modifier</a>
|
||||
<a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dept, id=app['id'], suppress=1)
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dept, appreciation_id=appreciation.id, suppress=1)
|
||||
}">supprimer</a>"""
|
||||
else:
|
||||
mlink = ""
|
||||
H.append(
|
||||
f"""<p><span class="bull_appreciations_date">{app["date"]}</span>{
|
||||
app["comment"]}<span class="bull_appreciations_link">{mlink}</span>
|
||||
</p>"""
|
||||
f"""<p>
|
||||
<span class="bull_appreciations_date">{
|
||||
appreciation.date.strftime("%d/%m/%Y")
|
||||
if appreciation.date else ""}</span>
|
||||
{appreciation.comment_safe()}
|
||||
<span class="bull_appreciations_link">{mlink}</span>
|
||||
</p>
|
||||
"""
|
||||
)
|
||||
if can_edit_app:
|
||||
H.append(
|
||||
f"""<p><a class="stdlink" href="{
|
||||
url_for('notes.appreciation_add_form', scodoc_dept=g.scodoc_dept,
|
||||
etudid=self.infos['etudid'],
|
||||
formsemestre_id=self.infos['formsemestre_id']
|
||||
etudid=self.etud.etudid,
|
||||
formsemestre_id=self.formsemestre.id
|
||||
)
|
||||
}">Ajouter une appréciation</a></p>"""
|
||||
% self.infos
|
||||
)
|
||||
H.append("</div>")
|
||||
# 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('<div class="bul_decision">' + field + "</div>")
|
||||
|
||||
# -----
|
||||
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):
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -23,9 +23,9 @@
|
||||
app.comment}}<span
|
||||
class="bull_appreciations_link">{% if can_edit_appreciations %}<a
|
||||
class="stdlink" href="{{url_for('notes.appreciation_add_form',
|
||||
scodoc_dept=g.scodoc_dept, id=app.id)}}">modifier</a>
|
||||
scodoc_dept=g.scodoc_dept, appreciation_id=app.id)}}">modifier</a>
|
||||
<a class="stdlink" href="{{url_for('notes.appreciation_add_form',
|
||||
scodoc_dept=g.scodoc_dept, id=app.id, suppress=1)}}">supprimer</a>{% endif %}
|
||||
scodoc_dept=g.scodoc_dept, appreciation_id=app.id, suppress=1)}}">supprimer</a>{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
{% endfor %}
|
||||
|
@ -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()
|
||||
+ "<h2>%s d'une appréciation sur %s</h2>" % (a, etud["nomprenom"])
|
||||
html_sco_header.sco_header(),
|
||||
f"""<h2>{action} d'une appréciation sur {etud.nomprenom}</h2>""",
|
||||
]
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user