Compare commits

..

No commits in common. "a43f1e0e229b394d79d8d37731bc557a4944b8c4" and "433b4b8f5c7839f7fb4ddd9bc5c504224a5dd63d" have entirely different histories.

10 changed files with 214 additions and 153 deletions

View File

@ -16,8 +16,8 @@ from app.scodoc.sco_saisie_notes import notes_add
@bp.route("/departements", methods=["GET"])
@token_auth.login_required # Commenté le temps des tests
# @permission_required(Permission.ScoView)
# @token_auth.login_required # Commenté le temps des tests
# @permission_required(Permission.APIView)
def departements():
"""
Retourne la liste des ids de départements visibles
@ -35,7 +35,7 @@ def departements():
@bp.route("/departements/<string:dept>/etudiants/liste", methods=["GET"])
@bp.route("/departements/<string:dept>/etudiants/liste/<int:formsemestre_id>", methods=["GET"])
@token_auth.login_required
# @token_auth.login_required
# @permission_required(Permission.APIView)
def liste_etudiants(dept: str, formsemestre_id=None):
"""
@ -94,6 +94,51 @@ def liste_etudiants(dept: str, formsemestre_id=None):
return jsonify(list_etu)
# Si le formsemestre_id a été renseigné
# if formsemestre_id is not None:
# a = 0
# Récupération du/des depts
# list_depts = models.Departement.query.filter(
# models.Departement.acronym == dept,
# models.FormSemestre.id == formsemestre_id,
# )
#
# formsemestre = models.FormSemestre.query.get_or_404(formsemestre_id)
# list_depts = formsemestre.departement
# print(formsemestre)
#
# dept_id = list_depts
# list_etuds = []
# for dept in list_depts:
# # Récupération des étudiants d'un département
# etudiants = models.Identite.query.filter_by(dept_id=dept.id).all()
# print(etudiants)
# for etu in etudiants:
# # Ajout des étudiants dans la liste global
# list_etuds.append(etu)
# Si le formsemestre_id n'a pas été renseigné
# else:
# # list_depts = models.Departement.query.filter(
# # models.Departement.acronym == dept,
# # models.FormSemestre.id == formsemestre_id,
# # )
#
# list_etuds = []
# for dept in list_depts:
# etudiants = models.Identite.query.filter_by(dept_id=dept.id).all()
# for etu in etudiants:
# list_etuds.append(etu)
#
# # Mise en forme des données
# data = [d.to_dict_bul() for d in list_etuds]
# return jsonify(data)
# return error_response(501, message="Not implemented")
@bp.route("/departements/<string:dept>/semestres_courants", methods=["GET"])
# @token_auth.login_required # Commenté le temps des tests
# @permission_required(Permission.APIView)

View File

@ -43,6 +43,8 @@ def get_departement():
get_token()
print("ééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééééé")
global HEADERS
global CHECK_CERTIFICATE
global SCODOC_USER
@ -55,6 +57,8 @@ def get_departement():
headers=HEADERS, verify=CHECK_CERTIFICATE,
)
print("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii")
if r.status_code == 200:
dept_id = r.json()[0]
# print(dept_id)

View File

