Compare commits

...

3 Commits

11 changed files with 107 additions and 70 deletions

View File

@ -20,10 +20,10 @@ Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
### É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.
- 9.2 (branche refactor_nt) est la version de développement.
- 9.2 (branche dev92) est la version de développement.
### Lignes de commandes

View File

@ -9,14 +9,15 @@
import datetime
from flask import url_for, g
from app.models.formsemestre import FormSemestre
from app.comp.res_but import ResultatsSemestreBUT
from app.models import FormSemestre, Identite
from app.scodoc import 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
from app.scodoc.sco_utils import fmt_note
from app.comp.res_but import ResultatsSemestreBUT
class BulletinBUT:
@ -28,6 +29,7 @@ class BulletinBUT:
def __init__(self, formsemestre: FormSemestre):
""" """
self.res = ResultatsSemestreBUT(formsemestre)
self.prefs = sco_preferences.SemPreferences(formsemestre.id)
def etud_ue_mod_results(self, etud, ue, modimpls) -> dict:
"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),
}
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]
rang = rangs[etud.id]
else:
@ -155,9 +157,7 @@ class BulletinBUT:
if e.visibulletin
and (
modimpl_results.evaluations_etat[e.id].is_complete
or sco_preferences.get_preference(
"bul_show_all_evals", res.formsemestre.id
)
or self.prefs["bul_show_all_evals"]
)
],
}
@ -216,9 +216,11 @@ class BulletinBUT:
else:
return f"Bonus de {fmt_note(bonus_vect.iloc[0])}"
def bulletin_etud(self, etud, formsemestre, force_publishing=False) -> dict:
"""Le bulletin de l'étudiant dans ce semestre.
Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
def bulletin_etud(
self, etud: Identite, formsemestre, force_publishing=False
) -> dict:
"""Le bulletin de l'étudiant dans ce semestre: dict pour la version JSON / HTML.
- Si force_publishing, rempli le bulletin même si bul_hide_xml est vrai
(bulletins non publiés).
"""
res = self.res
@ -239,7 +241,9 @@ class BulletinBUT:
},
"formsemestre_id": formsemestre.id,
"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:
return d
@ -312,3 +316,12 @@ class BulletinBUT:
)
return d
def bulletin_etud_complet(self, etud) -> dict:
"""Bulletin dict complet avec toutes les infos pour les bulletins pdf"""
d = self.bulletin_etud(force_publishing=True)
d["filigranne"] = sco_bulletins_pdf.get_filigranne(
self.res.get_etud_etat(etud.id), self.prefs
)
# XXX TODO A COMPLETER
raise NotImplementedError()

View File

@ -117,6 +117,7 @@ class FormSemestre(db.Model):
return f"<{self.__class__.__name__} {self.id} {self.titre_num()}>"
def to_dict(self):
"dict (compatible ScoDoc7)"
d = dict(self.__dict__)
d.pop("_sa_instance_state", None)
# ScoDoc7 output_formators: (backward compat)

View File

@ -28,30 +28,21 @@
"""Génération des bulletins de notes
"""
from app.models import formsemestre
import time
import pprint
import email
from email.mime.multipart import MIMEMultipart
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
import pprint
import time
from flask import g, request
from flask import url_for
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.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb
from app import email
from app import log
from app.but import bulletin_but
from app.comp import res_sem
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_exceptions import AccessDenied, ScoValueError
from app.scodoc import html_sco_header
@ -60,9 +51,9 @@ from app.scodoc import sco_abs
from app.scodoc import sco_abs_views
from app.scodoc import sco_bulletins_generator
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_codes_parcours
from app.scodoc import sco_cache
from app.scodoc import sco_etud
from app.scodoc import sco_evaluation_db
from app.scodoc import sco_formations
@ -73,7 +64,9 @@ from app.scodoc import sco_photos
from app.scodoc import sco_preferences
from app.scodoc import sco_pvjury
from app.scodoc import sco_users
from app import email
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import ModuleType
import app.scodoc.notesdb as ndb
# ----- CLASSES DE BULLETINS DE NOTES
from app.scodoc import sco_bulletins_standard
@ -190,28 +183,18 @@ def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
show_mention=prefs["bul_show_mention"],
)
if dpv:
I["decision_sem"] = dpv["decisions"][0]["decision_sem"]
else:
I["decision_sem"] = ""
I.update(infos)
I["etud_etat_html"] = _get_etud_etat_html(
formsemestre.etuds_inscriptions[etudid].etat
)
I["etud_etat"] = nt.get_etud_etat(etudid)
I["filigranne"] = ""
I["filigranne"] = sco_bulletins_pdf.get_filigranne(I["etud_etat"], prefs)
I["demission"] = ""
if I["etud_etat"] == "D":
if I["etud_etat"] == scu.DEMISSION:
I["demission"] = "(Démission)"
I["filigranne"] = "Démission"
elif I["etud_etat"] == sco_codes_parcours.DEF:
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
cnx = ndb.GetDBConnexion()
@ -687,6 +670,7 @@ def etud_descr_situation_semestre(
descr_defaillance : "Défaillant" ou vide si non défaillant.
decision_jury : "Validé", "Ajourné", ... (code semestre)
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.
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
@ -696,7 +680,7 @@ def etud_descr_situation_semestre(
# --- Situation et décisions jury
# demission/inscription ?
# démission/inscription ?
events = sco_etud.scolar_events_list(
cnx, args={"etudid": etudid, "formsemestre_id": formsemestre_id}
)
@ -763,11 +747,15 @@ def etud_descr_situation_semestre(
infos["situation"] += " " + infos["descr_defaillance"]
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:
return infos, dpv
# Decisions de jury:
# Décisions de jury:
pv = dpv["decisions"][0]
dec = ""
if pv["decision_sem_descr"]:
@ -819,11 +807,15 @@ def formsemestre_bulletinetud(
except:
sco_etud.log_unknown_etud()
raise ScoValueError("étudiant inconnu")
# API, donc erreurs admises en ScoValueError
sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
formsemestre: FormSemestre = FormSemestre.query.get(formsemestre_id)
if not formsemestre:
# API, donc erreurs admises
raise ScoValueError(f"semestre {formsemestre_id} inconnu !")
sem = formsemestre.to_dict()
bulletin = do_formsemestre_bulletinetud(
formsemestre_id,
formsemestre,
etudid,
format=format,
version=version,
@ -835,7 +827,6 @@ def formsemestre_bulletinetud(
filename = scu.bul_filename(sem, etud, format)
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [
_formsemestre_bulletinetud_header_html(
etud, etudid, sem, formsemestre_id, format, version
@ -892,14 +883,14 @@ def can_send_bulletin_by_mail(formsemestre_id):
def do_formsemestre_bulletinetud(
formsemestre_id,
etudid,
formsemestre: FormSemestre,
etudid: int,
version="long", # short, long, selectedevals
format="html",
nohtml=False,
xml_with_decisions=False, # force decisions dans XML
force_publishing=False, # force publication meme si semestre non publie sur "portail"
prefer_mail_perso=False, # mails envoyes sur adresse perso si non vide
xml_with_decisions=False, # force décisions dans XML
force_publishing=False, # force publication meme si semestre non publié sur "portail"
prefer_mail_perso=False, # mails envoyés sur adresse perso si non vide
):
"""Génère le bulletin au format demandé.
Retourne: (bul, filigranne)
@ -908,7 +899,7 @@ def do_formsemestre_bulletinetud(
"""
if format == "xml":
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
formsemestre_id,
formsemestre.id,
etudid,
xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing,
@ -919,7 +910,7 @@ def do_formsemestre_bulletinetud(
elif format == "json":
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
formsemestre_id,
formsemestre.id,
etudid,
xml_with_decisions=xml_with_decisions,
force_publishing=force_publishing,
@ -927,8 +918,13 @@ def do_formsemestre_bulletinetud(
)
return bul, ""
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
etud = I["etud"]
if formsemestre.formation.is_apc():
etud = Identite.query.get(etudid)
r = bulletin_but.BulletinBUT(formsemestre)
I = r.bulletin_etud_complet(etud, formsemestre)
else:
I = formsemestre_bulletinetud_dict(formsemestre.id, etudid)
etud = I["etud"]
if format == "html":
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
@ -954,7 +950,7 @@ def do_formsemestre_bulletinetud(
elif format == "pdfmail":
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
# 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 !")
if nohtml:
@ -983,7 +979,7 @@ def do_formsemestre_bulletinetud(
) + htm
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>' % (
recipient_addr,
recipient_addr,

View File

@ -99,7 +99,7 @@ def bulletin_get_class_name_displayed(formsemestre_id):
return "invalide ! (voir paramètres)"
class BulletinGenerator(object):
class BulletinGenerator:
"Virtual superclass for PDF bulletin generators" ""
# Here some helper methods
# see sco_bulletins_standard.BulletinGeneratorStandard subclass for real methods

View File

@ -61,12 +61,10 @@ from reportlab.platypus.doctemplate import BaseDocTemplate
from flask import g, request
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.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_preferences
from app.scodoc import sco_etud
@ -190,7 +188,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
i = 1
for etud in formsemestre.get_inscrits(include_demdef=True, order=True):
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre_id,
formsemestre,
etud.id,
format="pdfpart",
version=version,
@ -239,8 +237,9 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
filigrannes = {}
i = 1
for sem in etud["sems"]:
formsemestre = FormSemestre.query.get(sem["formsemestre_id"])
frag, filigranne = sco_bulletins.do_formsemestre_bulletinetud(
sem["formsemestre_id"],
formsemestre,
etudid,
format="pdfpart",
version=version,
@ -275,3 +274,16 @@ def get_etud_bulletins_pdf(etudid, version="selectedevals"):
)
return pdfdoc, filename
def get_filigranne(etud_etat: str, prefs) -> 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 I["decision_sem"]) or prefs[
"bul_show_temporary_forced"
]:
return prefs["bul_temporary_txt"]
return ""

View File

@ -2114,7 +2114,7 @@ class BasePreferences(object):
return form
class SemPreferences(object):
class SemPreferences:
"""Preferences for a formsemestre"""
def __init__(self, formsemestre_id=None):
@ -2270,9 +2270,8 @@ def doc_preferences():
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"
prefs = SemPreferences(formsemestre_id)
fields = (
"bul_show_abs",
"bul_show_abs_modules",

View File

@ -97,7 +97,8 @@ section>div:nth-child(1){
.hide_coef .synthese em,
.hide_coef .eval>em,
.hide_date_inscr .dateInscription,
.hide_ects .ects{
.hide_ects .ects,
.hide_rangs .rang{
display: none;
}
@ -158,7 +159,10 @@ section>div:nth-child(1){
text-align: right;
}
.rang{
text-decoration: underline var(--couleurIntense);
font-weight: bold;
}
.ue .rang{
font-weight: 400;
}
.decision{
margin: 5px 0;
@ -186,6 +190,9 @@ section>div:nth-child(1){
.synthese h3{
background: var(--couleurFondTitresUE);
}
.synthese .ue>div{
text-align: right;
}
.synthese em,
.eval em{
opacity: 0.6;
@ -308,6 +315,14 @@ h3{
margin-bottom: 8px;
}
@media screen and (max-width: 700px) {
section{
padding: 16px;
}
.syntheseModule, .eval {
margin: 0;
}
}
/*.absences{
display: grid;
grid-template-columns: auto auto;

View File

@ -254,6 +254,7 @@ class releveBUT extends HTMLElement {
</h3>
<div>
<div class=moyenne>Moyenne&nbsp;:&nbsp;${dataUE.moyenne?.value || "-"}</div>
<div class=rang>Rang&nbsp;:&nbsp;${dataUE.moyenne?.rang}&nbsp;/&nbsp;${dataUE.moyenne?.total}</div>
<div class=info>
Bonus&nbsp;:&nbsp;${dataUE.bonus || 0}&nbsp;-
Malus&nbsp;:&nbsp;${dataUE.malus || 0}

View File

@ -1925,7 +1925,7 @@ def formsemestre_bulletins_mailetuds(
nb_send = 0
for etudid in etudids:
h, _ = sco_bulletins.do_formsemestre_bulletinetud(
formsemestre_id,
formsemestre,
etudid,
version=version,
prefer_mail_perso=prefer_mail_perso,

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "9.1.56"
SCOVERSION = "9.2a-57"
SCONAME = "ScoDoc"