diff --git a/app/but/bulletin_but_court.py b/app/but/bulletin_but_court.py new file mode 100644 index 000000000..1831cf6f2 --- /dev/null +++ b/app/but/bulletin_but_court.py @@ -0,0 +1,87 @@ +############################################################################## +# ScoDoc +# Copyright (c) 1999 - 2023 Emmanuel Viennet. All rights reserved. +# See LICENSE +############################################################################## + +"""Génération bulletin BUT synthétique en une page + +On génère du HTML. Il sera si possible traduit en PDF par weasyprint. + +Le HTML est lui même généré à partir d'un template Jinja. + +## Données + +Ces données sont des objets passés au template. + +- `etud: Identite` : l'étudiant +- `formsemestre: FormSemestre` : le formsemestre d'où est émis ce bulletin +- `bulletins_sem: BulletinBUT` les données bulletins pour tous les étudiants +- `bul: dict` : le bulletin (dict, même structure que le json publié) +- `cursus: EtudCursusBUT`: infos sur le cursus BUT (niveaux validés etc) +- `decision_ues: dict`: `{ acronyme_ue : { 'code' : 'ADM' }}` accès aux décisions + de jury d'UE +- `ects_total` : nombre d'ECTS validées dans ce cursus +- `ue_validation_by_niveau : dict` : les validations d'UE de chaque niveau du cursus +""" +import datetime +import time + +from flask import render_template, url_for +from flask import g, request + +from app.but.bulletin_but import BulletinBUT +from app.but import cursus_but, validations_view +from app.decorators import ( + scodoc, + permission_required, +) +from app.models import FormSemestre, FormSemestreInscription, Identite +from app.scodoc.sco_exceptions import ScoNoReferentielCompetences +from app.scodoc.sco_logos import find_logo +from app.scodoc.sco_permissions import Permission +from app.views import notes_bp as bp +from app.views import ScoData + + +@bp.route("/bulletin_but//") +@scodoc +@permission_required(Permission.ScoView) +def bulletin_but(formsemestre_id: int, etudid: int = None): + """Page HTML affichant le bulletin BUT simplifié""" + etud: Identite = Identite.query.get_or_404(etudid) + formsemestre: FormSemestre = ( + FormSemestre.query.filter_by(id=formsemestre_id) + .join(FormSemestreInscription) + .filter_by(etudid=etudid) + .first_or_404() + ) + bulletins_sem = BulletinBUT(formsemestre) + bul = bulletins_sem.bulletin_etud(etud, formsemestre) # dict + decision_ues = {x["acronyme"]: x for x in bul["semestre"]["decision_ue"]} + cursus = cursus_but.EtudCursusBUT(etud, formsemestre.formation) + refcomp = formsemestre.formation.referentiel_competence + if refcomp is None: + raise ScoNoReferentielCompetences(formation=formsemestre.formation) + ue_validation_by_niveau = validations_view.get_ue_validation_by_niveau( + refcomp, etud + ) + ects_total = sum((v.ects() for v in ue_validation_by_niveau.values())) + + logo = find_logo(logoname="header", dept_id=g.scodoc_dept_id) + + return render_template( + "but/bulletin_court_page.j2", + bul=bul, + bulletins_sem=bulletins_sem, + cursus=cursus, + datetime=datetime, + decision_ues=decision_ues, + ects_total=ects_total, + etud=etud, + formsemestre=formsemestre, + logo=logo, + sco=ScoData(formsemestre=formsemestre, etud=etud), + time=time, + ue_validation_by_niveau=ue_validation_by_niveau, + ) diff --git a/app/scodoc/sco_bulletins_json.py b/app/scodoc/sco_bulletins_json.py index 72661afc8..8efcec253 100644 --- a/app/scodoc/sco_bulletins_json.py +++ b/app/scodoc/sco_bulletins_json.py @@ -426,6 +426,7 @@ def dict_decision_jury( etud: Identite, formsemestre: FormSemestre, with_decisions: bool = False ) -> dict: """dict avec decision pour bulletins json + - autorisation_inscription - decision : décision semestre - decision_ue : list des décisions UE - situation @@ -511,7 +512,10 @@ def dict_decision_jury( d["autorisation_inscription"] = [] for aut in decision["autorisations"]: d["autorisation_inscription"].append( - dict(semestre_id=aut["semestre_id"]) + dict( + semestre_id=aut["semestre_id"], + date=aut["date"].isoformat() if aut["date"] else None, + ) ) else: d["decision"] = dict(code="", etat="DEM") diff --git a/app/static/css/bulletin_court.css b/app/static/css/bulletin_court.css new file mode 100644 index 000000000..4438d058d --- /dev/null +++ b/app/static/css/bulletin_court.css @@ -0,0 +1,118 @@ + +div.but_bul_court { + width: 17cm; + display: grid; + grid-template-columns: 6cm 11cm; + font-size: 11pt; +} + +#infos_etudiant { + grid-column: 1; + grid-row: 1; + border-radius: 3mm; + border: 1px solid black; + background-color: white; + padding: 5mm; +} +.nom { + font-weight: bold; + font-size: 14pt; +} + + +#logo { + grid-column: 2; + grid-row: 1; + justify-self: end; +} + +#logo img { + text-align: right; + height: 3cm; +} + +div.but_bul_court table { + border-collapse: collapse; + border: 2px solid black; +} + +div.but_bul_court table th, +div.but_bul_court table td { + background-color: white; + border: 1px solid black; /* Thin black border between cells */ + padding: 2px 4px 2px 4px; /* Padding inside the cells */ +} + +table td.col_ue { + width: 18mm; +} + +#ues { + grid-row: 2; + grid-column: 1/3; + justify-self: end; + margin-top: 5mm; + margin-bottom: 5mm; +} + +#ues tr.titre_table th { + background-color: rgb(183,235,255); + padding: 2mm; +} + +tr.titres_ues td, tr.jury td { + font-weight: bold; +} + +table.resultats_modules { + width: 100%; +} + +#ressources { + grid-row: 3; + grid-column: 1/3; + margin-bottom: 5mm; + width: 100%; +} +#ressources tr.titres_ues td:first-child { + background-color: rgb(255, 192, 0); +} +#saes { + grid-row: 4; + grid-column: 1/3; + margin-bottom: 5mm; + width: 100%; +} +#saes tr.titres_ues td:first-child { + background-color: rgb(176, 255, 99); +} + +#row_situation { + grid-row: 5; + grid-column: 1/3; + display: grid; + grid-template-columns: auto auto; +} +#cursus_etud, #situation { + grid-row: 1; +} +#situation { + background-color: white; + justify-self: end; + margin-left: 1cm; + border-radius: 3mm; + border: 1px solid black; + padding: 5mm; +} + +#footer { + grid-row: 6; + grid-column: 1/3; + margin-top: 5mm; + font-size: 9pt; + font-style: italic; +} + +.but_bul_court .cursus_but { + margin-left: 0px; +} \ No newline at end of file diff --git a/app/templates/but/bulletin_court_page.j2 b/app/templates/but/bulletin_court_page.j2 new file mode 100644 index 000000000..cedef09e9 --- /dev/null +++ b/app/templates/but/bulletin_court_page.j2 @@ -0,0 +1,162 @@ +{% extends "sco_page.j2" %} + +{% block styles %} + {{super()}} + + + +{% endblock %} + +{% macro table_modules(mod_type, title) -%} + + + + + + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for mod in bul[mod_type] %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + {% endfor %} + +
Unités d'enseignement
{{title}}{{ue}}
{{mod}}{{bul[mod_type][mod].titre}}{{ + bul.ues[ue][mod_type][mod].moyenne + if mod in bul.ues[ue][mod_type] else "" + }}
+{%- endmacro %} + +{% block app_content %} + +
+
+
{{etud.nomprenom}}
+
BUT {{formsemestre.formation.referentiel_competence.specialite}}
+ {% if formsemestre.etuds_inscriptions[etud.id].parcour %} +
Parcours {{formsemestre.etuds_inscriptions[etud.id].parcour.code}}
+ {% endif %} +
Année {{formsemestre.annee_scolaire_str()}}
+
Semestre {{formsemestre.semestre_id}}
+
+ + + +
+ + + + + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + + + {% for ue in bul.ues %} + + {% endfor %} + + +
Unités d'enseignement du semestre {{formsemestre.semestre_id}}
{{ue}}
Moyenne{{bul.ues[ue].moyenne.value}}
Bonus{{bul.ues[ue].bonus if bul.ues[ue].bonus != "00.00" else ""}}
Malus{{bul.ues[ue].malus if bul.ues[ue].malus != "00.00" else ""}}
Rang{{bul.ues[ue].moyenne.rang}}
Effectif{{bul.ues[ue].moyenne.total}}
ECTS{{bul.ues[ue].moyenne.ects}}
Jury{{decision_ues[ue].code}}
+
+ +
+ {{ table_modules("ressources", "Ressources") }} +
+ +
+ {{ table_modules("saes", "Situations d'Apprentissage et d'Évaluation (SAÉ)") }} +
+ +
+
+ {% include "but/cursus_etud.j2" %} +
+ +
+
ECTS acquis : {{ects_total}}
+
+ {% if bul.semestre.decision_annee %} + Jury tenu le {{ + datetime.datetime.fromisoformat(bul.semestre.decision_annee.date).strftime("%d/%m/%Y à %H:%M") + }}, + année BUT {{bul.semestre.decision_annee.code}}. + {% endif %} + {% set virg = joiner(", ") %} + {% for aut in bul.semestre.autorisation_inscription -%} + {% if loop.first %} + Autorisé à s'inscrire en + {% endif %} + {{- virg() }}S{{aut.semestre_id -}} + {%- if loop.last -%} + . + {%- endif -%} + {%- endfor %} +
+
+
+ + +
+ +{% endblock %} diff --git a/app/views/notes.py b/app/views/notes.py index da80388a3..7e0ad26ec 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -35,7 +35,7 @@ from operator import itemgetter import time import flask -from flask import abort, flash, redirect, render_template, url_for +from flask import flash, redirect, render_template, url_for from flask import g, request from flask_login import current_user @@ -44,6 +44,7 @@ from app import models from app.auth.models import User from app.but import ( apc_edit_ue, + bulletin_but_court, cursus_but, jury_edit_manual, jury_but, @@ -58,7 +59,6 @@ from app.comp import jury, res_sem from app.comp.res_compat import NotesTableCompat from app.models import ( Formation, - ScolarFormSemestreValidation, ScolarAutorisationInscription, ScolarNews, Scolog, diff --git a/tools/debian/control b/tools/debian/control index 4c5c809b7..299ce6afa 100644 --- a/tools/debian/control +++ b/tools/debian/control @@ -3,5 +3,5 @@ Version: x.y.z Architecture: amd64 Maintainer: Emmanuel Viennet Description: ScoDoc 9 - Un logiciel pour le suivi de la scolarité universitaire. -Depends: adduser, curl, gcc, graphviz, graphviz-dev, libpq-dev, postfix|exim4, cracklib-runtime, libcrack2-dev, python3-dev, python3-venv, python3-pip, python3-wheel, nginx, postgresql, libpq-dev, redis, ufw + Un logiciel pour le suivi de la scolarité universitaire. +Depends: adduser, curl, gcc, graphviz, graphviz-dev, libpq-dev, postfix|exim4, cracklib-runtime, libcrack2-dev, libpango-1.0-0, pango1.0-tools, python3-dev, python3-venv, python3-pip, python3-wheel, nginx, postgresql, libpq-dev, redis, ufw