Update opolka/ScoDoc from ScoDoc/ScoDoc #2

Merged
opolka merged 1272 commits from ScoDoc/ScoDoc:master into master 2024-05-27 09:11:04 +02:00
11 changed files with 107 additions and 82 deletions
Showing only changes of commit 2b150cf521 - Show all commits

View File

@ -315,12 +315,6 @@ def create_app(config_class=DevConfig):
app.register_error_handler(503, postgresql_server_error) app.register_error_handler(503, postgresql_server_error)
app.register_error_handler(APIInvalidParams, handle_invalid_usage) app.register_error_handler(APIInvalidParams, handle_invalid_usage)
# Add some globals
# previously in Flask-Bootstrap:
app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance(
field, HiddenField
)
from app.auth import bp as auth_bp from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix="/auth") app.register_blueprint(auth_bp, url_prefix="/auth")
@ -338,8 +332,15 @@ def create_app(config_class=DevConfig):
from app.api import api_bp from app.api import api_bp
from app.api import api_web_bp from app.api import api_web_bp
# Jinja2 configuration
# Enable autoescaping of all templates, including .j2 # Enable autoescaping of all templates, including .j2
app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True) app.jinja_env.autoescape = select_autoescape(default_for_string=True, default=True)
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
# previously in Flask-Bootstrap:
app.jinja_env.globals["bootstrap_is_hidden_field"] = lambda field: isinstance(
field, HiddenField
)
# https://scodoc.fr/ScoDoc # https://scodoc.fr/ScoDoc
app.register_blueprint(scodoc_bp) app.register_blueprint(scodoc_bp)

View File