@ -11,7 +11,7 @@ import datetime
from flask import url_for, g
from app.comp.res_but import ResultatsSemestreBUT
from app.models import FormSemestre, Identite
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
@ -171,10 +171,6 @@ class BulletinBUT:
# eval_notes est une pd.Series avec toutes les notes des étudiants inscrits
eval_notes = self.res.modimpls_results[e.moduleimpl_id].evals_notes[e.id]
notes_ok = eval_notes.where(eval_notes > scu.NOTES_ABSENCE).dropna()
poids = {
ue.acronyme: self.res.modimpls_evals_poids[e.moduleimpl_id][ue.id][e.id]
for ue in self.res.ues
}
d = {
"id": e.id,
"description": e.description,
@ -182,7 +178,7 @@ class BulletinBUT:
"heure_debut": e.heure_debut.strftime("%H:%M") if e.heure_debut else None,
"heure_fin": e.heure_fin.strftime("%H:%M") if e.heure_debut else None,
"coef": fmt_note(e.coefficient),
"poids": poids,
"poids": {p.ue.acronyme: p.poids for p in e.ue_poids},
"note": {
"value": fmt_note(
eval_notes[etud.id],
@ -334,13 +330,11 @@ class BulletinBUT:
return d
def bulletin_etud_complet(self, etud: Identite, version="long") -> dict:
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, version=version, force_publishing=True
)
d = self.bulletin_etud(etud, self.res.formsemestre, force_publishing=True)
d["etudid"] = etud.id
d["etud"] = d["etudiant"]
d["etud"]["nomprenom"] = etud.nomprenom

View File

@ -6,7 +6,8 @@
"""Génération bulletin BUT au format PDF standard
"""
from reportlab.platypus import Paragraph, Spacer
import itertools
from reportlab.platypus import KeepInFrame, Paragraph, Spacer
from app.scodoc.sco_pdf import blue, cm, mm
@ -34,14 +35,11 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
tables_infos = [
# ---- TABLE SYNTHESE UES
self.but_table_synthese_ues(),
# ---- TABLE RESSOURCES
self.but_table_ressources(),
# ---- TABLE SAE
self.but_table_saes(),
]
if self.version != "short":
tables_infos += [
# ---- TABLE RESSOURCES
self.but_table_ressources(),
# ---- TABLE SAE
self.but_table_saes(),
]
objects = []
for i, (col_keys, rows, pdf_style, col_widths) in enumerate(tables_infos):
table = gen_tables.GenTable(
@ -84,7 +82,6 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
"titre": "Unités d'enseignement",
"moyenne": "Note/20",
"coef": "Coef.",
"_coef_pdf": Paragraph("<para align=right><b><i>Coef.</i></b></para>"),
"_css_row_class": "note_bold",
"_pdf_row_markup": ["b"],
"_pdf_style": [
@ -100,7 +97,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
],
}
]
col_keys = ["titre", "coef", "moyenne"] # noms des colonnes à afficher
col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher
for ue_acronym, ue in self.infos["ues"].items():
# 1er ligne titre UE
moy_ue = ue.get("moyenne")
@ -123,16 +120,16 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
}
rows.append(t)
# 2eme ligne titre UE (bonus/malus/ects)
ects_txt = f'ECTS: {ue["ECTS"]["acquis"]:.3g} / {ue["ECTS"]["total"]:.3g}'
t = {
"titre": f"""Bonus: {ue['bonus']} - Malus: {
ue["malus"]}""",
"coef": ects_txt,
"_coef_pdf": Paragraph(f"""<para align=right>{ects_txt}</para>"""),
"_coef_colspan": 2,
"moyenne": f"""ECTS: {ue["ECTS"]["acquis"]} / {ue["ECTS"]["total"]}""",
"_moyenne_colspan": 2,
# "_css_row_class": "",
# "_pdf_row_markup": [""],
"_pdf_style": [
("ALIGN", (0, 0), (1, 0), "RIGHT"),
("TEXTCOLOR", (0, 0), (-1, 0), blue),
("BACKGROUND", (0, 0), (-1, 0), title_bg),
(
"LINEBELOW",
@ -141,14 +138,6 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
self.PDF_LINEWIDTH,
self.PDF_LINECOLOR,
),
# cadre autour du bonus/malus
(
"BOX",
(0, 0),
(0, 0),
self.PDF_LINEWIDTH,
(0.7, 0.7, 0.7), # gris clair
),
],
}
rows.append(t)
@ -203,54 +192,40 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
- pdf_style : commandes table Platypus
- largeurs de colonnes pour PDF
"""
poids_fontsize = "8"
# UE à utiliser pour les poids (# colonne/UE)
ue_acros = list(self.infos["ues"].keys()) # ['RT1.1', 'RT2.1', 'RT3.1']
# Colonnes à afficher:
col_keys = ["titre"] + ue_acros + ["coef", "moyenne"]
# Largeurs des colonnes:
col_widths = {
"titre": None,
# "poids": None,
"moyenne": 2 * cm,
"coef": 2 * cm,
}
for ue_acro in ue_acros:
col_widths[ue_acro] = 12 * mm # largeur col. poids
title_bg = tuple(x / 255.0 for x in title_bg)
# elems pour générer table avec gen_table (liste de dicts)
# Ligne de titres
t = {
"titre": title,
# "_titre_colspan": 1 + len(ue_acros),
"moyenne": "Note/20",
"coef": "Coef.",
"_coef_pdf": Paragraph("<para align=right><i>Coef.</i></para>"),
"_css_row_class": "note_bold",
"_pdf_row_markup": ["b"],
"_pdf_style": [
("BACKGROUND", (0, 0), (-1, 0), title_bg),
("BOTTOMPADDING", (0, 0), (-1, 0), 7),
(
"LINEBELOW",
(0, 0),
(-1, 0),
self.PDF_LINEWIDTH,
blue,
),
],
}
for ue_acro in ue_acros:
t[ue_acro] = Paragraph(
f"<para align=right fontSize={poids_fontsize}><i>{ue_acro}</i></para>"
)
rows = [t]
rows = [
# Ligne de titres
{
"titre": title,
"moyenne": "Note/20",
"coef": "Coef.",
"_css_row_class": "note_bold",
"_pdf_row_markup": ["b"],
"_pdf_style": [
("BACKGROUND", (0, 0), (-1, 0), title_bg),
("BOTTOMPADDING", (0, 0), (-1, 0), 7),
(
"LINEBELOW",
(0, 0),
(-1, 0),
self.PDF_LINEWIDTH,
blue,
),
],
}
]
col_keys = ["titre", "moyenne", "coef"] # noms des colonnes à afficher
for mod_code, mod in self.infos[mod_type].items():
# 1er ligne titre module
t = {
"titre": f"{mod_code} - {mod['titre']}",
"_titre_colspan": 2 + len(ue_acros),
"moyenne": "", # pas de moyenne
"_css_row_class": "note_bold",
"_pdf_row_markup": ["b"],
"_pdf_style": [
@ -273,7 +248,7 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
"moyenne": e["note"]["value"],
"coef": e["coef"],
"_coef_pdf": Paragraph(
f"<para align=right fontSize={poids_fontsize}><i>{e['coef']}</i></para>"
f"<para align=right><i>{e['coef']}</i></para>"
),
"_pdf_style": [
(
@ -285,21 +260,6 @@ class BulletinGeneratorStandardBUT(BulletinGeneratorStandard):
)
],
}
col_idx = 1 # 1ere col. poids
for ue_acro in ue_acros:
t[ue_acro] = Paragraph(
f"""<para align=right fontSize={poids_fontsize}><i>{e["poids"].get(ue_acro, "")}</i></para>"""
)
t["_pdf_style"].append(
(
"BOX",
(col_idx, 0),
(col_idx, 0),
self.PDF_LINEWIDTH,
(0.7, 0.7, 0.7), # gris clair
),
)
col_idx += 1
rows.append(t)
# Global pdf style commands:
pdf_style = [

View File

@ -197,7 +197,6 @@ def notes_sem_load_cube(formsemestre: FormSemestre) -> tuple:
evals_poids, _ = moy_mod.load_evaluations_poids(modimpl.id)
etuds_moy_module = mod_results.compute_module_moy(evals_poids)
modimpls_results[modimpl.id] = mod_results
modimpls_evals_poids[modimpl.id] = evals_poids
modimpls_notes.append(etuds_moy_module)
if len(modimpls_notes):
cube = notes_sem_assemble_cube(modimpls_notes)

View File

@ -63,15 +63,12 @@ from app.scodoc.sco_pdf import SU
from app import log
def mark_paras(L, tags) -> list[str]:
"""Put each (string) element of L between <tag>...</tag>,
for each supplied tag.
Leave non string elements untouched.
"""
def mark_paras(L, tags):
"""Put each (string) element of L between <b>"""
for tag in tags:
start = "<" + tag + ">"
end = "</" + tag.split()[0] + ">"
L = [(start + (x or "") + end) if isinstance(x, str) else x for x in L]
b = "<" + tag + ">"
c = "</" + tag.split()[0] + ">"
L = [b + (x or "") + c for x in L]
return L

View File

@ -885,7 +885,7 @@ def do_formsemestre_bulletinetud(
if formsemestre.formation.is_apc():
etud = Identite.query.get(etudid)
r = bulletin_but.BulletinBUT(formsemestre)
I = r.bulletin_etud_complet(etud, version=version)
I = r.bulletin_etud_complet(etud)
else:
I = formsemestre_bulletinetud_dict(formsemestre.id, etudid)
etud = I["etud"]
@ -980,7 +980,7 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
except KeyError as e:
raise ScoValueError(
"format 'Message d'accompagnement' (bul_intro_mail) invalide, revoir les réglages dans les préférences"
) from e
)
else:
hea = ""
@ -1011,6 +1011,97 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
)
def _formsemestre_bulletinetud_header_html_old_XXX(
etud: Identite,
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"],
),
f"""<table class="bull_head"><tr><td>
<h2><a class="discretelink" href="{
url_for(
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id
)}">{etud.nomprenom}</a></h2>
<form name="f" method="GET" action="{request.base_url}">
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>
<br/>
<table><tr>
<td>établi le {time.strftime("%d/%m/%Y à %Hh%M")} (notes sur 20)</td>
<td><span class="rightjust">
<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>
<select name="version" onchange="document.f.submit()" class="noprint">
""",
]
for (v, e) in (
("short", "Version courte"),
("selectedevals", "Version intermédiaire"),
("long", "Version complète"),
):
if v == version:
selected = " selected"
else:
selected = ""
H.append('<option value="%s"%s>%s</option>' % (v, selected, e))
H.append("""</select></td>""")
# Menu
endpoint = "notes.formsemestre_bulletinetud"
menu_autres_operations = make_menu_autres_operations(
formsemestre, etud, endpoint, version
)
H.append("""<td class="bulletin_menubar"><div class="bulletin_menubar">""")
H.append(menu_autres_operations)
H.append("""</div></td>""")
H.append(
'<td> <a href="%s">%s</a></td>'
% (
url_for(
"notes.formsemestre_bulletinetud",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre.id,
etudid=etud.id,
format="pdf",
version=version,
),
scu.ICON_PDF,
)
)
H.append("""</tr></table>""")
#
H.append(
"""</form></span></td><td class="bull_photo"><a href="%s">%s</a>
"""
% (
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etud.id),
sco_photos.etud_photo_html(etud, title="fiche de " + etud.nomprenom),
)
)
H.append(
"""</td></tr>
</table>
"""
)
return "".join(H)
def make_menu_autres_operations(
formsemestre: FormSemestre, etud: Identite, endpoint: str, version: str
) -> str:

View File

@ -1885,6 +1885,7 @@ def formsemestre_bulletins_choice(
formsemestre_id, title="", explanation="", choose_mail=False
):
"""Choix d'une version de bulletin"""
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
H = [
html_sco_header.html_sem_header(title),
"""

View File

@ -28,34 +28,30 @@ def test_departements():
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 1
# liste_etudiants
def test_liste_etudiants():
def test_etudiants():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/departements/TAPI/etudiants/liste",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 16
r = requests.get(
SCODOC_URL + "/ScoDoc/api/departements/TAPI/etudiants/liste/1",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 16
# liste_semestres_courant
def test_semestres_courant():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/departements/TAPI/semestres_courants",
SCODOC_URL + "/ScoDoc/api/departements/TAPI/etudiants/liste/1",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 1
# referenciel_competences

View File

@ -16,7 +16,6 @@ Utilisation :
Lancer :
pytest tests/api/test_api_etudiants.py
"""
from random import randint
import requests
from tests.api.setup_test_api import SCODOC_URL, HEADERS, CHECK_CERTIFICATE
@ -25,46 +24,26 @@ from tests.api.setup_test_api import SCODOC_URL, HEADERS, CHECK_CERTIFICATE
# etudiants
def test_etudiants():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiants",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
SCODOC_URL + "/ScoDoc/api/etudiants",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 16
# Choisis aléatoirement un étudiant dans la liste des étudiants
etu = r.json()[randint(0, len(r.json()))-1]
fields = ["civilite", "code_ine", "code_nip", "date_naissance", "email", "emailperso", "etudid", "nom",
"prenom", "nomprenom"]
fields_OK = True
# Vérifie si tous les champs sont bien présents
for field in etu:
if field not in fields:
fields_OK = False
assert fields_OK is True
# etudiants_courant
def test_etudiants_courant():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
SCODOC_URL + "/ScoDoc/api/etudiants/courant",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 0
# etudiant
def test_etudiant():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 10
# r = requests.get(
# SCODOC_URL + "/ScoDoc/api/etudiant/nip/<int:nip>",
@ -81,11 +60,10 @@ def test_etudiant():
# etudiant_formsemestres
def test_etudiant_formsemestres():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestres",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestres",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 1
# r = requests.get(
# SCODOC_URL + "/ScoDoc/api/etudiant/nip/<int:nip>/formsemestres",
@ -101,14 +79,12 @@ def test_etudiant_formsemestres():
# etudiant_bulletin_semestre
# def test_etudiant_bulletin_semestre():
# r = requests.get(
# SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestre/1/bulletin",
# headers=HEADERS, verify=CHECK_CERTIFICATE
# )
# assert r.status_code == 200
# assert len(r.json()) == 1
def test_etudiant_bulletin_semestre():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/formsemestre/1/bulletin",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
# r = requests.get(
# SCODOC_URL + "/ScoDoc/api/etudiant/nip/<int:nip>/formsemestre/<int:formsemestre_id>/bulletin",
@ -126,12 +102,10 @@ def test_etudiant_formsemestres():
# etudiant_groups
def test_etudiant_groups():
r = requests.get(
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/semestre/1/groups",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
SCODOC_URL + "/ScoDoc/api/etudiant/etudid/1/semestre/1/groups",
headers=HEADERS, verify=CHECK_CERTIFICATE
)
assert r.status_code == 200
assert len(r.json()) == 1
# r = requests.get(
# SCODOC_URL + "/ScoDoc/api/etudiant/nip/<int:nip>/semestre/<int:formsemestre_id>/groups",
@ -143,4 +117,4 @@ def test_etudiant_groups():
# SCODOC_URL + "/ScoDoc/api/etudiant/ine/<int:ine>/semestre/<int:formsemestre_id>/groups",
# headers=HEADERS, verify=CHECK_CERTIFICATE
# )
# assert r.status_code == 200
# assert r.status_code == 200