@ -1,6 +1,4 @@
{# Base de toutes les pages ScoDoc #} {%- block doc -%}<!DOCTYPE html>{# Base de toutes les pages ScoDoc #}
{% block doc -%}
<!DOCTYPE html>
<html{% block html_attribs %}{% endblock html_attribs %}> <html{% block html_attribs %}{% endblock html_attribs %}>
{%- block html %} {%- block html %}
<head> <head>

View File

@ -1,5 +1,5 @@
{# -*- mode: jinja-html -*- #} {# -*- mode: jinja-html -*- #}
{% extends "base.j2" %} {% extends "sco_page.j2" %}
{% import 'wtf.j2' as wtf %} {% import 'wtf.j2' as wtf %}
{% block app_content %} {% block app_content %}

View File

@ -3,31 +3,31 @@
<!-- formsemestre_header --> <!-- formsemestre_header -->
<div class="formsemestre_page_title noprint"> <div class="formsemestre_page_title noprint">
<div class="infos"> <div class="infos">
<span class="semtitle"><a class="stdlink" title="{{sco.sem.session_id()}}" href="{{ <span class="semtitle"><a class="stdlink" title="{{sco.formsemestre.session_id()}}" href="{{
url_for('notes.formsemestre_status', url_for('notes.formsemestre_status',
scodoc_dept=g.scodoc_dept, formsemestre_id=sco.sem.id) scodoc_dept=g.scodoc_dept, formsemestre_id=sco.formsemestre.id)
}}">{{sco.sem.titre}}</a> }}">{{sco.formsemestre.titre}}</a>
<a title="{{sco.sem.etapes_apo_str()}}"> <a title="{{sco.formsemestre.etapes_apo_str()}}">
{% if sco.sem.semestre_id != -1 %}, {{sco.sem.formation.get_cursus().SESSION_NAME}} {% if sco.formsemestre.semestre_id != -1 %}, {{sco.formsemestre.formation.get_cursus().SESSION_NAME}}
{{sco.sem.semestre_id}} {{sco.formsemestre.semestre_id}}
{% endif %}</a> {% endif %}</a>
{% if sco.sem.modalite %} en {{sco.sem.modalite}}{% endif %}</span> {% if sco.formsemestre.modalite %} en {{sco.formsemestre.modalite}}{% endif %}</span>
<span class="dates"> <span class="dates">
<a title="du {{sco.sem.date_debut.strftime('%d/%m/%Y')}} <a title="du {{sco.formsemestre.date_debut.strftime('%d/%m/%Y')}}
au {{sco.sem.date_fin.strftime('%d/%m/%Y')}} ">{{scu.MONTH_NAMES_ABBREV[ sco.sem.date_debut.month - 1]}} au {{sco.formsemestre.date_fin.strftime('%d/%m/%Y')}} ">{{scu.MONTH_NAMES_ABBREV[ sco.formsemestre.date_debut.month - 1]}}
{{sco.sem.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.sem.date_fin.month - 1]}} {{sco.formsemestre.date_debut.year}} - {{scu.MONTH_NAMES_ABBREV[sco.formsemestre.date_fin.month - 1]}}
{{sco.sem.date_fin.year}}</a></span> {{sco.formsemestre.date_fin.year}}</a></span>
<span class="resp"><a <span class="resp"><a
title="{{sco.sem.responsables_str(abbrev_prenom=False)}}">{{sco.sem.responsables_str()}}</a></span> title="{{sco.formsemestre.responsables_str(abbrev_prenom=False)}}">{{sco.formsemestre.responsables_str()}}</a></span>
<span class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_view', scodoc_dept=g.scodoc_dept, <span class="nbinscrits"><a class="discretelink" href="{{url_for('scolar.groups_view', scodoc_dept=g.scodoc_dept,
formsemestre_id=sco.sem.id)}}">{{sco.sem.inscriptions|length}} inscrits</a></span><span class="lock">{% if formsemestre_id=sco.formsemestre.id)}}">{{sco.formsemestre.inscriptions|length}} inscrits</a></span><span class="lock">{% if
not sco.sem.etat %}<a href="{{url_for('notes.formsemestre_flip_lock', scodoc_dept=g.scodoc_dept, not sco.formsemestre.etat %}<a href="{{url_for('notes.formsemestre_flip_lock', scodoc_dept=g.scodoc_dept,
formsemestre_id=sco.sem.id)}}">{{scu.icontag("lock_img", border="0", title="Semestre formsemestre_id=sco.formsemestre.id)}}">{{scu.icontag("lock_img", border="0", title="Semestre
verrouillé")|safe}}</a>{% endif %}</span><span class="eye"> verrouillé")|safe}}</a>{% endif %}</span><span class="eye">
{% if not scu.is_passerelle_disabled() %} {% if not scu.is_passerelle_disabled() %}
<a href="{{url_for('notes.formsemestre_change_publication_bul', scodoc_dept=g.scodoc_dept, <a href="{{url_for('notes.formsemestre_change_publication_bul', scodoc_dept=g.scodoc_dept,
formsemestre_id=sco.sem.id)}}"> formsemestre_id=sco.formsemestre.id)}}">
{% if sco.sem.bul_hide_xml %} {% if sco.formsemestre.bul_hide_xml %}
{{ scu.ICON_HIDDEN|safe}} {{ scu.ICON_HIDDEN|safe}}
{% else %} {% else %}
{{ scu.ICON_PUBLISHED|safe }} {{ scu.ICON_PUBLISHED|safe }}
@ -36,6 +36,6 @@
</span> </span>
</div> </div>
{{ sco.sem_menu_bar|safe }} {{ sco.formsemestre_menu_bar|safe }}
</div> </div>
<!-- end of formsemestre_header --> <!-- end of formsemestre_header -->

View File

@ -1,5 +1,6 @@
{%- extends 'babase.j2' -%}
{# -*- Base des pages ordinaires, dans départements -*- #} {# -*- Base des pages ordinaires, dans départements -*- #}
{% extends 'babase.j2' %} <!-- sco_page -->
{% block styles %} {% block styles %}
{{super()}} {{super()}}
@ -27,7 +28,7 @@
<div id="gtrcontent" class="gtrcontent"> <div id="gtrcontent" class="gtrcontent">
{% include "flashed_messages.j2" %} {% include "flashed_messages.j2" %}
{% if sco.sem %} {% if sco.formsemestre %}
{% block formsemestre_header %} {% block formsemestre_header %}
{% include "formsemestre_header.j2" %} {% include "formsemestre_header.j2" %}
{% endblock %} {% endblock %}

View File

@ -58,7 +58,7 @@
{% if sco.etud_cur_sem %} {% if sco.etud_cur_sem %}
<span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }} <span title="absences du {{ sco.etud_cur_sem['date_debut'].strftime('%d/%m/%Y') }}
au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}">({{sco.prefs["assi_metrique"]}}) au {{ sco.etud_cur_sem['date_fin'].strftime('%d/%m/%Y') }}">({{sco.prefs["assi_metrique"]}})
<br />{{'%1g'|format(sco.nbabsjust)}} J., {{'%1g'|format(sco.nbabsnj)}} N.J.</span> <br />{{'%1g'|format(sco.nb_abs_just)}} J., {{'%1g'|format(sco.nb_abs_nj)}} N.J.</span>
{% endif %} {% endif %}
<ul> <ul>
{% if current_user.has_permission(sco.Permission.AbsChange) %} {% if current_user.has_permission(sco.Permission.AbsChange) %}

View File

@ -2,6 +2,7 @@
"""ScoDoc Flask views """ScoDoc Flask views
""" """
import datetime import datetime
from functools import cached_property
from flask import Blueprint from flask import Blueprint
from flask import g, current_app, request from flask import g, current_app, request
@ -57,8 +58,20 @@ class ScoData:
self.Permission = Permission self.Permission = Permission
self.scu = scu self.scu = scu
self.SCOVERSION = sco_version.SCOVERSION self.SCOVERSION = sco_version.SCOVERSION
# -- Informations étudiant courant, si sélectionné: self._init_etud = etud
if etud is None: self._init_formsemestre = formsemestre
# les comptes d'absences sont initialisés lors de l'accès à etud_cur_sem
self.nb_abs_nj = 0
self.nb_abs_just = 0
self.nb_abs = 0
# .etud, .formsemestre, etc. sont des cached_property
# car ScoData() est créé par @context_processor
# AVANT le décorateur scodoc qui initialise g.scodoc_xxx
@cached_property
def etud(self) -> Identite | None:
"Informations étudiant courant, si sélectionné"
if self._init_etud is None:
etudid = g.get("etudid", None) etudid = g.get("etudid", None)
if etudid is None: if etudid is None:
if request.method == "GET": if request.method == "GET":
@ -66,50 +79,61 @@ class ScoData:
elif request.method == "POST": elif request.method == "POST":
etudid = request.form.get("etudid", None) etudid = request.form.get("etudid", None)
if etudid is not None: if etudid is not None:
etud = Identite.get_etud(etudid) return Identite.get_etud(etudid)
self.etud = etud return self._init_etud
if etud is not None:
# Infos sur l'étudiant courant @cached_property
ins = self.etud.inscription_courante() def etud_cur_sem(self) -> FormSemestre | None:
if ins: "le semestre courant de l'étudiant courant"
self.etud_cur_sem = ins.formsemestre etud = self.etud
( if etud is None:
self.nbabsnj, return None
self.nbabsjust, ins = self.etud.inscription_courante()
self.nbabs, cur_sem = ins.formsemestre
) = sco_assiduites.get_assiduites_count_in_interval( if ins:
etud.id, (
self.etud_cur_sem.date_debut.isoformat(), self.nb_abs_nj,
self.etud_cur_sem.date_fin.isoformat(), self.nb_abs_just,
scu.translate_assiduites_metric( self.nb_abs,
sco_preferences.get_preference("assi_metrique") ) = sco_assiduites.get_assiduites_count_in_interval(
), etud.id,
) cur_sem.date_debut.isoformat(),
else: cur_sem.date_fin.isoformat(),
self.etud_cur_sem = None scu.translate_assiduites_metric(
else: sco_preferences.get_preference("assi_metrique")
self.etud = None ),
# --- Informations sur semestre courant, si sélectionné
if formsemestre is None:
formsemestre_id = retreive_formsemestre_from_request()
if formsemestre_id is not None:
formsemestre = FormSemestre.get_formsemestre(formsemestre_id)
if formsemestre is None:
self.sem = None
self.sem_menu_bar = None
else:
self.sem = formsemestre
self.sem_menu_bar = sco_formsemestre_status.formsemestre_status_menubar(
self.sem
) )
self.formsemestre = formsemestre return cur_sem
# --- Préférences return None
# prefs fallback to global pref if sem is None:
if formsemestre: @cached_property
formsemestre_id = formsemestre.id def formsemestre(self) -> FormSemestre | None:
else: "Le formsemestre courant, si sélectionné"
formsemestre_id = None if self._init_formsemestre is None:
self.prefs = sco_preferences.SemPreferences(formsemestre_id) formsemestre_id = retreive_formsemestre_from_request()
return (
FormSemestre.get_formsemestre(formsemestre_id)
if formsemestre_id is not None
else None
)
return self._init_formsemestre
@cached_property
def sem_menu_bar(self) -> str | None:
"Le html de la bare de menu formsemestre s'il y en a un."
return (
sco_formsemestre_status.formsemestre_status_menubar(self.formsemestre)
if self.formsemestre
else None
)
@cached_property
def prefs(self):
"Préférences"
# prefs fallback to global pref if no current formsemestre:
return sco_preferences.SemPreferences(
self.formsemestre.id if self.formsemestre else None
)
from app.views import ( from app.views import (

View File

@ -823,7 +823,8 @@ def form_change_coordonnees(etudid):
("telephonemobile", {"size": 13, "title": "Mobile"}), ("telephonemobile", {"size": 13, "title": "Mobile"}),
), ),
initvalues=adr, initvalues=adr,
submitlabel="Valider le formulaire", submitlabel="Enregistrer",
cancelbutton="Annuler",
) )
dest_url = url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid) dest_url = url_for("scolar.fiche_etud", scodoc_dept=g.scodoc_dept, etudid=etudid)
if tf[0] == 0: if tf[0] == 0:

View File

@ -1,7 +1,7 @@
# -*- mode: python -*- # -*- mode: python -*-
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
SCOVERSION = "9.6.959" SCOVERSION = "9.6.960"
SCONAME = "ScoDoc" SCONAME = "ScoDoc"

View File

@ -45,7 +45,7 @@ from app.models.evaluations import Evaluation
from app.scodoc import sco_dump_db from app.scodoc import sco_dump_db
from app.scodoc.sco_logos import make_logo_local from app.scodoc.sco_logos import make_logo_local
from app.scodoc.sco_permissions import Permission from app.scodoc.sco_permissions import Permission
from app.views import notes, scolar from app.views import notes, scolar, ScoData
import app.scodoc.sco_utils as scu import app.scodoc.sco_utils as scu
import tools import tools
from tools.fakedatabase import create_test_api_database from tools.fakedatabase import create_test_api_database
@ -58,9 +58,9 @@ cli.register(app)
@app.context_processor @app.context_processor
def inject_sco_utils(): def inject_sco_utils():
"Make scu available in all Jinja templates" "Make scu and sco available in all Jinja templates"
# if modified, put the same in conftest.py#27 # if modified, put the same in conftest.py#27
return dict(scu=scu) return {"scu": scu, "sco": ScoData()}
@app.shell_context_processor @app.shell_context_processor

View File

@ -27,7 +27,7 @@ def test_client():
@apptest.context_processor @apptest.context_processor
def inject_sco_utils(): def inject_sco_utils():
"Make scu available in all Jinja templates" "Make scu available in all Jinja templates"
return dict(scu=scu) return {"scu": scu, "sco": ScoData()}
with apptest.test_request_context(): with apptest.test_request_context():
# initialize scodoc "g": # initialize scodoc "g":