forked from ScoDoc/ScoDoc
Compare commits
125 Commits
master
...
dyn_fields
Author | SHA1 | Date |
---|---|---|
Jean-Marie Place | 53aea89560 | |
Emmanuel Viennet | 671ef6a7fa | |
Emmanuel Viennet | edc6da3005 | |
Emmanuel Viennet | b015cf3f88 | |
Emmanuel Viennet | a2c16207cb | |
Jean-Marie Place | 1aa39c72fd | |
Jean-Marie Place | 00dbd25b42 | |
Emmanuel Viennet | 4e59b9597b | |
Emmanuel Viennet | f1660e12e1 | |
Emmanuel Viennet | cb03cc962c | |
Jean-Marie Place | 81df68b491 | |
Jean-Marie Place | bd6e3e6648 | |
Jean-Marie Place | cd06a780d5 | |
Emmanuel Viennet | 1741e75f72 | |
Emmanuel Viennet | c41726c4a8 | |
Emmanuel Viennet | 7879c176dd | |
Emmanuel Viennet | 75f43bbdde | |
Jean-Marie Place | 3f86d9b380 | |
Jean-Marie Place | 37303df74c | |
Emmanuel Viennet | 0a50edc9f0 | |
Emmanuel Viennet | 373feece76 | |
Jean-Marie Place | e4c9e5f9e8 | |
Jean-Marie Place | 1af6f79da9 | |
Emmanuel Viennet | 6d1ffb122b | |
Emmanuel Viennet | 92c401f17c | |
Emmanuel Viennet | 36c7358eed | |
Emmanuel Viennet | 9c5408f503 | |
Emmanuel Viennet | 2add3e12cc | |
Emmanuel Viennet | d0ab9dc66a | |
Emmanuel Viennet | beeca54a94 | |
Emmanuel Viennet | 73cf9a6f4d | |
Emmanuel Viennet | fede1ae7af | |
Jean-Marie Place | 845152afdd | |
Jean-Marie Place | a4d091fa2d | |
Jean-Marie Place | ffa7e07cd3 | |
Emmanuel Viennet | 865192bc0d | |
Jean-Marie Place | b56f205e89 | |
Emmanuel Viennet | eded2fffe9 | |
Emmanuel Viennet | 13f1539282 | |
Emmanuel Viennet | ae51e4c17a | |
Emmanuel Viennet | 7214627994 | |
Emmanuel Viennet | 6cc1b60da4 | |
Jean-Marie Place | 4297d36dad | |
Emmanuel Viennet | 2999199b19 | |
Emmanuel Viennet | f516ccdfe7 | |
Emmanuel Viennet | 2c97349acf | |
Emmanuel Viennet | 5dfc64a62d | |
Jean-Marie Place | 9dd8198c7b | |
Jean-Marie Place | b05aea95b6 | |
Emmanuel Viennet | f18a9c7559 | |
Emmanuel Viennet | 985c6df3b6 | |
Emmanuel Viennet | 286e9cdc2f | |
Emmanuel Viennet | 0381576750 | |
Emmanuel Viennet | 7a0a04bdb3 | |
Emmanuel Viennet | 35f23995aa | |
Emmanuel Viennet | 29221666a4 | |
Emmanuel Viennet | d7e6a7d714 | |
Jean-Marie Place | 179be1baa0 | |
Jean-Marie Place | a5ed9b815f | |
Jean-Marie Place | b552588c1c | |
Emmanuel Viennet | 13c027fc19 | |
Jean-Marie Place | 2dbc1ca695 | |
Jean-Marie Place | 9383a53569 | |
Emmanuel Viennet | 31505e1330 | |
Jean-Marie Place | 0621cb1d0f | |
Emmanuel Viennet | 9a9dc4a483 | |
Emmanuel Viennet | 11ba73d264 | |
Emmanuel Viennet | 7daa49f2aa | |
Jean-Marie Place | f7961a135a | |
Jean-Marie Place | c955870e1e | |
Jean-Marie Place | 80f5536de5 | |
Jean-Marie Place | 2519d08e40 | |
Emmanuel Viennet | 987800c30e | |
Jean-Marie Place | ae1feba96c | |
Jean-Marie Place | 2a72fb881b | |
Jean-Marie Place | 87ecd09f0e | |
Jean-Marie Place | 6e7a104fb0 | |
Jean-Marie Place | b03eee12a1 | |
Jean-Marie Place | 44117fb0e2 | |
Jean-Marie Place | 42ef9f795f | |
Emmanuel Viennet | bd2e0ccde5 | |
Jean-Marie Place | 5f0f437f2e | |
Jean-Marie Place | b6cc251c94 | |
Jean-Marie Place | 5f6c434497 | |
Emmanuel Viennet | 45352d9248 | |
Jean-Marie Place | b3225e07f7 | |
Jean-Marie Place | 0ef822cfd8 | |
Jean-Marie Place | a23ae38014 | |
Emmanuel Viennet | 7d59b52018 | |
Emmanuel Viennet | 80238545f3 | |
Emmanuel Viennet | 72e075530c | |
Emmanuel Viennet | 91cc421ef8 | |
Emmanuel Viennet | 8b6a569a31 | |
Emmanuel Viennet | c8949e870f | |
Emmanuel Viennet | 30481e4729 | |
Emmanuel Viennet | 085aff657a | |
Emmanuel Viennet | 3666f8b1ec | |
Emmanuel Viennet | bec7deb581 | |
Emmanuel Viennet | 6dbbcde454 | |
Emmanuel Viennet | 9578c789dc | |
Emmanuel Viennet | 0fedb7771c | |
Emmanuel Viennet | 6dba8933c4 | |
Emmanuel Viennet | 5efc493542 | |
Emmanuel Viennet | a34dd656be | |
Emmanuel Viennet | 2ec2be4234 | |
Emmanuel Viennet | 49609fa657 | |
Emmanuel Viennet | 8a16216d4b | |
Emmanuel Viennet | 96f457260f | |
Emmanuel Viennet | 0f9b52bc9b | |
Emmanuel Viennet | 83174f2f5e | |
Emmanuel Viennet | 3fbda90a2f | |
Emmanuel Viennet | de206674d9 | |
Emmanuel Viennet | b06f37b18e | |
Emmanuel Viennet | 3496cc7beb | |
Jean-Marie Place | 01c264c3c7 | |
Jean-Marie Place | c44aa808df | |
Jean-Marie Place | c8872bd220 | |
Jean-Marie Place | 7f63ab222b | |
Jean-Marie Place | ed07e42222 | |
Jean-Marie Place | 35768e9241 | |
Jean-Marie Place | 050e54de3e | |
Jean-Marie Place | 37484b7fc9 | |
Jean-Marie Place | f828134ea2 | |
Jean-Marie Place | a4d0205cc7 | |
Jean-Marie Place | 770ccb4d6e |
|
@ -170,3 +170,4 @@ Thumbs.db
|
|||
*.code-workspace
|
||||
|
||||
|
||||
copy
|
||||
|
|
327
README.md
327
README.md
|
@ -1,163 +1,164 @@
|
|||
|
||||
# ScoDoc - Gestion de la scolarité - Version ScoDoc 9
|
||||
|
||||
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
|
||||
|
||||
VERSION EXPERIMENTALE - NE PAS DEPLOYER - TESTS EN COURS
|
||||
|
||||
Installation: voir instructions à jour sur <https://scodoc.org/GuideInstallDebian11>
|
||||
|
||||
Documentation utilisateur: <https://scodoc.org>
|
||||
|
||||
## Version ScoDoc 9
|
||||
|
||||
N'utiliser que pour les développements et tests.
|
||||
|
||||
La version ScoDoc 9 est basée sur Flask (au lieu de Zope) et sur
|
||||
**python 3.9+**.
|
||||
|
||||
La version 9.0 s'efforce de reproduire presque à l'identique le fonctionnement
|
||||
de ScoDoc7, avec des composants logiciels différents (Debian 11, Python 3,
|
||||
Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
|
||||
|
||||
|
||||
|
||||
### État actuel (27 août 21)
|
||||
|
||||
- Tests en cours, notamment système d'installation et de migration.
|
||||
|
||||
**Fonctionnalités non intégrées:**
|
||||
|
||||
- feuille "placement" (en cours)
|
||||
|
||||
- ancien module "Entreprises" (obsolete)
|
||||
|
||||
|
||||
### Lignes de commandes
|
||||
|
||||
Voir [https://scodoc.org/GuideConfig](le guide de configuration).
|
||||
|
||||
|
||||
## Organisation des fichiers
|
||||
|
||||
L'installation comporte les fichiers de l'application, sous `/opt/scodoc/`, et
|
||||
les fichiers locaux (archives, photos, configurations, logs) sous
|
||||
`/opt/scodoc-data`. Par ailleurs, il y a évidemment les bases de données
|
||||
postgresql et la configuration du système Linux.
|
||||
|
||||
### Fichiers locaux
|
||||
Sous `/opt/scodoc-data`, fichiers et répertoires appartienant à l'utilisateur `scodoc`.
|
||||
Ils ne doivent pas être modifiés à la main, sauf certains fichiers de configuration sous
|
||||
`/opt/scodoc-data/config`.
|
||||
|
||||
Le répertoire `/opt/scodoc-data` doit être régulièrement sauvegardé.
|
||||
|
||||
Principaux contenus:
|
||||
|
||||
/opt/scodoc-data
|
||||
/opt/scodoc-data/log # Fichiers de log ScoDoc
|
||||
/opt/scodoc-data/config # Fichiers de configuration
|
||||
.../config/logos # Logos de l'établissement
|
||||
.../config/depts # un fichier par département
|
||||
/opt/scodoc-data/photos # Photos des étudiants
|
||||
/opt/scodoc-data/archives # Archives: PV de jury, maquettes Apogée, fichiers étudiants
|
||||
|
||||
## Pour les développeurs
|
||||
|
||||
### Installation du code
|
||||
|
||||
Installer ScoDoc 9 normalement ([voir la doc](https://scodoc.org/GuideInstallDebian11)).
|
||||
|
||||
Puis remplacer `/opt/scodoc` par un clone du git.
|
||||
|
||||
sudo su
|
||||
mv /opt/scodoc /opt/off-scodoc # ou ce que vous voulez
|
||||
apt-get install git # si besoin
|
||||
cd /opt
|
||||
git clone https://scodoc.org/git/viennet/ScoDoc.git
|
||||
# (ou bien utiliser votre clone gitea si vous l'avez déjà créé !)
|
||||
mv ScoDoc scodoc # important !
|
||||
|
||||
Il faut ensuite installer l'environnement et le fichier de configuration:
|
||||
|
||||
# Le plus simple est de piquer le virtualenv configuré par l'installeur:
|
||||
mv /opt/off-scodoc/venv /opt/scodoc
|
||||
|
||||
Et la config:
|
||||
|
||||
ln -s /opt/scodoc-data/.env /opt/scodoc
|
||||
|
||||
Cette dernière commande utilise le `.env` crée lors de l'install, ce qui
|
||||
n'est pas toujours le plus judicieux: vous pouvez modifier son contenu, par
|
||||
exemple pour travailler en mode "développement" avec `FLASK_ENV=development`.
|
||||
|
||||
### Tests unitaires
|
||||
|
||||
Certains tests ont besoin d'un département déjà créé, qui n'est pas créé par les
|
||||
scripts de tests:
|
||||
Lancer au préalable:
|
||||
|
||||
flask sco-delete-dept TEST00 && flask sco-create-dept TEST00
|
||||
|
||||
Puis dérouler les tests unitaires:
|
||||
|
||||
pytest tests/unit
|
||||
|
||||
Ou avec couverture (`pip install pytest-cov`)
|
||||
|
||||
pytest --cov=app --cov-report=term-missing --cov-branch tests/unit/*
|
||||
|
||||
|
||||
#### Utilisation des tests unitaires pour initialiser la base de dev
|
||||
On peut aussi utiliser les tests unitaires pour mettre la base
|
||||
de données de développement dans un état connu, par exemple pour éviter de recréer à la main étudianst et semestres quand on développe.
|
||||
|
||||
Il suffit de positionner une variable d'environnement indiquant la BD utilisée par les tests:
|
||||
|
||||
export SCODOC_TEST_DATABASE_URI=postgresql:///SCODOC_DEV
|
||||
|
||||
puis de les lancer normalement, par exemple:
|
||||
|
||||
pytest tests/unit/test_sco_basic.py
|
||||
|
||||
Il est en général nécessaire d'affecter ensuite un mot de passe à (au moins)
|
||||
un utilisateur:
|
||||
|
||||
flask user-password admin
|
||||
|
||||
**Attention:** les tests unitaires **effacent** complètement le contenu de la
|
||||
base de données (tous les départements, et les utilisateurs) avant de commencer !
|
||||
|
||||
#### Modification du schéma de la base
|
||||
|
||||
On utilise SQLAlchemy avec Alembic et Flask-Migrate.
|
||||
|
||||
flask db migrate -m "ScoDoc 9.0.x: ..." # ajuster le message !
|
||||
flask db upgrade
|
||||
|
||||
Ne pas oublier de commiter les migrations (`git add migrations` ...).
|
||||
|
||||
Mémo pour développeurs: séquence re-création d'une base:
|
||||
|
||||
dropdb SCODOC_DEV
|
||||
tools/create_database.sh SCODOC_DEV # créé base SQL
|
||||
flask db upgrade # créé les tables à partir des migrations
|
||||
flask sco-db-init # ajoute au besoin les constantes (fait en migration 0)
|
||||
|
||||
# puis imports:
|
||||
flask import-scodoc7-users
|
||||
flask import-scodoc7-dept STID SCOSTID
|
||||
|
||||
Si la base utilisée pour les dev n'est plus en phase avec les scripts de
|
||||
migration, utiliser les commandes `flask db history`et `flask db stamp`pour se
|
||||
positionner à la bonne étape.
|
||||
|
||||
# Paquet debian 11
|
||||
|
||||
Les scripts associés au paquet Debian (.deb) sont dans `tools/debian`. Le plus
|
||||
important est `postinst`qui se charge de configurer le système (install ou
|
||||
upgrade de scodoc9).
|
||||
|
||||
La préparation d'une release se fait à l'aide du script
|
||||
`tools/build_release.sh`.
|
||||
|
||||
|
||||
|
||||
# ScoDoc - Gestion de la scolarité - Version ScoDoc 9
|
||||
|
||||
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)\r
|
||||
|
||||
VERSION EXPERIMENTALE - NE PAS DEPLOYER - TESTS EN COURS
|
||||
|
||||
Installation: voir instructions à jour sur <https://scodoc.org/GuideInstallDebian11>
|
||||
|
||||
Documentation utilisateur: <https://scodoc.org>
|
||||
|
||||
## Version ScoDoc 9
|
||||
|
||||
N'utiliser que pour les développements et tests.
|
||||
|
||||
La version ScoDoc 9 est basée sur Flask (au lieu de Zope) et sur
|
||||
**python 3.9+**.
|
||||
|
||||
La version 9.0 s'efforce de reproduire presque à l'identique le fonctionnement
|
||||
de ScoDoc7, avec des composants logiciels différents (Debian 11, Python 3,
|
||||
Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
|
||||
|
||||
|
||||
|
||||
### État actuel (27 août 21)
|
||||
|
||||
- Tests en cours, notamment système d'installation et de migration.
|
||||
|
||||
**Fonctionnalités non intégrées:**
|
||||
|
||||
- feuille "placement" (en cours)
|
||||
|
||||
- ancien module "Entreprises" (obsolete)
|
||||
|
||||
|
||||
### Lignes de commandes
|
||||
|
||||
Voir [https://scodoc.org/GuideConfig](le guide de configuration).
|
||||
|
||||
|
||||
## Organisation des fichiers
|
||||
|
||||
L'installation comporte les fichiers de l'application, sous `/opt/scodoc/`, et
|
||||
les fichiers locaux (archives, photos, configurations, logs) sous
|
||||
`/opt/scodoc-data`. Par ailleurs, il y a évidemment les bases de données
|
||||
postgresql et la configuration du système Linux.
|
||||
|
||||
### Fichiers locaux
|
||||
Sous `/opt/scodoc-data`, fichiers et répertoires appartienant à l'utilisateur `scodoc`.
|
||||
Ils ne doivent pas être modifiés à la main, sauf certains fichiers de configuration sous
|
||||
`/opt/scodoc-data/config`.
|
||||
|
||||
Le répertoire `/opt/scodoc-data` doit être régulièrement sauvegardé.
|
||||
|
||||
Principaux contenus:
|
||||
|
||||
/opt/scodoc-data
|
||||
/opt/scodoc-data/log # Fichiers de log ScoDoc
|
||||
/opt/scodoc-data/config # Fichiers de configuration
|
||||
.../config/logos # Logos de l'établissement
|
||||
.../config/depts # un fichier par département
|
||||
/opt/scodoc-data/photos # Photos des étudiants
|
||||
/opt/scodoc-data/archives # Archives: PV de jury, maquettes Apogée, fichiers étudiants
|
||||
|
||||
## Pour les développeurs
|
||||
|
||||
### Installation du code
|
||||
|
||||
Installer ScoDoc 9 normalement ([voir la doc](https://scodoc.org/GuideInstallDebian11)).
|
||||
|
||||
Puis remplacer `/opt/scodoc` par un clone du git.
|
||||
|
||||
sudo su
|
||||
mv /opt/scodoc /opt/off-scodoc # ou ce que vous voulez
|
||||
apt-get install git # si besoin
|
||||
cd /opt
|
||||
git clone https://scodoc.org/git/viennet/ScoDoc.git
|
||||
# (ou bien utiliser votre clone gitea si vous l'avez déjà créé !)
|
||||
mv ScoDoc scodoc # important !
|
||||
|
||||
Il faut ensuite installer l'environnement et le fichier de configuration:
|
||||
|
||||
# Le plus simple est de piquer le virtualenv configuré par l'installeur:
|
||||
mv /opt/off-scodoc/venv /opt/scodoc
|
||||
|
||||
Et la config:
|
||||
|
||||
ln -s /opt/scodoc-data/.env /opt/scodoc
|
||||
|
||||
Cette dernière commande utilise le `.env` crée lors de l'install, ce qui
|
||||
n'est pas toujours le plus judicieux: vous pouvez modifier son contenu, par
|
||||
exemple pour travailler en mode "développement" avec `FLASK_ENV=development`.
|
||||
|
||||
### Tests unitaires
|
||||
|
||||
Certains tests ont besoin d'un département déjà créé, qui n'est pas créé par les
|
||||
scripts de tests:
|
||||
Lancer au préalable:
|
||||
|
||||
flask sco-delete-dept TEST00 && flask sco-create-dept TEST00
|
||||
|
||||
Puis dérouler les tests unitaires:
|
||||
|
||||
pytest tests/unit
|
||||
|
||||
Ou avec couverture (`pip install pytest-cov`)
|
||||
|
||||
pytest --cov=app --cov-report=term-missing --cov-branch tests/unit/*
|
||||
|
||||
|
||||
#### Utilisation des tests unitaires pour initialiser la base de dev
|
||||
On peut aussi utiliser les tests unitaires pour mettre la base
|
||||
de données de développement dans un état connu, par exemple pour éviter de recréer à la main étudianst et semestres quand on développe.
|
||||
|
||||
Il suffit de positionner une variable d'environnement indiquant la BD utilisée par les tests:
|
||||
|
||||
export SCODOC_TEST_DATABASE_URI=postgresql:///SCODOC_DEV
|
||||
|
||||
puis de les lancer normalement, par exemple:
|
||||
|
||||
pytest tests/unit/test_sco_basic.py
|
||||
|
||||
Il est en général nécessaire d'affecter ensuite un mot de passe à (au moins)
|
||||
un utilisateur:
|
||||
|
||||
flask user-password admin
|
||||
|
||||
**Attention:** les tests unitaires **effacent** complètement le contenu de la
|
||||
base de données (tous les départements, et les utilisateurs) avant de commencer !
|
||||
|
||||
#### Modification du schéma de la base
|
||||
|
||||
On utilise SQLAlchemy avec Alembic et Flask-Migrate.
|
||||
|
||||
flask db migrate -m "ScoDoc 9.0.x: ..." # ajuster le message !
|
||||
flask db upgrade
|
||||
|
||||
Ne pas oublier de commiter les migrations (`git add migrations` ...).
|
||||
|
||||
Mémo pour développeurs: séquence re-création d'une base:
|
||||
|
||||
dropdb SCODOC_DEV
|
||||
tools/create_database.sh SCODOC_DEV # créé base SQL
|
||||
flask db upgrade # créé les tables à partir des migrations
|
||||
flask sco-db-init # ajoute au besoin les constantes (fait en migration 0)
|
||||
|
||||
# puis imports:
|
||||
flask import-scodoc7-users
|
||||
flask import-scodoc7-dept STID SCOSTID
|
||||
|
||||
Si la base utilisée pour les dev n'est plus en phase avec les scripts de
|
||||
migration, utiliser les commandes `flask db history`et `flask db stamp`pour se
|
||||
positionner à la bonne étape.
|
||||
|
||||
# Paquet debian 11
|
||||
|
||||
Les scripts associés au paquet Debian (.deb) sont dans `tools/debian`. Le plus
|
||||
important est `postinst`qui se charge de configurer le système (install ou
|
||||
upgrade de scodoc9).
|
||||
|
||||
La préparation d'une release se fait à l'aide du script
|
||||
`tools/build_release.sh`.
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# pylint: disable=invalid-name
|
||||
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
|
@ -17,14 +18,14 @@ from flask import render_template
|
|||
from flask.logging import default_handler
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager
|
||||
from flask_login import LoginManager, current_user
|
||||
from flask_mail import Mail
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask_moment import Moment
|
||||
from flask_caching import Cache
|
||||
import sqlalchemy
|
||||
|
||||
from app.scodoc.sco_exceptions import ScoValueError, APIInvalidParams
|
||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError, APIInvalidParams
|
||||
from config import DevConfig
|
||||
import sco_version
|
||||
|
||||
|
@ -82,7 +83,7 @@ def postgresql_server_error(e):
|
|||
return render_raw_html("error_503.html", SCOVERSION=sco_version.SCOVERSION), 503
|
||||
|
||||
|
||||
class RequestFormatter(logging.Formatter):
|
||||
class LogRequestFormatter(logging.Formatter):
|
||||
"""Ajoute URL et remote_addr for logging"""
|
||||
|
||||
def format(self, record):
|
||||
|
@ -92,6 +93,33 @@ class RequestFormatter(logging.Formatter):
|
|||
else:
|
||||
record.url = None
|
||||
record.remote_addr = None
|
||||
record.sco_user = current_user
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
class LogExceptionFormatter(logging.Formatter):
|
||||
"""Formatteur pour les exceptions: ajoute détails"""
|
||||
|
||||
def format(self, record):
|
||||
if has_request_context():
|
||||
record.url = request.url
|
||||
record.remote_addr = request.environ.get(
|
||||
"HTTP_X_FORWARDED_FOR", request.remote_addr
|
||||
)
|
||||
record.http_referrer = request.referrer
|
||||
record.http_method = request.method
|
||||
if request.method == "GET":
|
||||
record.http_params = str(request.args)
|
||||
else:
|
||||
record.http_params = "(post data not loggued)"
|
||||
else:
|
||||
record.url = None
|
||||
record.remote_addr = None
|
||||
record.http_referrer = None
|
||||
record.http_method = None
|
||||
record.http_params = None
|
||||
record.sco_user = current_user
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
@ -105,8 +133,24 @@ class ScoSMTPHandler(SMTPHandler):
|
|||
return subject
|
||||
|
||||
|
||||
class ReverseProxied(object):
|
||||
"""Adaptateur wsgi qui nous permet d'avoir toutes les URL calculées en https
|
||||
sauf quand on est en dev.
|
||||
La variable HTTP_X_FORWARDED_PROTO est positionnée par notre config nginx"""
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
scheme = environ.get("HTTP_X_FORWARDED_PROTO")
|
||||
if scheme:
|
||||
environ["wsgi.url_scheme"] = scheme # ou forcer à https ici ?
|
||||
return self.app(environ, start_response)
|
||||
|
||||
|
||||
def create_app(config_class=DevConfig):
|
||||
app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static")
|
||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||
app.logger.setLevel(logging.DEBUG)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
|
@ -119,6 +163,7 @@ def create_app(config_class=DevConfig):
|
|||
cache.init_app(app)
|
||||
sco_cache.CACHE = cache
|
||||
|
||||
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
||||
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
||||
app.register_error_handler(500, internal_server_error)
|
||||
app.register_error_handler(503, postgresql_server_error)
|
||||
|
@ -148,9 +193,16 @@ def create_app(config_class=DevConfig):
|
|||
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
|
||||
)
|
||||
app.register_blueprint(api_bp, url_prefix="/ScoDoc/api")
|
||||
scodoc_exc_formatter = RequestFormatter(
|
||||
"[%(asctime)s] %(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s in %(module)s: %(message)s"
|
||||
scodoc_log_formatter = LogRequestFormatter(
|
||||
"[%(asctime)s] %(sco_user)s@%(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s: %(message)s"
|
||||
)
|
||||
scodoc_exc_formatter = LogExceptionFormatter(
|
||||
"[%(asctime)s] %(sco_user)s@%(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s: %(message)s\n"
|
||||
"Referrer: %(http_referrer)s\n"
|
||||
"Method: %(http_method)s\n"
|
||||
"Params: %(http_params)s\n"
|
||||
)
|
||||
if not app.testing:
|
||||
if not app.debug:
|
||||
|
@ -179,7 +231,7 @@ def create_app(config_class=DevConfig):
|
|||
app.logger.addHandler(mail_handler)
|
||||
else:
|
||||
# Pour logs en DEV uniquement:
|
||||
default_handler.setFormatter(scodoc_exc_formatter)
|
||||
default_handler.setFormatter(scodoc_log_formatter)
|
||||
|
||||
# Config logs pour DEV et PRODUCTION
|
||||
# Configuration des logs (actifs aussi en mode development)
|
||||
|
@ -188,9 +240,17 @@ def create_app(config_class=DevConfig):
|
|||
file_handler = WatchedFileHandler(
|
||||
app.config["SCODOC_LOG_FILE"], encoding="utf-8"
|
||||
)
|
||||
file_handler.setFormatter(scodoc_exc_formatter)
|
||||
file_handler.setFormatter(scodoc_log_formatter)
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
# Log pour les erreurs (exceptions) uniquement:
|
||||
# usually /opt/scodoc-data/log/scodoc_exc.log
|
||||
file_handler = WatchedFileHandler(
|
||||
app.config["SCODOC_ERR_FILE"], encoding="utf-8"
|
||||
)
|
||||
file_handler.setFormatter(scodoc_exc_formatter)
|
||||
file_handler.setLevel(logging.ERROR)
|
||||
app.logger.addHandler(file_handler)
|
||||
|
||||
# app.logger.setLevel(logging.INFO)
|
||||
app.logger.info(f"{sco_version.SCONAME} {sco_version.SCOVERSION} startup")
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.from flask import jsonify
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from flask import jsonify
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
|
||||
|
|
|
@ -43,18 +43,20 @@ class ZRequest(object):
|
|||
"Emulating Zope 2 REQUEST"
|
||||
|
||||
def __init__(self):
|
||||
if current_app.config["DEBUG"]:
|
||||
self.URL = request.base_url
|
||||
self.BASE0 = request.url_root
|
||||
else:
|
||||
self.URL = request.base_url.replace("http://", "https://")
|
||||
self.BASE0 = request.url_root.replace("http://", "https://")
|
||||
self.URL0 = self.URL
|
||||
# if current_app.config["DEBUG"]:
|
||||
|
||||
# le ReverseProxied se charge maintenant de mettre le bon protocole http ou https
|
||||
# self.URL = request.base_url
|
||||
# self.BASE0 = request.url_root
|
||||
# else:
|
||||
# self.URL = request.base_url.replace("http://", "https://")
|
||||
# self.BASE0 = request.url_root.replace("http://", "https://")
|
||||
# self.URL0 = self.URL
|
||||
# query_string is bytes:
|
||||
self.QUERY_STRING = request.query_string.decode("utf-8")
|
||||
self.REQUEST_METHOD = request.method
|
||||
self.AUTHENTICATED_USER = current_user
|
||||
self.REMOTE_ADDR = request.remote_addr
|
||||
# self.QUERY_STRING = request.query_string.decode("utf-8")
|
||||
# self.REQUEST_METHOD = request.method
|
||||
# self.AUTHENTICATED_USER = current_user
|
||||
# self.REMOTE_ADDR = request.remote_addr
|
||||
if request.method == "POST":
|
||||
# request.form is a werkzeug.datastructures.ImmutableMultiDict
|
||||
# must copy to get a mutable version (needed by TrivialFormulator)
|
||||
|
@ -72,16 +74,13 @@ class ZRequest(object):
|
|||
if k.endswith(":list"):
|
||||
self.form[k[:-5]] = request.args.getlist(k)
|
||||
else:
|
||||
self.form[k] = request.args[k]
|
||||
values = request.args.getlist(k)
|
||||
self.form[k] = values[0] if len(values) == 1 else values
|
||||
# current_app.logger.info("ZRequest.form=%s" % str(self.form))
|
||||
self.RESPONSE = ZResponse()
|
||||
|
||||
def __str__(self):
|
||||
return """REQUEST
|
||||
URL={r.URL}
|
||||
QUERY_STRING={r.QUERY_STRING}
|
||||
REQUEST_METHOD={r.REQUEST_METHOD}
|
||||
AUTHENTICATED_USER={r.AUTHENTICATED_USER}
|
||||
return """ZREQUEST
|
||||
form={r.form}
|
||||
""".format(
|
||||
r=self
|
||||
|
@ -231,6 +230,7 @@ def scodoc7func(func):
|
|||
if arg_name == "REQUEST": # special case
|
||||
pos_arg_values.append(REQUEST)
|
||||
else:
|
||||
# peut produire une KeyError s'il manque un argument attendu:
|
||||
v = req_args[arg_name]
|
||||
# try to convert all arguments to INTEGERS
|
||||
# necessary for db ids and boolean values
|
||||
|
|
|
@ -41,6 +41,7 @@ class Identite(db.Model):
|
|||
code_nip = db.Column(db.Text())
|
||||
code_ine = db.Column(db.Text())
|
||||
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||
# ne pas utiliser après migrate_scodoc7_dept_archive
|
||||
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class NotesFormation(db.Model):
|
|||
"""Programme pédagogique d'une formation"""
|
||||
|
||||
__tablename__ = "notes_formations"
|
||||
__table_args__ = (db.UniqueConstraint("acronyme", "titre", "version"),)
|
||||
__table_args__ = (db.UniqueConstraint("dept_id", "acronyme", "titre", "version"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
formation_id = db.synonym("id")
|
||||
|
|
|
@ -41,6 +41,10 @@ class FormSemestre(db.Model):
|
|||
bul_hide_xml = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# Bloque le calcul des moyennes (générale et d'UE)
|
||||
block_moyennes = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# semestres decales (pour gestion jurys):
|
||||
gestion_semestrielle = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
|
@ -70,6 +74,7 @@ class FormSemestre(db.Model):
|
|||
"NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre"
|
||||
)
|
||||
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||
# ne pas utiliser après migrate_scodoc7_dept_archive
|
||||
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
v 1.3 (python3)
|
||||
"""
|
||||
import html
|
||||
|
||||
|
||||
def TrivialFormulator(
|
||||
|
@ -722,7 +723,9 @@ var {field}_as = new bsn.AutoSuggest('{field}', {field}_opts);
|
|||
if str(descr["allowed_values"][i]) == str(self.values[field]):
|
||||
R.append('<span class="tf-ro-value">%s</span>' % labels[i])
|
||||
elif input_type == "textarea":
|
||||
R.append('<div class="tf-ro-textarea">%s</div>' % self.values[field])
|
||||
R.append(
|
||||
'<div class="tf-ro-textarea">%s</div>' % html.escape(self.values[field])
|
||||
)
|
||||
elif input_type == "separator" or input_type == "hidden":
|
||||
pass
|
||||
elif input_type == "file":
|
||||
|
|
|
@ -379,6 +379,25 @@ def bonus_iutbethune(notes_sport, coefs, infos=None):
|
|||
return bonus
|
||||
|
||||
|
||||
def bonus_iutbeziers(notes_sport, coefs, infos=None):
|
||||
"""Calcul bonus modules optionels (sport, culture), regle IUT BEZIERS
|
||||
|
||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
||||
sport , etc) non rattaches à une unité d'enseignement. Les points
|
||||
au-dessus de 10 sur 20 obtenus dans chacune des matières
|
||||
optionnelles sont cumulés et 3% de ces points cumulés s'ajoutent à
|
||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
||||
"""
|
||||
sumc = sum(coefs) # assumes sum. coefs > 0
|
||||
# note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondérée
|
||||
bonus = sum([(x - 10) * 0.03 for x in notes_sport if x > 10])
|
||||
# le total du bonus ne doit pas dépasser 0.3 - Fred, 28/01/2020
|
||||
|
||||
if bonus > 0.3:
|
||||
bonus = 0.3
|
||||
return bonus
|
||||
|
||||
|
||||
def bonus_demo(notes_sport, coefs, infos=None):
|
||||
"""Fausse fonction "bonus" pour afficher les informations disponibles
|
||||
et aider les développeurs.
|
||||
|
@ -386,8 +405,8 @@ def bonus_demo(notes_sport, coefs, infos=None):
|
|||
qui est ECRASE à chaque appel.
|
||||
*** Ne pas utiliser en production !!! ***
|
||||
"""
|
||||
f = open("/tmp/scodoc_bonus.log", "w") # mettre 'a' pour ajouter en fin
|
||||
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
|
||||
with open("/tmp/scodoc_bonus.log", "w") as f: # mettre 'a' pour ajouter en fin
|
||||
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
|
||||
# Statut de chaque UE
|
||||
# for ue_id in infos['moy_ues']:
|
||||
# ue_status = infos['moy_ues'][ue_id]
|
||||
|
|
|
@ -185,6 +185,9 @@ class GenTable(object):
|
|||
else:
|
||||
self.preferences = DEFAULT_TABLE_PREFERENCES()
|
||||
|
||||
def __repr__(self):
|
||||
return f"<gen_table( nrows={self.get_nb_rows()}, ncols={self.get_nb_cols()} )>"
|
||||
|
||||
def get_nb_cols(self):
|
||||
return len(self.columns_ids)
|
||||
|
||||
|
@ -482,9 +485,9 @@ class GenTable(object):
|
|||
ses.append_blank_row() # empty line
|
||||
ses.append_single_cell_row(self.origin, style_base)
|
||||
if wb is None:
|
||||
return ses.generate_standalone()
|
||||
return ses.generate()
|
||||
else:
|
||||
ses.generate_embeded()
|
||||
ses.generate()
|
||||
|
||||
def text(self):
|
||||
"raw text representation of the table"
|
||||
|
@ -573,7 +576,7 @@ class GenTable(object):
|
|||
"""
|
||||
doc = ElementTree.Element(
|
||||
self.xml_outer_tag,
|
||||
id=self.table_id,
|
||||
id=str(self.table_id),
|
||||
origin=self.origin or "",
|
||||
caption=self.caption or "",
|
||||
)
|
||||
|
@ -587,7 +590,7 @@ class GenTable(object):
|
|||
v = row.get(cid, "")
|
||||
if v is None:
|
||||
v = ""
|
||||
x_cell = ElementTree.Element(cid, value=str(v))
|
||||
x_cell = ElementTree.Element(str(cid), value=str(v))
|
||||
x_row.append(x_cell)
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
|
||||
|
@ -610,7 +613,6 @@ class GenTable(object):
|
|||
format="html",
|
||||
page_title="",
|
||||
filename=None,
|
||||
REQUEST=None,
|
||||
javascripts=[],
|
||||
with_html_headers=True,
|
||||
publish=True,
|
||||
|
@ -643,35 +645,53 @@ class GenTable(object):
|
|||
H.append(html_sco_header.sco_footer())
|
||||
return "\n".join(H)
|
||||
elif format == "pdf":
|
||||
objects = self.pdf()
|
||||
doc = sco_pdf.pdf_basic_page(
|
||||
objects, title=title, preferences=self.preferences
|
||||
pdf_objs = self.pdf()
|
||||
pdf_doc = sco_pdf.pdf_basic_page(
|
||||
pdf_objs, title=title, preferences=self.preferences
|
||||
)
|
||||
if publish:
|
||||
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
|
||||
return scu.send_file(
|
||||
pdf_doc,
|
||||
filename,
|
||||
suffix=".pdf",
|
||||
mime=scu.PDF_MIMETYPE,
|
||||
)
|
||||
else:
|
||||
return doc
|
||||
elif format == "xls" or format == "xlsx":
|
||||
return pdf_doc
|
||||
elif format == "xls" or format == "xlsx": # dans les 2 cas retourne du xlsx
|
||||
xls = self.excel()
|
||||
if publish:
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, xls, filename + scu.XLSX_SUFFIX
|
||||
return scu.send_file(
|
||||
xls,
|
||||
filename,
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
else:
|
||||
return xls
|
||||
elif format == "text":
|
||||
return self.text()
|
||||
elif format == "csv":
|
||||
return scu.sendCSVFile(REQUEST, self.text(), filename + ".csv")
|
||||
return scu.send_file(
|
||||
self.text(),
|
||||
filename,
|
||||
suffix=".csv",
|
||||
mime=scu.CSV_MIMETYPE,
|
||||
attached=True,
|
||||
)
|
||||
elif format == "xml":
|
||||
xml = self.xml()
|
||||
if REQUEST and publish:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
if publish:
|
||||
return scu.send_file(
|
||||
xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE
|
||||
)
|
||||
return xml
|
||||
elif format == "json":
|
||||
js = self.json()
|
||||
if REQUEST and publish:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
|
||||
if publish:
|
||||
return scu.send_file(
|
||||
js, filename, suffix=".json", mime=scu.JSON_MIMETYPE
|
||||
)
|
||||
return js
|
||||
else:
|
||||
log("make_page: format=%s" % format)
|
||||
|
@ -732,5 +752,5 @@ if __name__ == "__main__":
|
|||
document.build(objects)
|
||||
data = doc.getvalue()
|
||||
open("/tmp/gen_table.pdf", "wb").write(data)
|
||||
p = T.make_page(format="pdf", REQUEST=None)
|
||||
p = T.make_page(format="pdf")
|
||||
open("toto.pdf", "wb").write(p)
|
||||
|
|
|
@ -87,10 +87,6 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
|
|||
)
|
||||
|
||||
|
||||
_TOP_LEVEL_CSS = """
|
||||
<style type="text/css">
|
||||
</style>"""
|
||||
|
||||
_HTML_BEGIN = """<?xml version="1.0" encoding="%(encoding)s"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
@ -105,31 +101,30 @@ _HTML_BEGIN = """<?xml version="1.0" encoding="%(encoding)s"?>
|
|||
|
||||
<link href="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/ScoDoc/static/css/menu.css" rel="stylesheet" type="text/css" />
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/sorttable.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script type="text/javascript">
|
||||
<script src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/sorttable.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script>
|
||||
window.onload=function(){enableTooltips("gtrcontent")};
|
||||
</script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery.field.min.js"></script>
|
||||
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
<script src="/ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/jquery.field.min.js"></script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css" />
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/scodoc.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/etud_info.js"></script>
|
||||
<script src="/ScoDoc/static/js/scodoc.js"></script>
|
||||
<script src="/ScoDoc/static/js/etud_info.js"></script>
|
||||
"""
|
||||
|
||||
|
||||
def scodoc_top_html_header(page_title="ScoDoc: bienvenue"):
|
||||
H = [
|
||||
_HTML_BEGIN % {"page_title": page_title, "encoding": scu.SCO_ENCODING},
|
||||
_TOP_LEVEL_CSS,
|
||||
"""</head><body class="gtrcontent" id="gtrcontent">""",
|
||||
scu.CUSTOM_HTML_HEADER_CNX,
|
||||
]
|
||||
|
@ -185,13 +180,10 @@ def sco_header(
|
|||
init_jquery = True
|
||||
|
||||
H = [
|
||||
"""<?xml version="1.0" encoding="%(encoding)s"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
"""<!DOCTYPE html><html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>%(page_title)s</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=%(encoding)s" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="LANG" content="fr" />
|
||||
<meta name="DESCRIPTION" content="ScoDoc" />
|
||||
|
||||
|
@ -206,9 +198,7 @@ def sco_header(
|
|||
)
|
||||
if init_google_maps:
|
||||
# It may be necessary to add an API key:
|
||||
H.append(
|
||||
'<script type="text/javascript" src="https://maps.google.com/maps/api/js"></script>'
|
||||
)
|
||||
H.append('<script src="https://maps.google.com/maps/api/js"></script>')
|
||||
|
||||
# Feuilles de style additionnelles:
|
||||
for cssstyle in cssstyles:
|
||||
|
@ -223,9 +213,9 @@ def sco_header(
|
|||
<link href="/ScoDoc/static/css/menu.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/ScoDoc/static/css/gt_table.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script type="text/javascript">
|
||||
<script src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script>
|
||||
window.onload=function(){enableTooltips("gtrcontent")};
|
||||
|
||||
var SCO_URL="%(ScoURL)s";
|
||||
|
@ -236,16 +226,14 @@ def sco_header(
|
|||
# jQuery
|
||||
if init_jquery:
|
||||
H.append(
|
||||
"""<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
"""<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
"""
|
||||
)
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery.field.min.js"></script>'
|
||||
)
|
||||
H.append('<script src="/ScoDoc/static/libjs/jquery.field.min.js"></script>')
|
||||
# qTip
|
||||
if init_qtip:
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>'
|
||||
)
|
||||
H.append(
|
||||
'<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css" />'
|
||||
|
@ -253,32 +241,25 @@ def sco_header(
|
|||
|
||||
if init_jquery_ui:
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>'
|
||||
)
|
||||
# H.append('<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui/js/jquery-ui-i18n.js"></script>')
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/scodoc.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>'
|
||||
)
|
||||
# H.append('<script src="/ScoDoc/static/libjs/jquery-ui/js/jquery-ui-i18n.js"></script>')
|
||||
H.append('<script src="/ScoDoc/static/js/scodoc.js"></script>')
|
||||
if init_google_maps:
|
||||
H.append(
|
||||
'<script type="text/javascript" src="/ScoDoc/static/libjs/jquery.ui.map.full.min.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/jquery.ui.map.full.min.js"></script>'
|
||||
)
|
||||
if init_datatables:
|
||||
H.append(
|
||||
'<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css"/>'
|
||||
)
|
||||
H.append(
|
||||
'<script type="text/javascript" src="/ScoDoc/static/DataTables/datatables.min.js"></script>'
|
||||
)
|
||||
H.append('<script src="/ScoDoc/static/DataTables/datatables.min.js"></script>')
|
||||
# JS additionels
|
||||
for js in javascripts:
|
||||
H.append(
|
||||
"""<script language="javascript" type="text/javascript" src="/ScoDoc/static/%s"></script>\n"""
|
||||
% js
|
||||
)
|
||||
H.append("""<script src="/ScoDoc/static/%s"></script>\n""" % js)
|
||||
|
||||
H.append(
|
||||
"""<style type="text/css">
|
||||
"""<style>
|
||||
.gtrcontent {
|
||||
margin-left: %(margin_left)s;
|
||||
height: 100%%;
|
||||
|
@ -290,7 +271,7 @@ def sco_header(
|
|||
)
|
||||
# Scripts de la page:
|
||||
if scripts:
|
||||
H.append("""<script language="javascript" type="text/javascript">""")
|
||||
H.append("""<script>""")
|
||||
for script in scripts:
|
||||
H.append(script)
|
||||
H.append("""</script>""")
|
||||
|
@ -337,13 +318,7 @@ def sco_footer():
|
|||
|
||||
|
||||
def html_sem_header(
|
||||
REQUEST,
|
||||
title,
|
||||
sem=None,
|
||||
with_page_header=True,
|
||||
with_h2=True,
|
||||
page_title=None,
|
||||
**args
|
||||
title, sem=None, with_page_header=True, with_h2=True, page_title=None, **args
|
||||
):
|
||||
"Titre d'une page semestre avec lien vers tableau de bord"
|
||||
# sem now unused and thus optional...
|
||||
|
|
|
@ -28,9 +28,8 @@
|
|||
"""
|
||||
Génération de la "sidebar" (marge gauche des pages HTML)
|
||||
"""
|
||||
from flask import url_for
|
||||
from flask import g
|
||||
from flask import request
|
||||
from flask import render_template, url_for
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -155,8 +154,9 @@ def sidebar():
|
|||
<div class="sidebar-bottom"><a href="{ url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }" class="sidebar">À propos</a><br/>
|
||||
<a href="{ scu.SCO_USER_MANUAL }" target="_blank" class="sidebar">Aide</a>
|
||||
</div></div>
|
||||
<div class="logo-logo"><a href= { url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }
|
||||
">{ scu.icontag("scologo_img", no_size=True) }</a>
|
||||
<div class="logo-logo">
|
||||
<a href="{ url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }">
|
||||
{ scu.icontag("scologo_img", no_size=True) }</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of sidebar -->
|
||||
|
@ -167,19 +167,7 @@ def sidebar():
|
|||
|
||||
def sidebar_dept():
|
||||
"""Partie supérieure de la marge de gauche"""
|
||||
H = [
|
||||
f"""<h2 class="insidebar">Dépt. {sco_preferences.get_preference("DeptName")}</h2>
|
||||
<a href="{url_for("scodoc.index")}" class="sidebar">Accueil</a> <br/> """
|
||||
]
|
||||
dept_intranet_url = sco_preferences.get_preference("DeptIntranetURL")
|
||||
if dept_intranet_url:
|
||||
H.append(
|
||||
f"""<a href="{dept_intranet_url}" class="sidebar">{
|
||||
sco_preferences.get_preference("DeptIntranetTitle")}</a> <br/>"""
|
||||
)
|
||||
# Entreprises pas encore supporté en ScoDoc8
|
||||
# H.append(
|
||||
# """<br/><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br/>"""
|
||||
# % infos
|
||||
# )
|
||||
return "\n".join(H)
|
||||
return render_template(
|
||||
"sidebar_dept.html",
|
||||
prefs=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
|
|
@ -186,6 +186,8 @@ class NotesTable(object):
|
|||
self.use_ue_coefs = sco_preferences.get_preference(
|
||||
"use_ue_coefs", formsemestre_id
|
||||
)
|
||||
# si vrai, bloque calcul des moy gen. et d'UE.:
|
||||
self.block_moyennes = self.sem["block_moyennes"]
|
||||
# Infos sur les etudiants
|
||||
self.inscrlist = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||
args={"formsemestre_id": formsemestre_id}
|
||||
|
@ -738,6 +740,7 @@ class NotesTable(object):
|
|||
block_computation = (
|
||||
self.inscrdict[etudid]["etat"] == "D"
|
||||
or self.inscrdict[etudid]["etat"] == DEF
|
||||
or self.block_moyennes
|
||||
)
|
||||
|
||||
moy_ues = {}
|
||||
|
|
|
@ -626,7 +626,6 @@ def add_absence(
|
|||
jour,
|
||||
matin,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=None,
|
||||
moduleimpl_id=None,
|
||||
):
|
||||
|
@ -656,7 +655,7 @@ def add_absence(
|
|||
sco_abs_notification.abs_notify(etudid, jour)
|
||||
|
||||
|
||||
def add_justif(etudid, jour, matin, REQUEST, description=None):
|
||||
def add_justif(etudid, jour, matin, description=None):
|
||||
"Ajoute un justificatif dans la base"
|
||||
# unpublished
|
||||
if _isFarFutur(jour):
|
||||
|
@ -665,7 +664,9 @@ def add_justif(etudid, jour, matin, REQUEST, description=None):
|
|||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
cursor.execute(
|
||||
"insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )",
|
||||
"""INSERT INTO absences (etudid, jour, estabs, estjust, matin, description)
|
||||
VALUES (%(etudid)s, %(jour)s, FALSE, TRUE, %(matin)s, %(description)s)
|
||||
""",
|
||||
vars(),
|
||||
)
|
||||
logdb(
|
||||
|
@ -678,7 +679,7 @@ def add_justif(etudid, jour, matin, REQUEST, description=None):
|
|||
invalidate_abs_etud_date(etudid, jour)
|
||||
|
||||
|
||||
def _add_abslist(abslist, REQUEST, moduleimpl_id=None):
|
||||
def add_abslist(abslist, moduleimpl_id=None):
|
||||
for a in abslist:
|
||||
etudid, jour, ampm = a.split(":")
|
||||
if ampm == "am":
|
||||
|
@ -689,7 +690,7 @@ def _add_abslist(abslist, REQUEST, moduleimpl_id=None):
|
|||
raise ValueError("invalid ampm !")
|
||||
# ajoute abs si pas deja absent
|
||||
if count_abs(etudid, jour, jour, matin, moduleimpl_id) == 0:
|
||||
add_absence(etudid, jour, matin, 0, REQUEST, "", moduleimpl_id)
|
||||
add_absence(etudid, jour, matin, 0, "", moduleimpl_id)
|
||||
|
||||
|
||||
def annule_absence(etudid, jour, matin, moduleimpl_id=None):
|
||||
|
@ -721,7 +722,7 @@ def annule_absence(etudid, jour, matin, moduleimpl_id=None):
|
|||
invalidate_abs_etud_date(etudid, jour)
|
||||
|
||||
|
||||
def annule_justif(etudid, jour, matin, REQUEST=None):
|
||||
def annule_justif(etudid, jour, matin):
|
||||
"Annule un justificatif"
|
||||
# unpublished
|
||||
matin = _toboolean(matin)
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
"""
|
||||
import datetime
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
|
@ -58,7 +58,6 @@ def doSignaleAbsence(
|
|||
estjust=False,
|
||||
description=None,
|
||||
etudid=False,
|
||||
REQUEST=None,
|
||||
): # etudid implied
|
||||
"""Signalement d'une absence.
|
||||
|
||||
|
@ -69,7 +68,8 @@ def doSignaleAbsence(
|
|||
demijournee: 2 si journée complète, 1 matin, 0 après-midi
|
||||
estjust: absence justifiée
|
||||
description: str
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans REQUEST.form
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans
|
||||
les paramètres de la requête courante.
|
||||
"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
|
@ -86,7 +86,6 @@ def doSignaleAbsence(
|
|||
jour,
|
||||
False,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
|
@ -95,7 +94,6 @@ def doSignaleAbsence(
|
|||
jour,
|
||||
True,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
|
@ -106,7 +104,6 @@ def doSignaleAbsence(
|
|||
jour,
|
||||
demijournee,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
|
@ -156,7 +153,7 @@ def doSignaleAbsence(
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def SignaleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def SignaleAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple de signalement d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
|
@ -228,7 +225,6 @@ def SignaleAbsenceEtud(REQUEST=None): # etudid implied
|
|||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""
|
||||
|
@ -281,7 +277,6 @@ def doJustifAbsence(
|
|||
demijournee,
|
||||
description=None,
|
||||
etudid=False,
|
||||
REQUEST=None,
|
||||
): # etudid implied
|
||||
"""Justification d'une absence
|
||||
|
||||
|
@ -291,7 +286,8 @@ def doJustifAbsence(
|
|||
demijournee: 2 si journée complète, 1 matin, 0 après-midi
|
||||
estjust: absence justifiée
|
||||
description: str
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans REQUEST.form
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans les
|
||||
paramètres de la requête.
|
||||
"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
|
@ -305,14 +301,12 @@ def doJustifAbsence(
|
|||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=False,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
sco_abs.add_justif(
|
||||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=True,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
nbadded += 2
|
||||
|
@ -321,7 +315,6 @@ def doJustifAbsence(
|
|||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=demijournee,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
nbadded += 1
|
||||
|
@ -357,7 +350,7 @@ def doJustifAbsence(
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def JustifAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def JustifAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple de justification d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
|
@ -376,7 +369,6 @@ def JustifAbsenceEtud(REQUEST=None): # etudid implied
|
|||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""
|
||||
|
@ -412,9 +404,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def doAnnuleAbsence(
|
||||
datedebut, datefin, demijournee, etudid=False, REQUEST=None
|
||||
): # etudid implied
|
||||
def doAnnuleAbsence(datedebut, datefin, demijournee, etudid=False): # etudid implied
|
||||
"""Annulation des absences pour une demi journée"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
|
@ -462,7 +452,7 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def AnnuleAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple d'annulation d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
|
@ -482,7 +472,6 @@ def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
|||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p>
|
||||
|
@ -548,7 +537,7 @@ def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def doAnnuleJustif(datedebut0, datefin0, demijournee, REQUEST=None): # etudid implied
|
||||
def doAnnuleJustif(datedebut0, datefin0, demijournee): # etudid implied
|
||||
"""Annulation d'une justification"""
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
etudid = etud["etudid"]
|
||||
|
@ -558,11 +547,11 @@ def doAnnuleJustif(datedebut0, datefin0, demijournee, REQUEST=None): # etudid i
|
|||
for jour in dates:
|
||||
# Attention: supprime matin et après-midi
|
||||
if demijournee == 2:
|
||||
sco_abs.annule_justif(etudid, jour, False, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, True, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, False)
|
||||
sco_abs.annule_justif(etudid, jour, True)
|
||||
nbadded += 2
|
||||
else:
|
||||
sco_abs.annule_justif(etudid, jour, demijournee, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, demijournee)
|
||||
nbadded += 1
|
||||
#
|
||||
H = [
|
||||
|
@ -716,7 +705,6 @@ def formChoixSemestreGroupe(all=False):
|
|||
def CalAbs(etudid, sco_year=None):
|
||||
"""Calendrier des absences d'un etudiant"""
|
||||
# crude portage from 1999 DTML
|
||||
REQUEST = None # XXX
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
anneescolaire = int(scu.AnneeScolaire(sco_year))
|
||||
|
@ -766,7 +754,6 @@ def CalAbs(etudid, sco_year=None):
|
|||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
),
|
||||
CalHTML,
|
||||
|
@ -791,7 +778,6 @@ def ListeAbsEtud(
|
|||
format="html",
|
||||
absjust_only=0,
|
||||
sco_year=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Liste des absences d'un étudiant sur l'année en cours
|
||||
En format 'html': page avec deux tableaux (non justifiées et justifiées).
|
||||
|
@ -810,12 +796,12 @@ def ListeAbsEtud(
|
|||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
|
||||
# Liste des absences et titres colonnes tables:
|
||||
titles, columns_ids, absnonjust, absjust = _TablesAbsEtud(
|
||||
titles, columns_ids, absnonjust, absjust = _tables_abs_etud(
|
||||
etudid, datedebut, with_evals=with_evals, format=format
|
||||
)
|
||||
if REQUEST:
|
||||
base_url_nj = "%s?etudid=%s&absjust_only=0" % (REQUEST.URL0, etudid)
|
||||
base_url_j = "%s?etudid=%s&absjust_only=1" % (REQUEST.URL0, etudid)
|
||||
if request.base_url:
|
||||
base_url_nj = "%s?etudid=%s&absjust_only=0" % (request.base_url, etudid)
|
||||
base_url_j = "%s?etudid=%s&absjust_only=1" % (request.base_url, etudid)
|
||||
else:
|
||||
base_url_nj = base_url_j = ""
|
||||
tab_absnonjust = GenTable(
|
||||
|
@ -844,9 +830,9 @@ def ListeAbsEtud(
|
|||
# Formats non HTML et demande d'une seule table:
|
||||
if format != "html" and format != "text":
|
||||
if absjust_only == 1:
|
||||
return tab_absjust.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab_absjust.make_page(format=format)
|
||||
else:
|
||||
return tab_absnonjust.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab_absnonjust.make_page(format=format)
|
||||
|
||||
if format == "html":
|
||||
# Mise en forme HTML:
|
||||
|
@ -896,13 +882,12 @@ def ListeAbsEtud(
|
|||
raise ValueError("Invalid format !")
|
||||
|
||||
|
||||
def _TablesAbsEtud(
|
||||
def _tables_abs_etud(
|
||||
etudid,
|
||||
datedebut,
|
||||
with_evals=True,
|
||||
format="html",
|
||||
absjust_only=0,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Tables des absences justifiees et non justifiees d'un étudiant
|
||||
sur l'année en cours
|
||||
|
@ -928,11 +913,11 @@ def _TablesAbsEtud(
|
|||
cursor.execute(
|
||||
"""SELECT mi.moduleimpl_id
|
||||
FROM absences abs, notes_moduleimpl_inscription mi, notes_moduleimpl m
|
||||
WHERE abs.matin = %(matin)s
|
||||
and abs.jour = %(jour)s
|
||||
and abs.etudid = %(etudid)s
|
||||
and abs.moduleimpl_id = mi.moduleimpl_id
|
||||
and mi.moduleimpl_id = m.id
|
||||
WHERE abs.matin = %(matin)s
|
||||
and abs.jour = %(jour)s
|
||||
and abs.etudid = %(etudid)s
|
||||
and abs.moduleimpl_id = mi.moduleimpl_id
|
||||
and mi.moduleimpl_id = m.id
|
||||
and mi.etudid = %(etudid)s
|
||||
""",
|
||||
{
|
||||
|
@ -959,8 +944,9 @@ def _TablesAbsEtud(
|
|||
)[0]
|
||||
if format == "html":
|
||||
ex.append(
|
||||
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
|
||||
% (mod["moduleimpl_id"], mod["module"]["code"])
|
||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"]}</a>"""
|
||||
)
|
||||
else:
|
||||
ex.append(mod["module"]["code"])
|
||||
|
@ -976,8 +962,9 @@ def _TablesAbsEtud(
|
|||
)[0]
|
||||
if format == "html":
|
||||
ex.append(
|
||||
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
|
||||
% (mod["moduleimpl_id"], mod["module"]["code"])
|
||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"]}</a>"""
|
||||
)
|
||||
else:
|
||||
ex.append(mod["module"]["code"])
|
||||
|
|
|
@ -29,37 +29,40 @@
|
|||
|
||||
|
||||
Archives are plain files, stored in
|
||||
<SCODOC_VAR_DIR>/archives/<deptid>
|
||||
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <deptid> a departement id)
|
||||
<SCODOC_VAR_DIR>/archives/<dept_id>
|
||||
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <dept_id> a departement id (int))
|
||||
|
||||
Les PV de jurys et documents associés sont stockées dans un sous-repertoire de la forme
|
||||
<archivedir>/<dept>/<formsemestre_id>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(formsemestre_id est ici FormSemestre.scodoc7_id ou à défaut FormSemestre.id)
|
||||
(formsemestre_id est ici FormSemestre.id)
|
||||
|
||||
Les documents liés à l'étudiant sont dans
|
||||
<archivedir>/docetuds/<dept>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(etudid est ici soit Identite.scodoc7id, soit à défaut Identite.id)
|
||||
<archivedir>/docetuds/<dept_id>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(etudid est ici Identite.id)
|
||||
|
||||
Les maquettes Apogée pour l'export des notes sont dans
|
||||
<archivedir>/apo_csv/<dept>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
||||
<archivedir>/apo_csv/<dept_id>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
||||
|
||||
Un répertoire d'archive contient des fichiers quelconques, et un fichier texte nommé _description.txt
|
||||
qui est une description (humaine, format libre) de l'archive.
|
||||
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import glob
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import glob
|
||||
import time
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from config import Config
|
||||
from app import log
|
||||
from app.models import Departement
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc.sco_exceptions import (
|
||||
AccessDenied,
|
||||
|
@ -108,7 +111,8 @@ class BaseArchiver(object):
|
|||
If directory does not yet exist, create it.
|
||||
"""
|
||||
self.initialize()
|
||||
dept_dir = os.path.join(self.root, g.scodoc_dept)
|
||||
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
|
||||
dept_dir = os.path.join(self.root, str(dept.id))
|
||||
try:
|
||||
scu.GSL.acquire()
|
||||
if not os.path.isdir(dept_dir):
|
||||
|
@ -127,7 +131,8 @@ class BaseArchiver(object):
|
|||
:return: list of archive oids
|
||||
"""
|
||||
self.initialize()
|
||||
base = os.path.join(self.root, g.scodoc_dept) + os.path.sep
|
||||
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
|
||||
base = os.path.join(self.root, str(dept.id)) + os.path.sep
|
||||
dirs = glob.glob(base + "*")
|
||||
return [os.path.split(x)[1] for x in dirs]
|
||||
|
||||
|
@ -244,31 +249,15 @@ class BaseArchiver(object):
|
|||
log("reading archive file %s" % fname)
|
||||
return open(fname, "rb").read()
|
||||
|
||||
def get_archived_file(self, REQUEST, oid, archive_name, filename):
|
||||
def get_archived_file(self, oid, archive_name, filename):
|
||||
"""Recupere donnees du fichier indiqué et envoie au client"""
|
||||
# XXX très incomplet: devrait inférer et assigner un type MIME
|
||||
archive_id = self.get_id_from_name(oid, archive_name)
|
||||
data = self.get(archive_id, filename)
|
||||
ext = os.path.splitext(filename.lower())[1]
|
||||
if ext == ".html" or ext == ".htm":
|
||||
return data
|
||||
elif ext == ".xml":
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
return data
|
||||
elif ext == ".xls":
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, filename, mime=scu.XLS_MIMETYPE
|
||||
)
|
||||
elif ext == ".xlsx":
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, filename, mime=scu.XLSX_MIMETYPE
|
||||
)
|
||||
elif ext == ".csv":
|
||||
return scu.sendCSVFile(REQUEST, data, filename)
|
||||
elif ext == ".pdf":
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
REQUEST.RESPONSE.setHeader("content-type", "application/octet-stream")
|
||||
return data # should set mimetype for known files like images
|
||||
mime = mimetypes.guess_type(filename)[0]
|
||||
if mime is None:
|
||||
mime = "application/octet-stream"
|
||||
|
||||
return scu.send_file(data, filename, mime=mime)
|
||||
|
||||
|
||||
class SemsArchiver(BaseArchiver):
|
||||
|
@ -305,7 +294,7 @@ def do_formsemestre_archive(
|
|||
from app.scodoc.sco_recapcomplet import make_formsemestre_recapcomplet
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
archive_id = PVArchive.create_obj_archive(sem_archive_id, description)
|
||||
date = PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
|
||||
|
||||
|
@ -394,9 +383,7 @@ def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
|
|||
(all students or only selected groups)
|
||||
"""
|
||||
if not sco_permissions_check.can_edit_pv(formsemestre_id):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not group_ids:
|
||||
|
@ -408,7 +395,6 @@ def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Archiver les PV et résultats du semestre",
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
|
@ -469,7 +455,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
|
@ -519,7 +505,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
|||
def formsemestre_list_archives(REQUEST, formsemestre_id):
|
||||
"""Page listing archives"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
L = []
|
||||
for archive_id in PVArchive.list_obj_archives(sem_archive_id):
|
||||
a = {
|
||||
|
@ -530,7 +516,7 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
|||
}
|
||||
L.append(a)
|
||||
|
||||
H = [html_sco_header.html_sem_header(REQUEST, "Archive des PV et résultats ", sem)]
|
||||
H = [html_sco_header.html_sem_header("Archive des PV et résultats ", sem)]
|
||||
if not L:
|
||||
H.append("<p>aucune archive enregistrée</p>")
|
||||
else:
|
||||
|
@ -559,11 +545,11 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
|||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def formsemestre_get_archived_file(REQUEST, formsemestre_id, archive_name, filename):
|
||||
def formsemestre_get_archived_file(formsemestre_id, archive_name, filename):
|
||||
"""Send file to client."""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
return PVArchive.get_archived_file(REQUEST, sem_archive_id, archive_name, filename)
|
||||
sem_archive_id = formsemestre_id
|
||||
return PVArchive.get_archived_file(sem_archive_id, archive_name, filename)
|
||||
|
||||
|
||||
def formsemestre_delete_archive(
|
||||
|
@ -571,11 +557,9 @@ def formsemestre_delete_archive(
|
|||
):
|
||||
"""Delete an archive"""
|
||||
if not sco_permissions_check.can_edit_pv(formsemestre_id):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
archive_id = PVArchive.get_id_from_name(sem_archive_id, archive_name)
|
||||
|
||||
dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id)
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
les dossiers d'admission et autres pièces utiles.
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_import_etuds
|
||||
|
@ -60,12 +61,12 @@ def can_edit_etud_archive(authuser):
|
|||
|
||||
def etud_list_archives_html(REQUEST, etudid):
|
||||
"""HTML snippet listing archives"""
|
||||
can_edit = can_edit_etud_archive(REQUEST.AUTHENTICATED_USER)
|
||||
can_edit = can_edit_etud_archive(current_user)
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etudid
|
||||
etud_archive_id = etudid
|
||||
L = []
|
||||
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||
a = {
|
||||
|
@ -118,7 +119,7 @@ def add_archives_info_to_etud_list(etuds):
|
|||
"""
|
||||
for etud in etuds:
|
||||
l = []
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||
l.append(
|
||||
"%s (%s)"
|
||||
|
@ -133,10 +134,8 @@ def add_archives_info_to_etud_list(etuds):
|
|||
def etud_upload_file_form(REQUEST, etudid):
|
||||
"""Page with a form to choose and upload a file, with a description."""
|
||||
# check permission
|
||||
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % REQUEST.AUTHENTICATED_USER
|
||||
)
|
||||
if not can_edit_etud_archive(current_user):
|
||||
raise AccessDenied("opération non autorisée pour %s" % current_user)
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
|
@ -153,7 +152,7 @@ def etud_upload_file_form(REQUEST, etudid):
|
|||
% (scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("etudid", {"default": etudid, "input_type": "hidden"}),
|
||||
|
@ -181,7 +180,7 @@ def etud_upload_file_form(REQUEST, etudid):
|
|||
data = tf[2]["datafile"].read()
|
||||
descr = tf[2]["description"]
|
||||
filename = tf[2]["datafile"].filename
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
_store_etud_file_to_new_archive(
|
||||
etud_archive_id, data, filename, description=descr
|
||||
)
|
||||
|
@ -202,15 +201,13 @@ def _store_etud_file_to_new_archive(etud_archive_id, data, filename, description
|
|||
def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
||||
"""Delete an archive"""
|
||||
# check permission
|
||||
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
if not can_edit_etud_archive(current_user):
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
archive_id = EtudsArchive.get_id_from_name(etud_archive_id, archive_name)
|
||||
if not dialog_confirmed:
|
||||
return scu.confirm_dialog(
|
||||
|
@ -242,16 +239,14 @@ def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
|||
)
|
||||
|
||||
|
||||
def etud_get_archived_file(REQUEST, etudid, archive_name, filename):
|
||||
def etud_get_archived_file(etudid, archive_name, filename):
|
||||
"""Send file to client."""
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid, filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
return EtudsArchive.get_archived_file(
|
||||
REQUEST, etud_archive_id, archive_name, filename
|
||||
)
|
||||
etud_archive_id = etud["etudid"]
|
||||
return EtudsArchive.get_archived_file(etud_archive_id, archive_name, filename)
|
||||
|
||||
|
||||
# --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants)
|
||||
|
@ -271,8 +266,11 @@ def etudarchive_generate_excel_sample(group_id=None, REQUEST=None):
|
|||
],
|
||||
extra_cols=["fichier_a_charger"],
|
||||
)
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX
|
||||
return scu.send_file(
|
||||
data,
|
||||
"ImportFichiersEtudiants",
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
|
||||
|
||||
|
@ -310,7 +308,7 @@ def etudarchive_import_files_form(group_id, REQUEST=None):
|
|||
]
|
||||
F = html_sco_header.sco_footer()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"""Génération des bulletins de notes
|
||||
|
||||
"""
|
||||
from app.models import formsemestre
|
||||
import time
|
||||
import pprint
|
||||
import email
|
||||
|
@ -35,11 +36,10 @@ 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 six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask import url_for
|
||||
from flask_login import current_user
|
||||
from flask_mail import Message
|
||||
|
@ -48,7 +48,7 @@ import app.scodoc.sco_utils as scu
|
|||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_exceptions import AccessDenied
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import htmlutils
|
||||
from app.scodoc import sco_abs
|
||||
|
@ -121,9 +121,7 @@ def make_context_dict(sem, etud):
|
|||
return C
|
||||
|
||||
|
||||
def formsemestre_bulletinetud_dict(
|
||||
formsemestre_id, etudid, version="long", REQUEST=None
|
||||
):
|
||||
def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
||||
"""Collecte informations pour bulletin de notes
|
||||
Retourne un dictionnaire (avec valeur par défaut chaine vide).
|
||||
Le contenu du dictionnaire dépend des options (rangs, ...)
|
||||
|
@ -143,10 +141,7 @@ def formsemestre_bulletinetud_dict(
|
|||
I["etudid"] = etudid
|
||||
I["formsemestre_id"] = formsemestre_id
|
||||
I["sem"] = nt.sem
|
||||
if REQUEST:
|
||||
I["server_name"] = REQUEST.BASE0
|
||||
else:
|
||||
I["server_name"] = ""
|
||||
I["server_name"] = request.url_root
|
||||
|
||||
# Formation et parcours
|
||||
I["formation"] = sco_formations.formation_list(
|
||||
|
@ -778,7 +773,10 @@ def formsemestre_bulletinetud(
|
|||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
etudid = etud["etudid"]
|
||||
except:
|
||||
return scu.log_unknown_etud(REQUEST, format=format)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
bulletin = do_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
|
@ -791,7 +789,8 @@ def formsemestre_bulletinetud(
|
|||
REQUEST=REQUEST,
|
||||
)[0]
|
||||
if format not in {"html", "pdfmail"}:
|
||||
return bulletin
|
||||
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 = [
|
||||
|
@ -862,14 +861,13 @@ def do_formsemestre_bulletinetud(
|
|||
):
|
||||
"""Génère le bulletin au format demandé.
|
||||
Retourne: (bul, filigranne)
|
||||
où bul est au format demandé (html, pdf, pdfmail, pdfpart, xml)
|
||||
où bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
|
||||
et filigranne est un message à placer en "filigranne" (eg "Provisoire").
|
||||
"""
|
||||
if format == "xml":
|
||||
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
version=version,
|
||||
|
@ -881,19 +879,18 @@ def do_formsemestre_bulletinetud(
|
|||
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
version=version,
|
||||
)
|
||||
return bul, ""
|
||||
|
||||
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid, REQUEST=REQUEST)
|
||||
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
|
||||
etud = I["etud"]
|
||||
|
||||
if format == "html":
|
||||
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="html", REQUEST=REQUEST
|
||||
I, version=version, format="html"
|
||||
)
|
||||
return htm, I["filigranne"]
|
||||
|
||||
|
@ -903,11 +900,10 @@ def do_formsemestre_bulletinetud(
|
|||
version=version,
|
||||
format="pdf",
|
||||
stand_alone=(format != "pdfpart"),
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
if format == "pdf":
|
||||
return (
|
||||
scu.sendPDFFile(REQUEST, bul, filename),
|
||||
scu.sendPDFFile(bul, filename),
|
||||
I["filigranne"],
|
||||
) # unused ret. value
|
||||
else:
|
||||
|
@ -923,11 +919,11 @@ def do_formsemestre_bulletinetud(
|
|||
htm = "" # speed up if html version not needed
|
||||
else:
|
||||
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="html", REQUEST=REQUEST
|
||||
I, version=version, format="html"
|
||||
)
|
||||
|
||||
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="pdf", REQUEST=REQUEST
|
||||
I, version=version, format="pdf"
|
||||
)
|
||||
|
||||
if prefer_mail_perso:
|
||||
|
@ -998,7 +994,6 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
|
|||
|
||||
# Attach pdf
|
||||
msg.attach(filename, scu.PDF_MIMETYPE, pdfdata)
|
||||
|
||||
log("mail bulletin a %s" % recipient_addr)
|
||||
email.send_message(msg)
|
||||
|
||||
|
@ -1033,7 +1028,7 @@ def _formsemestre_bulletinetud_header_html(
|
|||
),
|
||||
"""
|
||||
<form name="f" method="GET" action="%s">"""
|
||||
% REQUEST.URL0,
|
||||
% request.base_url,
|
||||
f"""Bulletin <span class="bull_liensemestre"><a href="{
|
||||
url_for("notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
|
@ -1063,14 +1058,20 @@ def _formsemestre_bulletinetud_header_html(
|
|||
H.append("""</select></td>""")
|
||||
# Menu
|
||||
endpoint = "notes.formsemestre_bulletinetud"
|
||||
url = REQUEST.URL0
|
||||
qurl = six.moves.urllib.parse.quote_plus(url + "?" + REQUEST.QUERY_STRING)
|
||||
|
||||
menuBul = [
|
||||
{
|
||||
"title": "Réglages bulletins",
|
||||
"endpoint": "notes.formsemestre_edit_options",
|
||||
"args": {"formsemestre_id": formsemestre_id, "target_url": qurl},
|
||||
"args": {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
# "target_url": url_for(
|
||||
# "notes.formsemestre_bulletinetud",
|
||||
# scodoc_dept=g.scodoc_dept,
|
||||
# formsemestre_id=formsemestre_id,
|
||||
# etudid=etudid,
|
||||
# ),
|
||||
},
|
||||
"enabled": (current_user.id in sem["responsables"])
|
||||
or current_user.has_permission(Permission.ScoImplement),
|
||||
},
|
||||
|
@ -1113,6 +1114,16 @@ def _formsemestre_bulletinetud_header_html(
|
|||
"enabled": etud["emailperso"]
|
||||
and can_send_bulletin_by_mail(formsemestre_id),
|
||||
},
|
||||
{
|
||||
"title": "Version json",
|
||||
"endpoint": endpoint,
|
||||
"args": {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"etudid": etudid,
|
||||
"version": version,
|
||||
"format": "json",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Version XML",
|
||||
"endpoint": endpoint,
|
||||
|
@ -1188,9 +1199,14 @@ def _formsemestre_bulletinetud_header_html(
|
|||
H.append(
|
||||
'<td> <a href="%s">%s</a></td>'
|
||||
% (
|
||||
url
|
||||
+ "?formsemestre_id=%s&etudid=%s&format=pdf&version=%s"
|
||||
% (formsemestre_id, etudid, version),
|
||||
url_for(
|
||||
"notes.formsemestre_bulletinetud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
format="pdf",
|
||||
version=version,
|
||||
),
|
||||
scu.ICON_PDF,
|
||||
)
|
||||
)
|
||||
|
@ -1201,9 +1217,7 @@ def _formsemestre_bulletinetud_header_html(
|
|||
"""
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de " + etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de " + etud["nom"]),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
|
|
|
@ -52,6 +52,9 @@ import reportlab
|
|||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
|
||||
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
|
||||
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import NoteProcessError
|
||||
from app import log
|
||||
|
@ -148,14 +151,7 @@ class BulletinGenerator(object):
|
|||
def get_filename(self):
|
||||
"""Build a filename to be proposed to the web client"""
|
||||
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"])
|
||||
dt = time.strftime("%Y-%m-%d")
|
||||
filename = "bul-%s-%s-%s.pdf" % (
|
||||
sem["titre_num"],
|
||||
dt,
|
||||
self.infos["etud"]["nom"],
|
||||
)
|
||||
filename = scu.unescape_html(filename).replace(" ", "_").replace("&", "")
|
||||
return filename
|
||||
return scu.bul_filename(sem, self.infos["etud"], "pdf")
|
||||
|
||||
def generate(self, format="", stand_alone=True):
|
||||
"""Return bulletin in specified format"""
|
||||
|
@ -260,7 +256,6 @@ def make_formsemestre_bulletinetud(
|
|||
version="long", # short, long, selectedevals
|
||||
format="pdf", # html, pdf
|
||||
stand_alone=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Bulletin de notes
|
||||
|
||||
|
@ -286,10 +281,10 @@ def make_formsemestre_bulletinetud(
|
|||
PDFLOCK.acquire()
|
||||
bul_generator = gen_class(
|
||||
infos,
|
||||
authuser=REQUEST.AUTHENTICATED_USER,
|
||||
authuser=current_user,
|
||||
version=version,
|
||||
filigranne=infos["filigranne"],
|
||||
server_name=REQUEST.BASE0,
|
||||
server_name=request.url_root,
|
||||
)
|
||||
if format not in bul_generator.supported_formats:
|
||||
# use standard generator
|
||||
|
@ -301,10 +296,10 @@ def make_formsemestre_bulletinetud(
|
|||
gen_class = bulletin_get_class(bul_class_name)
|
||||
bul_generator = gen_class(
|
||||
infos,
|
||||
authuser=REQUEST.AUTHENTICATED_USER,
|
||||
authuser=current_user,
|
||||
version=version,
|
||||
filigranne=infos["filigranne"],
|
||||
server_name=REQUEST.BASE0,
|
||||
server_name=request.url_root,
|
||||
)
|
||||
|
||||
data = bul_generator.generate(format=format, stand_alone=stand_alone)
|
||||
|
|
|
@ -47,27 +47,22 @@ from app.scodoc import sco_etud
|
|||
|
||||
|
||||
def make_json_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=None,
|
||||
formsemestre_id: int,
|
||||
etudid: int,
|
||||
xml_with_decisions=False,
|
||||
version="long",
|
||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
||||
):
|
||||
) -> str:
|
||||
"""Renvoie bulletin en chaine JSON"""
|
||||
|
||||
d = formsemestre_bulletinetud_published_dict(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
force_publishing=force_publishing,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
version=version,
|
||||
)
|
||||
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
|
||||
|
||||
return json.dumps(d, cls=scu.ScoDocJSONEncoder)
|
||||
|
||||
|
||||
|
@ -79,7 +74,6 @@ def formsemestre_bulletinetud_published_dict(
|
|||
etudid,
|
||||
force_publishing=False,
|
||||
xml_nodate=False,
|
||||
REQUEST=None,
|
||||
xml_with_decisions=False, # inclue les decisions même si non publiées
|
||||
version="long",
|
||||
):
|
||||
|
|
|
@ -58,7 +58,7 @@ import traceback
|
|||
|
||||
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
|
||||
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
|
@ -193,7 +193,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedev
|
|||
#
|
||||
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}
|
||||
if REQUEST:
|
||||
server_name = REQUEST.BASE0
|
||||
server_name = request.url_root
|
||||
else:
|
||||
server_name = ""
|
||||
try:
|
||||
|
@ -243,7 +243,7 @@ def get_etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
|
|||
i = i + 1
|
||||
infos = {"DeptName": sco_preferences.get_preference("DeptName")}
|
||||
if REQUEST:
|
||||
server_name = REQUEST.BASE0
|
||||
server_name = request.url_root
|
||||
else:
|
||||
server_name = ""
|
||||
try:
|
||||
|
|
|
@ -69,16 +69,13 @@ def make_xml_formsemestre_bulletinetud(
|
|||
doc=None, # XML document
|
||||
force_publishing=False,
|
||||
xml_nodate=False,
|
||||
REQUEST=None,
|
||||
xml_with_decisions=False, # inclue les decisions même si non publiées
|
||||
version="long",
|
||||
):
|
||||
) -> str:
|
||||
"bulletin au format XML"
|
||||
from app.scodoc import sco_bulletins
|
||||
|
||||
log("xml_bulletin( formsemestre_id=%s, etudid=%s )" % (formsemestre_id, etudid))
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if (not sem["bul_hide_xml"]) or force_publishing:
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
(coût théorique en heures équivalent TD)
|
||||
"""
|
||||
from flask import request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import sco_formsemestre
|
||||
|
@ -182,7 +184,7 @@ def formsemestre_estim_cost(
|
|||
<br/>
|
||||
</form>
|
||||
""" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
n_group_td,
|
||||
n_group_tp,
|
||||
|
@ -190,11 +192,11 @@ def formsemestre_estim_cost(
|
|||
)
|
||||
tab.html_before_table = h
|
||||
tab.base_url = "%s?formsemestre_id=%s&n_group_td=%s&n_group_tp=%s&coef_tp=%s" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
n_group_td,
|
||||
n_group_tp,
|
||||
coef_tp,
|
||||
)
|
||||
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant
|
||||
"""
|
||||
import http
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -47,10 +47,10 @@ from app.scodoc import sco_etud
|
|||
import sco_version
|
||||
|
||||
|
||||
def report_debouche_date(start_year=None, format="html", REQUEST=None):
|
||||
def report_debouche_date(start_year=None, format="html"):
|
||||
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
|
||||
if not start_year:
|
||||
return report_debouche_ask_date(REQUEST=REQUEST)
|
||||
return report_debouche_ask_date()
|
||||
if format == "xls":
|
||||
keep_numeric = True # pas de conversion des notes en strings
|
||||
else:
|
||||
|
@ -64,13 +64,12 @@ def report_debouche_date(start_year=None, format="html", REQUEST=None):
|
|||
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + ""
|
||||
)
|
||||
tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year
|
||||
tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year)
|
||||
tab.base_url = "%s?start_year=%s" % (request.base_url, start_year)
|
||||
return tab.make_page(
|
||||
title="""<h2 class="formsemestre">Débouchés étudiants </h2>""",
|
||||
init_qtip=True,
|
||||
javascripts=["js/etud_info.js"],
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=True,
|
||||
)
|
||||
|
||||
|
@ -194,7 +193,7 @@ def table_debouche_etudids(etudids, keep_numeric=True):
|
|||
return tab
|
||||
|
||||
|
||||
def report_debouche_ask_date(REQUEST=None):
|
||||
def report_debouche_ask_date():
|
||||
"""Formulaire demande date départ"""
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
|
@ -249,7 +248,7 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
|
|||
return None
|
||||
|
||||
|
||||
def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
|
||||
def itemsuivi_suppress(itemsuivi_id):
|
||||
"""Suppression d'un item"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
|
@ -259,9 +258,10 @@ def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
|
|||
_itemsuivi_delete(cnx, itemsuivi_id)
|
||||
logdb(cnx, method="itemsuivi_suppress", etudid=item["etudid"])
|
||||
log("suppressed itemsuivi %s" % (itemsuivi_id,))
|
||||
return ("", 204)
|
||||
|
||||
|
||||
def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=None):
|
||||
def itemsuivi_create(etudid, item_date=None, situation="", format=None):
|
||||
"""Creation d'un item"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
|
@ -273,11 +273,11 @@ def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=
|
|||
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
|
||||
item = itemsuivi_get(cnx, itemsuivi_id)
|
||||
if format == "json":
|
||||
return scu.sendJSON(REQUEST, item)
|
||||
return scu.sendJSON(item)
|
||||
return item
|
||||
|
||||
|
||||
def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
|
||||
def itemsuivi_set_date(itemsuivi_id, item_date):
|
||||
"""set item date
|
||||
item_date is a string dd/mm/yyyy
|
||||
"""
|
||||
|
@ -288,9 +288,10 @@ def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
|
|||
item = itemsuivi_get(cnx, itemsuivi_id)
|
||||
item["item_date"] = item_date
|
||||
_itemsuivi_edit(cnx, item)
|
||||
return ("", 204)
|
||||
|
||||
|
||||
def itemsuivi_set_situation(object, value, REQUEST=None):
|
||||
def itemsuivi_set_situation(object, value):
|
||||
"""set situation"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
|
@ -304,14 +305,14 @@ def itemsuivi_set_situation(object, value, REQUEST=None):
|
|||
return situation or scu.IT_SITUATION_MISSING_STR
|
||||
|
||||
|
||||
def itemsuivi_list_etud(etudid, format=None, REQUEST=None):
|
||||
def itemsuivi_list_etud(etudid, format=None):
|
||||
"""Liste des items pour cet étudiant, avec tags"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
items = _itemsuivi_list(cnx, {"etudid": etudid})
|
||||
for it in items:
|
||||
it["tags"] = ", ".join(itemsuivi_tag_list(it["itemsuivi_id"]))
|
||||
if format == "json":
|
||||
return scu.sendJSON(REQUEST, items)
|
||||
return scu.sendJSON(items)
|
||||
return items
|
||||
|
||||
|
||||
|
@ -328,7 +329,7 @@ def itemsuivi_tag_list(itemsuivi_id):
|
|||
return [x["title"] for x in r]
|
||||
|
||||
|
||||
def itemsuivi_tag_search(term, REQUEST=None):
|
||||
def itemsuivi_tag_search(term):
|
||||
"""List all used tag names (for auto-completion)"""
|
||||
# restrict charset to avoid injections
|
||||
if not scu.ALPHANUM_EXP.match(term):
|
||||
|
@ -343,10 +344,10 @@ def itemsuivi_tag_search(term, REQUEST=None):
|
|||
)
|
||||
data = [x["title"] for x in r]
|
||||
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
return scu.sendJSON(data)
|
||||
|
||||
|
||||
def itemsuivi_tag_set(itemsuivi_id="", taglist=[], REQUEST=None):
|
||||
def itemsuivi_tag_set(itemsuivi_id="", taglist=None):
|
||||
"""taglist may either be:
|
||||
a string with tag names separated by commas ("un;deux")
|
||||
or a list of strings (["un", "deux"])
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"""Page accueil département (liste des semestres, etc)
|
||||
"""
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app
|
||||
|
@ -131,7 +131,7 @@ def index_html(REQUEST=None, showcodes=0, showsemtable=0):
|
|||
if not showsemtable:
|
||||
H.append(
|
||||
'<hr/><p><a href="%s?showsemtable=1">Voir tous les semestres</a></p>'
|
||||
% REQUEST.URL0
|
||||
% request.base_url
|
||||
)
|
||||
|
||||
H.append(
|
||||
|
@ -242,7 +242,7 @@ def _sem_table_gt(sems, showcodes=False):
|
|||
rows=sems,
|
||||
html_class="table_leftalign semlist",
|
||||
html_sortable=True,
|
||||
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
|
||||
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
|
||||
# caption='Maquettes enregistrées',
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
|
|
@ -51,6 +51,7 @@ import fcntl
|
|||
import subprocess
|
||||
import requests
|
||||
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -64,7 +65,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
|||
SCO_DUMP_LOCK = "/tmp/scodump.lock"
|
||||
|
||||
|
||||
def sco_dump_and_send_db(REQUEST=None):
|
||||
def sco_dump_and_send_db():
|
||||
"""Dump base de données et l'envoie anonymisée pour debug"""
|
||||
H = [html_sco_header.sco_header(page_title="Assistance technique")]
|
||||
# get currect (dept) DB name:
|
||||
|
@ -93,7 +94,7 @@ def sco_dump_and_send_db(REQUEST=None):
|
|||
_anonymize_db(ano_db_name)
|
||||
|
||||
# Send
|
||||
r = _send_db(REQUEST, ano_db_name)
|
||||
r = _send_db(ano_db_name)
|
||||
if (
|
||||
r.status_code
|
||||
== requests.codes.INSUFFICIENT_STORAGE # pylint: disable=no-member
|
||||
|
@ -171,29 +172,27 @@ def _get_scodoc_serial():
|
|||
return 0
|
||||
|
||||
|
||||
def _send_db(REQUEST, ano_db_name):
|
||||
def _send_db(ano_db_name):
|
||||
"""Dump this (anonymized) database and send it to tech support"""
|
||||
log("dumping anonymized database {}".format(ano_db_name))
|
||||
log(f"dumping anonymized database {ano_db_name}")
|
||||
try:
|
||||
data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log("sco_dump_and_send_db: exception in anonymisation: {}".format(e))
|
||||
raise ScoValueError(
|
||||
"erreur lors de l'anonymisation de la base {}".format(ano_db_name)
|
||||
dump = subprocess.check_output(
|
||||
f"pg_dump --format=custom {ano_db_name}", shell=1
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log(f"sco_dump_and_send_db: exception in anonymisation: {e}")
|
||||
raise ScoValueError(f"erreur lors de l'anonymisation de la base {ano_db_name}")
|
||||
|
||||
log("uploading anonymized dump...")
|
||||
files = {"file": (ano_db_name + ".gz", data)}
|
||||
files = {"file": (ano_db_name + ".dump", dump)}
|
||||
r = requests.post(
|
||||
scu.SCO_DUMP_UP_URL,
|
||||
files=files,
|
||||
data={
|
||||
"dept_name": sco_preferences.get_preference("DeptName"),
|
||||
"serial": _get_scodoc_serial(),
|
||||
"sco_user": str(REQUEST.AUTHENTICATED_USER),
|
||||
"sent_by": sco_users.user_info(str(REQUEST.AUTHENTICATED_USER))[
|
||||
"nomcomplet"
|
||||
],
|
||||
"sco_user": str(current_user),
|
||||
"sent_by": sco_users.user_info(str(current_user))["nomcomplet"],
|
||||
"sco_version": sco_version.SCOVERSION,
|
||||
"sco_fullversion": scu.get_scodoc_version(),
|
||||
},
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -47,7 +47,7 @@ from app.scodoc import sco_formsemestre
|
|||
from app.scodoc import sco_news
|
||||
|
||||
|
||||
def formation_delete(formation_id=None, dialog_confirmed=False, REQUEST=None):
|
||||
def formation_delete(formation_id=None, dialog_confirmed=False):
|
||||
"""Delete a formation"""
|
||||
F = sco_formations.formation_list(args={"formation_id": formation_id})
|
||||
if not F:
|
||||
|
@ -159,7 +159,7 @@ def formation_edit(formation_id=None, create=False, REQUEST=None):
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("formation_id", {"default": formation_id, "input_type": "hidden"}),
|
||||
|
@ -311,7 +311,7 @@ def invalidate_sems_in_formation(formation_id):
|
|||
) # > formation modif.
|
||||
|
||||
|
||||
def module_move(module_id, after=0, REQUEST=None, redirect=1):
|
||||
def module_move(module_id, after=0, redirect=1):
|
||||
"""Move before/after previous one (decrement/increment numero)"""
|
||||
module = sco_edit_module.do_module_list({"module_id": module_id})[0]
|
||||
redirect = int(redirect)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -116,7 +116,7 @@ associé.
|
|||
</p>""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("ue_id", {"input_type": "hidden", "default": ue_id}),
|
||||
|
@ -202,7 +202,7 @@ def matiere_delete(matiere_id=None, REQUEST=None):
|
|||
]
|
||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(UE["formation_id"])
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("matiere_id", {"input_type": "hidden"}),),
|
||||
initvalues=M,
|
||||
|
@ -256,7 +256,7 @@ des notes.</em>
|
|||
associé.
|
||||
</p>"""
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("matiere_id", {"input_type": "hidden"}),
|
||||
|
@ -323,4 +323,4 @@ def matiere_is_locked(matiere_id):
|
|||
""",
|
||||
{"matiere_id": matiere_id},
|
||||
)
|
||||
return len(r) > 0
|
||||
return len(r) > 0
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -143,7 +144,7 @@ def module_create(matiere_id=None, REQUEST=None):
|
|||
else:
|
||||
default_num = 10
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -258,12 +259,13 @@ def do_module_delete(oid):
|
|||
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
||||
mods = sco_moduleimpl.do_moduleimpl_list(module_id=oid)
|
||||
if mods:
|
||||
err_page = scu.confirm_dialog(
|
||||
message="""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>""",
|
||||
helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""",
|
||||
dest_url="ue_list",
|
||||
parameters={"formation_id": mod["formation_id"]},
|
||||
)
|
||||
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
||||
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
|
||||
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
||||
</p>
|
||||
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept,
|
||||
formation_id=mod["formation_id"])}">reprendre</a>
|
||||
"""
|
||||
raise ScoGenError(err_page)
|
||||
# delete
|
||||
cnx = ndb.GetDBConnexion()
|
||||
|
@ -294,7 +296,7 @@ def module_delete(module_id=None, REQUEST=None):
|
|||
|
||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("module_id", {"input_type": "hidden"}),),
|
||||
initvalues=Mod,
|
||||
|
@ -388,7 +390,7 @@ def module_edit(module_id=None, REQUEST=None):
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -544,7 +546,7 @@ def module_list(formation_id, REQUEST=None):
|
|||
% F,
|
||||
'<ul class="notes_module_list">',
|
||||
]
|
||||
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
|
||||
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
||||
|
||||
for Mod in do_module_list(args={"formation_id": formation_id}):
|
||||
H.append('<li class="notes_module_list">%s' % Mod)
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -326,7 +326,11 @@ def ue_edit(ue_id=None, create=False, formation_id=None, REQUEST=None):
|
|||
)
|
||||
)
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0, REQUEST.form, fw, initvalues=initvalues, submitlabel=submitlabel
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
fw,
|
||||
initvalues=initvalues,
|
||||
submitlabel=submitlabel,
|
||||
)
|
||||
if tf[0] == 0:
|
||||
X = """<div id="ue_list_code"></div>
|
||||
|
@ -1033,13 +1037,13 @@ def formation_table_recap(formation_id, format="html", REQUEST=None):
|
|||
caption=title,
|
||||
html_caption=title,
|
||||
html_class="table_leftalign",
|
||||
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
|
||||
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
|
||||
page_title=title,
|
||||
html_title="<h2>" + title + "</h2>",
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def ue_list_semestre_ids(ue):
|
||||
|
|
|
@ -149,7 +149,7 @@ def group_edt_json(group_id, start="", end="", REQUEST=None): # actuellement in
|
|||
}
|
||||
J.append(d)
|
||||
|
||||
return scu.sendJSON(REQUEST, J)
|
||||
return scu.sendJSON(J)
|
||||
|
||||
|
||||
"""XXX
|
||||
|
|
|
@ -32,11 +32,11 @@
|
|||
Voir sco_apogee_csv.py pour la structure du fichier Apogée.
|
||||
|
||||
Stockage: utilise sco_archive.py
|
||||
=> /opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR.csv
|
||||
=> /opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR.csv
|
||||
pour une maquette de l'année scolaire 2016, semestre 1, etape V3ASR
|
||||
|
||||
ou bien (à partir de ScoDoc 1678) :
|
||||
/opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
|
||||
/opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
|
||||
pour une maquette de l'étape V3ASR version VDI 111.
|
||||
|
||||
La version VDI sera ignorée sauf si elle est indiquée dans l'étape du semestre.
|
||||
|
|
|
@ -32,7 +32,7 @@ import io
|
|||
from zipfile import ZipFile
|
||||
|
||||
import flask
|
||||
from flask import url_for, g, send_file
|
||||
from flask import url_for, g, send_file, request
|
||||
|
||||
# from werkzeug.utils import send_file
|
||||
|
||||
|
@ -250,7 +250,7 @@ def apo_semset_maq_status(
|
|||
"""<form name="f" method="get" action="%s">
|
||||
<input type="hidden" name="semset_id" value="%s"></input>
|
||||
<div><input type="checkbox" name="allow_missing_apo" value="1" onchange="document.f.submit()" """
|
||||
% (REQUEST.URL0, semset_id)
|
||||
% (request.base_url, semset_id)
|
||||
)
|
||||
if allow_missing_apo:
|
||||
H.append("checked")
|
||||
|
@ -476,7 +476,7 @@ def table_apo_csv_list(semset, REQUEST=None):
|
|||
rows=T,
|
||||
html_class="table_leftalign apo_maq_list",
|
||||
html_sortable=True,
|
||||
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
|
||||
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
|
||||
# caption='Maquettes enregistrées',
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
@ -578,7 +578,7 @@ def _view_etuds_page(
|
|||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H.append(tab.html())
|
||||
|
||||
|
@ -678,7 +678,8 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
|||
sem_id = semset["sem_id"]
|
||||
csv_data = sco_etape_apogee.apo_csv_get(etape_apo, annee_scolaire, sem_id)
|
||||
if format == "raw":
|
||||
return scu.sendCSVFile(REQUEST, csv_data, etape_apo + ".txt")
|
||||
scu.send_file(csv_data, etape_apo, suffix=".txt", mime=scu.CSV_MIMETYPE)
|
||||
|
||||
apo_data = sco_apogee_csv.ApoData(csv_data, periode=semset["sem_id"])
|
||||
|
||||
(
|
||||
|
@ -746,14 +747,15 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
|||
rows=etuds,
|
||||
html_sortable=True,
|
||||
html_class="table_leftalign apo_maq_table",
|
||||
base_url="%s?etape_apo=%s&semset_id=%s" % (REQUEST.URL0, etape_apo, semset_id),
|
||||
base_url="%s?etape_apo=%s&semset_id=%s"
|
||||
% (request.base_url, etape_apo, semset_id),
|
||||
filename="students_" + etape_apo,
|
||||
caption="Etudiants Apogée en " + etape_apo,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H += [
|
||||
tab.html(),
|
||||
|
@ -768,7 +770,7 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
# called from Web
|
||||
# called from Web (GET)
|
||||
def apo_csv_export_results(
|
||||
semset_id,
|
||||
block_export_res_etape=False,
|
||||
|
|
|
@ -31,27 +31,21 @@
|
|||
# Ancien module "scolars"
|
||||
import os
|
||||
import time
|
||||
|
||||
from flask import url_for, g, request
|
||||
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
from email.mime.base import MIMEBase
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g, request
|
||||
from flask_mail import Message
|
||||
|
||||
from app import email
|
||||
from app import log
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import SCO_ENCODING
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
||||
|
||||
from app import log
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc import safehtml
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc.scolog import logdb
|
||||
from flask_mail import Message
|
||||
from app import mail
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
|
||||
MONTH_NAMES_ABBREV = [
|
||||
"Jan ",
|
||||
|
@ -256,7 +250,6 @@ _identiteEditor = ndb.EditableTable(
|
|||
"photo_filename",
|
||||
"code_ine",
|
||||
"code_nip",
|
||||
"scodoc7_id",
|
||||
),
|
||||
filter_dept=True,
|
||||
sortkey="nom",
|
||||
|
@ -345,31 +338,33 @@ def _check_duplicate_code(
|
|||
)
|
||||
if etudid:
|
||||
OK = "retour à la fiche étudiant"
|
||||
dest_url = "ficheEtud"
|
||||
dest_endpoint = "scolar.ficheEtud"
|
||||
parameters = {"etudid": etudid}
|
||||
else:
|
||||
if "tf_submitted" in args:
|
||||
del args["tf_submitted"]
|
||||
OK = "Continuer"
|
||||
dest_url = "etudident_create_form"
|
||||
dest_endpoint = "scolar.etudident_create_form"
|
||||
parameters = args
|
||||
else:
|
||||
OK = "Annuler"
|
||||
dest_url = ""
|
||||
dest_endpoint = "notes.index_html"
|
||||
parameters = {}
|
||||
if not disable_notify:
|
||||
err_page = scu.confirm_dialog(
|
||||
message="""<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name,
|
||||
helpmsg="""Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.<p><ul><li>"""
|
||||
% (code_name, args[code_name])
|
||||
+ "</li><li>".join(listh)
|
||||
+ "</li></ul><p>",
|
||||
OK=OK,
|
||||
dest_url=dest_url,
|
||||
parameters=parameters,
|
||||
)
|
||||
err_page = f"""<h3><h3>Code étudiant ({code_name}) dupliqué !</h3>
|
||||
<p class="help">Le {code_name} {args[code_name]} est déjà utilisé: un seul étudiant peut avoir
|
||||
ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.
|
||||
</p>
|
||||
<ul><li>
|
||||
{ '</li><li>'.join(listh) }
|
||||
</li></ul>
|
||||
<p>
|
||||
<a href="{ url_for(dest_endpoint, scodoc_dept=g.scodoc_dept, **parameters) }
|
||||
">{OK}</a>
|
||||
</p>
|
||||
"""
|
||||
else:
|
||||
err_page = """<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name
|
||||
err_page = f"""<h3>Code étudiant ({code_name}) dupliqué !</h3>"""
|
||||
log("*** error: code %s duplique: %s" % (code_name, args[code_name]))
|
||||
raise ScoGenError(err_page)
|
||||
|
||||
|
@ -456,7 +451,7 @@ def notify_etud_change(email_addr, etud, before, after, subject):
|
|||
log("notify_etud_change: sending notification to %s" % email_addr)
|
||||
log("notify_etud_change: subject: %s" % subject)
|
||||
log(txt)
|
||||
mail.send_email(
|
||||
email.send_email(
|
||||
subject, sco_preferences.get_preference("email_from_addr"), [email_addr], txt
|
||||
)
|
||||
return txt
|
||||
|
@ -559,7 +554,6 @@ _admissionEditor = ndb.EditableTable(
|
|||
"villelycee",
|
||||
"codepostallycee",
|
||||
"codelycee",
|
||||
"debouche",
|
||||
"type_admission",
|
||||
"boursier_prec",
|
||||
),
|
||||
|
@ -656,6 +650,12 @@ def make_etud_args(etudid=None, code_nip=None, use_request=True, raise_exc=True)
|
|||
return args
|
||||
|
||||
|
||||
def log_unknown_etud():
|
||||
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
|
||||
etud_args = make_etud_args(raise_exc=False)
|
||||
log(f"unknown student: args={etud_args}")
|
||||
|
||||
|
||||
def get_etud_info(etudid=False, code_nip=False, filled=False) -> list:
|
||||
"""infos sur un etudiant (API). If not foud, returns empty list.
|
||||
On peut specifier etudid ou code_nip
|
||||
|
|
|
@ -31,7 +31,7 @@ import datetime
|
|||
import operator
|
||||
import pprint
|
||||
import time
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
import flask
|
||||
from flask import url_for
|
||||
|
@ -274,13 +274,13 @@ def do_evaluation_create(
|
|||
if args["jour"]:
|
||||
next_eval = None
|
||||
t = (
|
||||
ndb.DateDMYtoISO(args["jour"]),
|
||||
ndb.TimetoISO8601(args["heure_debut"]),
|
||||
ndb.DateDMYtoISO(args["jour"], null_is_empty=True),
|
||||
ndb.TimetoISO8601(args["heure_debut"], null_is_empty=True),
|
||||
)
|
||||
for e in ModEvals:
|
||||
if (
|
||||
ndb.DateDMYtoISO(e["jour"]),
|
||||
ndb.TimetoISO8601(e["heure_debut"]),
|
||||
ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
|
||||
ndb.TimetoISO8601(e["heure_debut"], null_is_empty=True),
|
||||
) > t:
|
||||
next_eval = e
|
||||
break
|
||||
|
@ -780,7 +780,6 @@ def formsemestre_evaluations_cal(formsemestre_id, REQUEST=None):
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Evaluations du semestre",
|
||||
sem,
|
||||
cssstyles=["css/calabs.css"],
|
||||
|
@ -915,13 +914,13 @@ def formsemestre_evaluations_delai_correction(
|
|||
html_title="<h2>Correction des évaluations du semestre</h2>",
|
||||
caption="Correction des évaluations du semestre",
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
|
||||
origin="Généré par %s le " % sco_version.SCONAME
|
||||
+ scu.timedate_human_repr()
|
||||
+ "",
|
||||
filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def module_evaluation_insert_before(ModEvals, next_eval):
|
||||
|
@ -1089,7 +1088,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
|||
% (
|
||||
scu.ScoURL(),
|
||||
group_id,
|
||||
six.moves.urllib.parse.quote(E["jour"], safe=""),
|
||||
urllib.parse.quote(E["jour"], safe=""),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
|
@ -1346,7 +1345,7 @@ def evaluation_create_form(
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
cancelbutton="Annuler",
|
||||
|
|
|
@ -35,11 +35,11 @@ from enum import Enum
|
|||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import openpyxl.utils.datetime
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||
from openpyxl.comments import Comment
|
||||
from openpyxl import Workbook, load_workbook
|
||||
from openpyxl.cell import WriteOnlyCell
|
||||
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||
from openpyxl.comments import Comment
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb
|
||||
|
@ -59,24 +59,9 @@ class COLORS(Enum):
|
|||
LIGHT_YELLOW = "FFFFFF99"
|
||||
|
||||
|
||||
def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE):
|
||||
"""publication fichier.
|
||||
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
|
||||
"""
|
||||
filename = (
|
||||
scu.unescape_html(scu.suppress_accents(filename))
|
||||
.replace("&", "")
|
||||
.replace(" ", "_")
|
||||
)
|
||||
request.RESPONSE.setHeader("content-type", mime)
|
||||
request.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
|
||||
# font, border, number_format, fill, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||
# font, border, number_format, fill,...
|
||||
# (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||
|
||||
|
||||
def xldate_as_datetime(xldate, datemode=0):
|
||||
|
@ -86,6 +71,17 @@ def xldate_as_datetime(xldate, datemode=0):
|
|||
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
||||
|
||||
|
||||
def adjust_sheetname(sheet_name):
|
||||
"""Renvoie un nom convenable pour une feuille excel: < 31 cars, sans caractères spéciaux
|
||||
Le / n'est pas autorisé par exemple.
|
||||
Voir https://xlsxwriter.readthedocs.io/workbook.html#add_worksheet
|
||||
"""
|
||||
sheet_name = scu.make_filename(sheet_name)
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
return sheet_name[:31]
|
||||
|
||||
|
||||
class ScoExcelBook:
|
||||
"""Permet la génération d'un classeur xlsx composé de plusieurs feuilles.
|
||||
usage:
|
||||
|
@ -98,13 +94,16 @@ class ScoExcelBook:
|
|||
|
||||
def __init__(self):
|
||||
self.sheets = [] # list of sheets
|
||||
self.wb = Workbook(write_only=True)
|
||||
|
||||
def create_sheet(self, sheet_name="feuille", default_style=None):
|
||||
"""Crée une nouvelle feuille dans ce classeur
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut
|
||||
"""
|
||||
sheet = ScoExcelSheet(sheet_name, default_style)
|
||||
sheet_name = adjust_sheetname(sheet_name)
|
||||
ws = self.wb.create_sheet(sheet_name)
|
||||
sheet = ScoExcelSheet(sheet_name, default_style, ws)
|
||||
self.sheets.append(sheet)
|
||||
return sheet
|
||||
|
||||
|
@ -112,12 +111,12 @@ class ScoExcelBook:
|
|||
"""génération d'un stream binaire représentant la totalité du classeur.
|
||||
retourne le flux
|
||||
"""
|
||||
wb = Workbook(write_only=True)
|
||||
for sheet in self.sheets:
|
||||
sheet.generate(self)
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
sheet.prepare()
|
||||
# construction d'un flux
|
||||
# (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
with NamedTemporaryFile() as tmp:
|
||||
wb.save(tmp.name)
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
|
@ -125,6 +124,7 @@ class ScoExcelBook:
|
|||
def excel_make_style(
|
||||
bold=False,
|
||||
italic=False,
|
||||
outline=False,
|
||||
color: COLORS = COLORS.BLACK,
|
||||
bgcolor: COLORS = None,
|
||||
halign=None,
|
||||
|
@ -145,7 +145,14 @@ def excel_make_style(
|
|||
size -- taille de police
|
||||
"""
|
||||
style = {}
|
||||
font = Font(name=font_name, bold=bold, italic=italic, color=color.value, size=size)
|
||||
font = Font(
|
||||
name=font_name,
|
||||
bold=bold,
|
||||
italic=italic,
|
||||
outline=outline,
|
||||
color=color.value,
|
||||
size=size,
|
||||
)
|
||||
style["font"] = font
|
||||
if bgcolor:
|
||||
style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value)
|
||||
|
@ -182,41 +189,93 @@ class ScoExcelSheet:
|
|||
"""
|
||||
|
||||
def __init__(self, sheet_name="feuille", default_style=None, wb=None):
|
||||
"""Création de la feuille.
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut des cellules
|
||||
wb -- le WorkBook dans laquelle se trouve la feuille. Si wb est None (cas d'un classeur mono-feuille),
|
||||
un workbook est crée et associé à cette feuille.
|
||||
"""Création de la feuille. sheet_name
|
||||
-- le nom de la feuille default_style
|
||||
-- le style par défaut des cellules ws
|
||||
-- None si la feuille est autonome (dans ce cas ell crée son propre wb), sinon c'est la worksheet
|
||||
créée par le workbook propriétaire un workbook est crée et associé à cette feuille.
|
||||
"""
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
self.sheet_name = sheet_name[
|
||||
:31
|
||||
] # if len(sheet_name) > 31: sheet_name = 'Feuille' ?
|
||||
self.rows = [] # list of list of cells
|
||||
# self.cells_styles_lico = {} # { (li,co) : style }
|
||||
# self.cells_styles_li = {} # { li : style }
|
||||
# self.cells_styles_co = {} # { co : style }
|
||||
self.sheet_name = adjust_sheetname(sheet_name)
|
||||
if default_style is None:
|
||||
default_style = excel_make_style()
|
||||
self.default_style = default_style
|
||||
self.wb = wb or Workbook(write_only=True) # Création de workbook si nécessaire
|
||||
self.ws = self.wb.create_sheet(title=self.sheet_name)
|
||||
if wb is None:
|
||||
self.wb = Workbook()
|
||||
self.ws = self.wb.active
|
||||
self.ws.title = self.sheet_name
|
||||
else:
|
||||
self.wb = None
|
||||
self.ws = wb
|
||||
# internal data
|
||||
self.rows = [] # list of list of cells
|
||||
self.column_dimensions = {}
|
||||
self.row_dimensions = {}
|
||||
|
||||
def set_column_dimension_width(self, cle, value):
|
||||
"""Détermine la largeur d'une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
value -- la dimension (unité : 7 pixels comme affiché dans Excel)
|
||||
def excel_make_composite_style(
|
||||
self,
|
||||
alignment=None,
|
||||
border=None,
|
||||
fill=None,
|
||||
number_format=None,
|
||||
font=None,
|
||||
):
|
||||
style = {}
|
||||
if font is not None:
|
||||
style["font"] = font
|
||||
if alignment is not None:
|
||||
style["alignment"] = alignment
|
||||
if border is not None:
|
||||
style["border"] = border
|
||||
if fill is not None:
|
||||
style["fill"] = fill
|
||||
if number_format is None:
|
||||
style["number_format"] = FORMAT_GENERAL
|
||||
else:
|
||||
style["number_format"] = number_format
|
||||
return style
|
||||
|
||||
@staticmethod
|
||||
def i2col(idx):
|
||||
if idx < 26: # one letter key
|
||||
return chr(idx + 65)
|
||||
else: # two letters AA..ZZ
|
||||
first = (idx // 26) + 66
|
||||
second = (idx % 26) + 65
|
||||
return "" + chr(first) + chr(second)
|
||||
|
||||
def set_column_dimension_width(self, cle=None, value=21):
|
||||
"""Détermine la largeur d'une colonne. cle -- identifie la colonne ("A" "B", ... ou 0, 1, 2, ...) si None,
|
||||
value donne la liste des largeurs de colonnes depuis A, B, C, ... value -- la dimension (unité : 7 pixels
|
||||
comme affiché dans Excel)
|
||||
"""
|
||||
self.ws.column_dimensions[cle].width = value
|
||||
if cle is None:
|
||||
for i, val in enumerate(value):
|
||||
self.ws.column_dimensions[self.i2col(i)].width = val
|
||||
# No keys: value is a list of widths
|
||||
elif type(cle) == str: # accepts set_column_with("D", ...)
|
||||
self.ws.column_dimensions[cle].width = value
|
||||
else:
|
||||
self.ws.column_dimensions[self.i2col(cle)].width = value
|
||||
|
||||
def set_column_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
def set_row_dimension_height(self, cle=None, value=21):
|
||||
"""Détermine la hauteur d'une ligne. cle -- identifie la ligne (1, 2, ...) si None,
|
||||
value donne la liste des hauteurs de colonnes depuis 1, 2, 3, ... value -- la dimension
|
||||
"""
|
||||
if cle is None:
|
||||
for i, val in enumerate(value, start=1):
|
||||
self.ws.row_dimensions[i].height = val
|
||||
# No keys: value is a list of widths
|
||||
else:
|
||||
self.ws.row_dimensions[cle].height = value
|
||||
|
||||
def set_row_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une ligne.
|
||||
cle -- identifie la colonne (1...)
|
||||
value -- boolean (vrai = colonne cachée)
|
||||
"""
|
||||
self.ws.column_dimensions[cle].hidden = value
|
||||
self.ws.row_dimensions[cle].hidden = value
|
||||
|
||||
def make_cell(self, value: any = None, style=None, comment=None):
|
||||
"""Construit une cellule.
|
||||
|
@ -232,8 +291,12 @@ class ScoExcelSheet:
|
|||
style = self.default_style
|
||||
if "font" in style:
|
||||
cell.font = style["font"]
|
||||
if "alignment" in style:
|
||||
cell.alignment = style["alignment"]
|
||||
if "border" in style:
|
||||
cell.border = style["border"]
|
||||
if "fill" in style:
|
||||
cell.fill = style["fill"]
|
||||
if "number_format" in style:
|
||||
cell.number_format = style["number_format"]
|
||||
if "fill" in style:
|
||||
|
@ -272,73 +335,31 @@ class ScoExcelSheet:
|
|||
"""ajoute une ligne déjà construite à la feuille."""
|
||||
self.rows.append(row)
|
||||
|
||||
# def set_style(self, style=None, li=None, co=None):
|
||||
# if li is not None and co is not None:
|
||||
# self.cells_styles_lico[(li, co)] = style
|
||||
# elif li is None:
|
||||
# self.cells_styles_li[li] = style
|
||||
# elif co is None:
|
||||
# self.cells_styles_co[co] = style
|
||||
#
|
||||
# def get_cell_style(self, li, co):
|
||||
# """Get style for specified cell"""
|
||||
# return (
|
||||
# self.cells_styles_lico.get((li, co), None)
|
||||
# or self.cells_styles_li.get(li, None)
|
||||
# or self.cells_styles_co.get(co, None)
|
||||
# or self.default_style
|
||||
# )
|
||||
|
||||
def _generate_ws(self):
|
||||
def prepare(self):
|
||||
"""génére un flux décrivant la feuille.
|
||||
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
|
||||
ou pour la génération d'un classeur multi-feuilles
|
||||
"""
|
||||
for col in self.column_dimensions.keys():
|
||||
self.ws.column_dimensions[col] = self.column_dimensions[col]
|
||||
for row in self.column_dimensions.keys():
|
||||
self.ws.column_dimensions[row] = self.column_dimensions[row]
|
||||
for row in self.row_dimensions.keys():
|
||||
self.ws.row_dimensions[row] = self.row_dimensions[row]
|
||||
for row in self.rows:
|
||||
self.ws.append(row)
|
||||
|
||||
def generate_standalone(self):
|
||||
def generate(self):
|
||||
"""génération d'un classeur mono-feuille"""
|
||||
self._generate_ws()
|
||||
# this method makes sense only if it is a standalone worksheet (else call workbook.generate()
|
||||
if self.wb is None: # embeded sheet
|
||||
raise ScoValueError("can't generate a single sheet from a ScoWorkbook")
|
||||
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
self.prepare()
|
||||
with NamedTemporaryFile() as tmp:
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
def generate_embeded(self):
|
||||
"""generation d'une feuille include dans un classeur multi-feuilles"""
|
||||
self._generate_ws()
|
||||
|
||||
def gen_workbook(self, wb=None):
|
||||
"""TODO: à remplacer"""
|
||||
"""Generates and returns a workbook from stored data.
|
||||
If wb, add a sheet (tab) to the existing workbook (in this case, returns None).
|
||||
"""
|
||||
if wb is None:
|
||||
wb = Workbook() # Création du fichier
|
||||
sauvegarde = True
|
||||
else:
|
||||
sauvegarde = False
|
||||
ws0 = wb.add_sheet(self.sheet_name)
|
||||
li = 0
|
||||
for row in self.rows:
|
||||
co = 0
|
||||
for c in row:
|
||||
# safety net: allow only str, int and float
|
||||
# #py3 #sco8 A revoir lors de la ré-écriture de ce module
|
||||
# XXX if type(c) not in (IntType, FloatType):
|
||||
# c = str(c).decode(scu.SCO_ENCODING)
|
||||
ws0.write(li, co, c, self.get_cell_style(li, co))
|
||||
co += 1
|
||||
li += 1
|
||||
if sauvegarde:
|
||||
return wb.savetostr()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def excel_simple_table(
|
||||
titles=None, lines=None, sheet_name=b"feuille", titles_styles=None, comments=None
|
||||
|
@ -377,7 +398,7 @@ def excel_simple_table(
|
|||
cell_style = text_style
|
||||
cells.append(ws.make_cell(it, cell_style))
|
||||
ws.append_row(cells)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_feuille_saisie(e, titreannee, description, lines):
|
||||
|
@ -538,16 +559,28 @@ def excel_feuille_saisie(e, titreannee, description, lines):
|
|||
ws.make_cell("cellule vide -> note non modifiée", style_expl),
|
||||
]
|
||||
)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_bytes_to_list(bytes_content):
|
||||
filelike = io.BytesIO(bytes_content)
|
||||
return _excel_to_list(filelike)
|
||||
try:
|
||||
filelike = io.BytesIO(bytes_content)
|
||||
return _excel_to_list(filelike)
|
||||
except:
|
||||
raise ScoValueError("""
|
||||
scolars_import_excel_file: un contenu xlsx semble corrompu!
|
||||
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
|
||||
""")
|
||||
|
||||
|
||||
def excel_file_to_list(filename):
|
||||
return _excel_to_list(filename)
|
||||
try:
|
||||
return _excel_to_list(filename)
|
||||
except:
|
||||
raise ScoValueError("""
|
||||
scolars_import_excel_file: un contenu xlsx semble corrompu!
|
||||
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
|
||||
""")
|
||||
|
||||
|
||||
def _excel_to_list(filelike): # we may need 'encoding' argument ?
|
||||
|
@ -758,4 +791,4 @@ def excel_feuille_listeappel(
|
|||
cell_2 = ws.make_cell(("Liste éditée le " + dt), style1i)
|
||||
ws.append_row([None, cell_2])
|
||||
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
|
|
@ -55,11 +55,9 @@ class InvalidNoteValue(ScoException):
|
|||
|
||||
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
|
||||
class ScoValueError(ScoException):
|
||||
def __init__(self, msg, dest_url=None, REQUEST=None):
|
||||
def __init__(self, msg, dest_url=None):
|
||||
ScoException.__init__(self, msg)
|
||||
self.dest_url = dest_url
|
||||
if REQUEST and dest_url:
|
||||
REQUEST.set("dest_url", dest_url)
|
||||
|
||||
|
||||
class FormatError(ScoValueError):
|
||||
|
@ -79,7 +77,7 @@ class ScoConfigurationError(ScoValueError):
|
|||
|
||||
|
||||
class ScoLockedFormError(ScoException):
|
||||
def __init__(self, msg="", REQUEST=None):
|
||||
def __init__(self, msg=""):
|
||||
msg = (
|
||||
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
|
||||
+ str(msg)
|
||||
|
@ -90,7 +88,7 @@ class ScoLockedFormError(ScoException):
|
|||
class ScoGenError(ScoException):
|
||||
"exception avec affichage d'une page explicative ad-hoc"
|
||||
|
||||
def __init__(self, msg="", REQUEST=None):
|
||||
def __init__(self, msg=""):
|
||||
ScoException.__init__(self, msg)
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
"""Export d'une table avec les résultats de tous les étudiants
|
||||
"""
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -240,15 +240,13 @@ def scodoc_table_results(
|
|||
start_date_iso, end_date_iso, types_parcours
|
||||
)
|
||||
tab.base_url = "%s?start_date=%s&end_date=%s&types_parcours=%s" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
start_date,
|
||||
end_date,
|
||||
"&types_parcours=".join([str(x) for x in types_parcours]),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(
|
||||
format=format, with_html_headers=False, REQUEST=REQUEST
|
||||
)
|
||||
return tab.make_page(format=format, with_html_headers=False)
|
||||
tab_html = tab.html()
|
||||
nb_rows = tab.get_nb_rows()
|
||||
else:
|
||||
|
|
|
@ -404,6 +404,4 @@ def search_inscr_etud_by_nip(code_nip, REQUEST=None, format="json"):
|
|||
)
|
||||
tab = GenTable(columns_ids=columns_ids, rows=T)
|
||||
|
||||
return tab.make_page(
|
||||
format=format, with_html_headers=False, REQUEST=REQUEST, publish=True
|
||||
)
|
||||
return tab.make_page(format=format, with_html_headers=False, publish=True)
|
||||
|
|
|
@ -31,7 +31,8 @@ from operator import itemgetter
|
|||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
|
@ -92,9 +93,7 @@ def formation_has_locked_sems(formation_id):
|
|||
return sems
|
||||
|
||||
|
||||
def formation_export(
|
||||
formation_id, export_ids=False, export_tags=True, format=None, REQUEST=None
|
||||
):
|
||||
def formation_export(formation_id, export_ids=False, export_tags=True, format=None):
|
||||
"""Get a formation, with UE, matieres, modules
|
||||
in desired format
|
||||
"""
|
||||
|
@ -131,9 +130,7 @@ def formation_export(
|
|||
if mod["ects"] is None:
|
||||
del mod["ects"]
|
||||
|
||||
return scu.sendResult(
|
||||
REQUEST, F, name="formation", format=format, force_outer_xml_tag=False
|
||||
)
|
||||
return scu.sendResult(F, name="formation", format=format, force_outer_xml_tag=False, attached=True)
|
||||
|
||||
|
||||
def formation_import_xml(doc: str, import_tags=True):
|
||||
|
@ -162,20 +159,18 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||
D = sco_xml.xml_to_dicts(f)
|
||||
assert D[0] == "formation"
|
||||
F = D[1]
|
||||
F_quoted = F.copy()
|
||||
log("F=%s" % F)
|
||||
ndb.quote_dict(F_quoted)
|
||||
log("F_quoted=%s" % F_quoted)
|
||||
# F_quoted = F.copy()
|
||||
# ndb.quote_dict(F_quoted)
|
||||
F["dept_id"] = g.scodoc_dept_id
|
||||
# find new version number
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
log(
|
||||
"select max(version) from notes_formations where acronyme=%(acronyme)s and titre=%(titre)s"
|
||||
% F_quoted
|
||||
)
|
||||
cursor.execute(
|
||||
"select max(version) from notes_formations where acronyme=%(acronyme)s and titre=%(titre)s",
|
||||
F_quoted,
|
||||
"""SELECT max(version)
|
||||
FROM notes_formations
|
||||
WHERE acronyme=%(acronyme)s and titre=%(titre)s and dept_id=%(dept_id)s
|
||||
""",
|
||||
F,
|
||||
)
|
||||
res = cursor.fetchall()
|
||||
try:
|
||||
|
@ -196,7 +191,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||
assert ue_info[0] == "ue"
|
||||
ue_info[1]["formation_id"] = formation_id
|
||||
if "ue_id" in ue_info[1]:
|
||||
xml_ue_id = ue_info[1]["ue_id"]
|
||||
xml_ue_id = int(ue_info[1]["ue_id"])
|
||||
del ue_info[1]["ue_id"]
|
||||
else:
|
||||
xml_ue_id = None
|
||||
|
@ -212,7 +207,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||
for mod_info in mat_info[2]:
|
||||
assert mod_info[0] == "module"
|
||||
if "module_id" in mod_info[1]:
|
||||
xml_module_id = mod_info[1]["module_id"]
|
||||
xml_module_id = int(mod_info[1]["module_id"])
|
||||
del mod_info[1]["module_id"]
|
||||
else:
|
||||
xml_module_id = None
|
||||
|
@ -230,7 +225,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
|||
return formation_id, modules_old2new, ues_old2new
|
||||
|
||||
|
||||
def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
||||
def formation_list_table(formation_id=None, args={}):
|
||||
"""List formation, grouped by titre and sorted by versions
|
||||
and listing associated semestres
|
||||
returns a table
|
||||
|
@ -247,7 +242,7 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
|||
"edit_img", border="0", alt="modifier", title="Modifier titres et code"
|
||||
)
|
||||
|
||||
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
|
||||
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
||||
|
||||
# Traduit/ajoute des champs à afficher:
|
||||
for f in formations:
|
||||
|
@ -347,17 +342,18 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
|||
html_class="formation_list_table table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
|
||||
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
|
||||
page_title=title,
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
|
||||
def formation_create_new_version(formation_id, redirect=True, REQUEST=None):
|
||||
def formation_create_new_version(formation_id, redirect=True):
|
||||
"duplicate formation, with new version number"
|
||||
xml = formation_export(formation_id, export_ids=True, format="xml")
|
||||
new_id, modules_old2new, ues_old2new = formation_import_xml(xml)
|
||||
resp = formation_export(formation_id, export_ids=True, format="xml")
|
||||
xml_data = resp.get_data(as_text=True)
|
||||
new_id, modules_old2new, ues_old2new = formation_import_xml(xml_data)
|
||||
# news
|
||||
F = formation_list(args={"formation_id": new_id})[0]
|
||||
sco_news.add(
|
||||
|
|
|
@ -31,7 +31,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
|||
import time
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
|
||||
import app
|
||||
from app.models import Departement
|
||||
|
@ -61,6 +61,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||
"gestion_semestrielle",
|
||||
"etat",
|
||||
"bul_hide_xml",
|
||||
"block_moyennes",
|
||||
"bul_bgcolor",
|
||||
"modalite",
|
||||
"resp_can_edit",
|
||||
|
@ -68,7 +69,6 @@ _formsemestreEditor = ndb.EditableTable(
|
|||
"ens_can_edit_eval",
|
||||
"elt_sem_apo",
|
||||
"elt_annee_apo",
|
||||
"scodoc7_id",
|
||||
),
|
||||
filter_dept=True,
|
||||
sortkey="date_debut",
|
||||
|
@ -82,6 +82,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||
"etat": bool,
|
||||
"gestion_compensation": bool,
|
||||
"bul_hide_xml": bool,
|
||||
"block_moyennes": bool,
|
||||
"gestion_semestrielle": bool,
|
||||
"gestion_compensation": bool,
|
||||
"gestion_semestrielle": bool,
|
||||
|
@ -95,6 +96,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||
def get_formsemestre(formsemestre_id):
|
||||
"list ONE formsemestre"
|
||||
if not isinstance(formsemestre_id, int):
|
||||
raise ValueError()
|
||||
raise ScoValueError(
|
||||
"""Semestre invalide, reprenez l'opération au départ ou si le problème persiste signalez l'erreur sur scodoc-devel@listes.univ-paris13.fr"""
|
||||
)
|
||||
|
@ -565,7 +567,7 @@ def list_formsemestre_by_etape(etape_apo=False, annee_scolaire=False):
|
|||
return sems
|
||||
|
||||
|
||||
def view_formsemestre_by_etape(etape_apo=None, format="html", REQUEST=None):
|
||||
def view_formsemestre_by_etape(etape_apo=None, format="html"):
|
||||
"""Affiche table des semestres correspondants à l'étape"""
|
||||
if etape_apo:
|
||||
html_title = (
|
||||
|
@ -582,8 +584,8 @@ def view_formsemestre_by_etape(etape_apo=None, format="html", REQUEST=None):
|
|||
Etape: <input name="etape_apo" type="text" size="8"></input>
|
||||
</form>""",
|
||||
)
|
||||
tab.base_url = "%s?etape_apo=%s" % (REQUEST.URL0, etape_apo or "")
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
tab.base_url = "%s?etape_apo=%s" % (request.base_url, etape_apo or "")
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def sem_has_etape(sem, code_etape):
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"""Menu "custom" (défini par l'utilisateur) dans les semestres
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -84,9 +84,7 @@ def formsemestre_custommenu_edit(formsemestre_id, REQUEST=None):
|
|||
scu.NotesURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||
)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Modification du menu du semestre ", sem
|
||||
),
|
||||
html_sco_header.html_sem_header("Modification du menu du semestre ", sem),
|
||||
"""<p class="help">Ce menu, spécifique à chaque semestre, peut être utilisé pour placer des liens vers vos applications préférées.</p>
|
||||
<p class="help">Procédez en plusieurs fois si vous voulez ajouter plusieurs items.</p>""",
|
||||
]
|
||||
|
@ -119,7 +117,7 @@ def formsemestre_custommenu_edit(formsemestre_id, REQUEST=None):
|
|||
initvalues["title_" + str(item["custommenu_id"])] = item["title"]
|
||||
initvalues["url_" + str(item["custommenu_id"])] = item["url"]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"""Form choix modules / responsables et creation formsemestre
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
from app.auth.models import User
|
||||
|
||||
|
@ -91,7 +91,6 @@ def formsemestre_editwithmodules(REQUEST, formsemestre_id):
|
|||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Modification du semestre",
|
||||
sem,
|
||||
javascripts=["libjs/AutoSuggest.js"],
|
||||
|
@ -501,6 +500,14 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
|||
"labels": [""],
|
||||
},
|
||||
),
|
||||
(
|
||||
"block_moyennes",
|
||||
{
|
||||
"input_type": "boolcheckbox",
|
||||
"title": "Bloquer moyennes",
|
||||
"explanation": "empêcher le calcul des moyennes d'UE et générale.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"sep",
|
||||
{
|
||||
|
@ -653,7 +660,7 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
|||
|
||||
#
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
modform,
|
||||
submitlabel=submitlabel,
|
||||
|
@ -693,7 +700,6 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
|||
tf[2]["bul_hide_xml"] = False
|
||||
else:
|
||||
tf[2]["bul_hide_xml"] = True
|
||||
|
||||
# remap les identifiants de responsables:
|
||||
tf[2]["responsable_id"] = User.get_user_id_from_nomplogin(
|
||||
tf[2]["responsable_id"]
|
||||
|
@ -888,7 +894,6 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Copie du semestre",
|
||||
sem,
|
||||
javascripts=["libjs/AutoSuggest.js"],
|
||||
|
@ -959,7 +964,7 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
submitlabel="Dupliquer ce semestre",
|
||||
|
@ -1113,10 +1118,11 @@ def do_formsemestre_clone(
|
|||
def formsemestre_associate_new_version(
|
||||
formsemestre_id,
|
||||
other_formsemestre_ids=[],
|
||||
REQUEST=None,
|
||||
dialog_confirmed=False,
|
||||
):
|
||||
"""Formulaire changement formation d'un semestre"""
|
||||
formsemestre_id = int(formsemestre_id)
|
||||
other_formsemestre_ids = [int(x) for x in other_formsemestre_ids]
|
||||
if not dialog_confirmed:
|
||||
# dresse le liste des semestres de la meme formation et version
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
@ -1161,15 +1167,19 @@ def formsemestre_associate_new_version(
|
|||
)
|
||||
else:
|
||||
do_formsemestres_associate_new_version(
|
||||
[formsemestre_id] + other_formsemestre_ids, REQUEST=REQUEST
|
||||
[formsemestre_id] + other_formsemestre_ids
|
||||
)
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s&head_message=Formation%%20dupliquée"
|
||||
% formsemestre_id
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
head_message="Formation dupliquée",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
||||
def do_formsemestres_associate_new_version(formsemestre_ids):
|
||||
"""Cree une nouvelle version de la formation du semestre, et y rattache les semestres.
|
||||
Tous les moduleimpl sont ré-associés à la nouvelle formation, ainsi que les decisions de jury
|
||||
si elles existent (codes d'UE validées).
|
||||
|
@ -1179,9 +1189,11 @@ def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
|||
if not formsemestre_ids:
|
||||
return
|
||||
# Check: tous de la même formation
|
||||
assert isinstance(formsemestre_ids[0], int)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_ids[0])
|
||||
formation_id = sem["formation_id"]
|
||||
for formsemestre_id in formsemestre_ids[1:]:
|
||||
assert isinstance(formsemestre_id, int)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if formation_id != sem["formation_id"]:
|
||||
raise ScoValueError("les semestres ne sont pas tous de la même formation !")
|
||||
|
@ -1192,9 +1204,7 @@ def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
|||
formation_id,
|
||||
modules_old2new,
|
||||
ues_old2new,
|
||||
) = sco_formations.formation_create_new_version(
|
||||
formation_id, redirect=False, REQUEST=REQUEST
|
||||
)
|
||||
) = sco_formations.formation_create_new_version(formation_id, redirect=False)
|
||||
|
||||
for formsemestre_id in formsemestre_ids:
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
@ -1235,7 +1245,7 @@ def formsemestre_delete(formsemestre_id, REQUEST=None):
|
|||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Suppression du semestre", sem),
|
||||
html_sco_header.html_sem_header("Suppression du semestre", sem),
|
||||
"""<div class="ue_warning"><span>Attention !</span>
|
||||
<p class="help">A n'utiliser qu'en cas d'erreur lors de la saisie d'une formation. Normalement,
|
||||
<b>un semestre ne doit jamais être supprimé</b> (on perd la mémoire des notes et de tous les événements liés à ce semestre !).</p>
|
||||
|
@ -1261,7 +1271,7 @@ def formsemestre_delete(formsemestre_id, REQUEST=None):
|
|||
else:
|
||||
submit_label = "Confirmer la suppression du semestre"
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("formsemestre_id", {"input_type": "hidden"}),),
|
||||
initvalues=F,
|
||||
|
@ -1421,7 +1431,7 @@ def do_formsemestre_delete(formsemestre_id):
|
|||
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
def formsemestre_edit_options(formsemestre_id, target_url=None, REQUEST=None):
|
||||
def formsemestre_edit_options(formsemestre_id, REQUEST=None):
|
||||
"""dialog to change formsemestre options
|
||||
(accessible par ScoImplement ou dir. etudes)
|
||||
"""
|
||||
|
@ -1538,9 +1548,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
|||
</p>
|
||||
"""
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Coefficients des UE du semestre", sem
|
||||
),
|
||||
html_sco_header.html_sem_header("Coefficients des UE du semestre", sem),
|
||||
help,
|
||||
]
|
||||
#
|
||||
|
@ -1576,7 +1584,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
|||
form.append(("ue_" + str(ue["ue_id"]), descr))
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
submitlabel="Changer les coefficients",
|
||||
|
@ -1652,9 +1660,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
|||
formsemestre_id=formsemestre_id
|
||||
) # > modif coef UE cap (modifs notes de _certains_ etudiants)
|
||||
|
||||
header = html_sco_header.html_sem_header(
|
||||
REQUEST, "Coefficients des UE du semestre", sem
|
||||
)
|
||||
header = html_sco_header.html_sem_header("Coefficients des UE du semestre", sem)
|
||||
return (
|
||||
header
|
||||
+ "\n".join(z)
|
||||
|
|
|
@ -34,7 +34,7 @@ Ces semestres n'auront qu'un seul inscrit !
|
|||
import time
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -181,7 +181,7 @@ def formsemestre_ext_create_form(etudid, formsemestre_id, REQUEST=None):
|
|||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
|
@ -231,7 +231,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
|||
else:
|
||||
initvalues = {}
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cssclass="tf_ext_edit_ue_validations",
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
import time
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
|
@ -334,7 +334,6 @@ def formsemestre_inscription_with_modules(
|
|||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Inscription de %s dans ce semestre" % etud["nomprenom"],
|
||||
sem,
|
||||
)
|
||||
|
@ -415,7 +414,7 @@ def formsemestre_inscription_with_modules(
|
|||
<input type="hidden" name="etudid" value="%s">
|
||||
<input type="hidden" name="formsemestre_id" value="%s">
|
||||
"""
|
||||
% (REQUEST.URL0, etudid, formsemestre_id)
|
||||
% (request.base_url, etudid, formsemestre_id)
|
||||
)
|
||||
|
||||
H.append(sco_groups.form_group_choice(formsemestre_id, allow_none=True))
|
||||
|
@ -533,7 +532,7 @@ function chkbx_select(field_id, state) {
|
|||
"""
|
||||
)
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
initvalues,
|
||||
|
@ -763,7 +762,6 @@ def formsemestre_inscrits_ailleurs(formsemestre_id, REQUEST=None):
|
|||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Inscriptions multiples parmi les étudiants du semestre ",
|
||||
sem,
|
||||
)
|
||||
|
|
|
@ -436,7 +436,7 @@ def formsemestre_status_menubar(sem):
|
|||
return "\n".join(H)
|
||||
|
||||
|
||||
def retreive_formsemestre_from_request():
|
||||
def retreive_formsemestre_from_request() -> int:
|
||||
"""Cherche si on a de quoi déduire le semestre affiché à partir des
|
||||
arguments de la requête:
|
||||
formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id
|
||||
|
@ -482,7 +482,7 @@ def retreive_formsemestre_from_request():
|
|||
else:
|
||||
return None # no current formsemestre
|
||||
|
||||
return formsemestre_id
|
||||
return int(formsemestre_id)
|
||||
|
||||
|
||||
# Element HTML decrivant un semestre (barre de menu et infos)
|
||||
|
@ -698,10 +698,10 @@ def formsemestre_description_table(formsemestre_id, REQUEST=None, with_evals=Fal
|
|||
html_caption=title,
|
||||
html_class="table_leftalign formsemestre_description",
|
||||
base_url="%s?formsemestre_id=%s&with_evals=%s"
|
||||
% (REQUEST.URL0, formsemestre_id, with_evals),
|
||||
% (request.base_url, formsemestre_id, with_evals),
|
||||
page_title=title,
|
||||
html_title=html_sco_header.html_sem_header(
|
||||
REQUEST, "Description du semestre", sem, with_page_header=False
|
||||
"Description du semestre", sem, with_page_header=False
|
||||
),
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
|
@ -721,14 +721,14 @@ def formsemestre_description(
|
|||
tab.html_before_table = """<form name="f" method="get" action="%s">
|
||||
<input type="hidden" name="formsemestre_id" value="%s"></input>
|
||||
<input type="checkbox" name="with_evals" value="1" onchange="document.f.submit()" """ % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
)
|
||||
if with_evals:
|
||||
tab.html_before_table += "checked"
|
||||
tab.html_before_table += ">indiquer les évaluations</input></form>"
|
||||
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
# genere liste html pour accès aux groupes de ce semestre
|
||||
|
@ -912,12 +912,12 @@ def formsemestre_status_head(formsemestre_id=None, REQUEST=None, page_title=None
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, page_title, sem, with_page_header=False, with_h2=False
|
||||
page_title, sem, with_page_header=False, with_h2=False
|
||||
),
|
||||
"""<table>
|
||||
f"""<table>
|
||||
<tr><td class="fichetitre2">Formation: </td><td>
|
||||
<a href="Notes/ue_list?formation_id=%(formation_id)s" class="discretelink" title="Formation %(acronyme)s, v%(version)s">%(titre)s</a>"""
|
||||
% F,
|
||||
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept, formation_id=F['formation_id'])}"
|
||||
class="discretelink" title="Formation {F['acronyme']}, v{F['version']}">{F['titre']}</a>""",
|
||||
]
|
||||
if sem["semestre_id"] >= 0:
|
||||
H.append(", %s %s" % (parcours.SESSION_NAME, sem["semestre_id"]))
|
||||
|
@ -948,10 +948,13 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
|
|||
</td></tr>"""
|
||||
)
|
||||
H.append("</table>")
|
||||
sem_warning = ""
|
||||
if sem["bul_hide_xml"]:
|
||||
H.append(
|
||||
'<p class="fontorange"><em>Bulletins non publiés sur le portail</em></p>'
|
||||
)
|
||||
sem_warning += "Bulletins non publiés sur le portail. "
|
||||
if sem["block_moyennes"]:
|
||||
sem_warning += "Calcul des moyennes bloqué !"
|
||||
if sem_warning:
|
||||
H.append('<p class="fontorange"><em>' + sem_warning + "</em></p>")
|
||||
if sem["semestre_id"] >= 0 and not sco_formsemestre.sem_une_annee(sem):
|
||||
H.append(
|
||||
'<p class="fontorange"><em>Attention: ce semestre couvre plusieurs années scolaires !</em></p>'
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -149,9 +149,7 @@ def formsemestre_validation_etud_form(
|
|||
'</td><td style="text-align: right;"><a href="%s">%s</a></td></tr></table>'
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de %s" % etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de %s" % etud["nom"]),
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -356,7 +354,7 @@ def formsemestre_validation_etud(
|
|||
#
|
||||
Se.valide_decision(selected_choice, REQUEST) # enregistre
|
||||
return _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol, REQUEST
|
||||
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol
|
||||
)
|
||||
|
||||
|
||||
|
@ -402,19 +400,17 @@ def formsemestre_validation_etud_manu(
|
|||
Se.valide_decision(choice, REQUEST) # enregistre
|
||||
if redirect:
|
||||
return _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol
|
||||
)
|
||||
|
||||
|
||||
def _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
|
||||
):
|
||||
def _redirect_valid_choice(formsemestre_id, etudid, Se, choice, desturl, sortcol):
|
||||
adr = "formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1" % (
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
)
|
||||
if sortcol:
|
||||
adr += "&sortcol=" + sortcol
|
||||
adr += "&sortcol=" + str(sortcol)
|
||||
# if desturl:
|
||||
# desturl += "&desturl=" + desturl
|
||||
return flask.redirect(adr)
|
||||
|
@ -826,7 +822,7 @@ def formsemestre_validation_auto(formsemestre_id, REQUEST):
|
|||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Saisie automatique des décisions du semestre", sem
|
||||
"Saisie automatique des décisions du semestre", sem
|
||||
),
|
||||
"""
|
||||
<ul>
|
||||
|
@ -994,9 +990,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
|||
'</td><td style="text-align: right;"><a href="%s">%s</a></td></tr></table>'
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de %s" % etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de %s" % etud["nom"]),
|
||||
)
|
||||
),
|
||||
"""<p class="help">Utiliser cette page pour enregistrer une UE validée antérieurement,
|
||||
|
@ -1017,7 +1011,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
|||
ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues]
|
||||
ue_ids = [""] + [ue["ue_id"] for ue in ues]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
|
|
|
@ -42,7 +42,7 @@ from xml.etree import ElementTree
|
|||
from xml.etree.ElementTree import Element
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask import url_for
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -476,7 +476,7 @@ def formsemestre_partition_list(formsemestre_id, format="xml", REQUEST=None):
|
|||
# Ajoute les groupes
|
||||
for p in partitions:
|
||||
p["group"] = get_partition_groups(p)
|
||||
return scu.sendResult(REQUEST, partitions, name="partition", format=format)
|
||||
return scu.sendResult(partitions, name="partition", format=format)
|
||||
|
||||
|
||||
# Encore utilisé par groupmgr.js
|
||||
|
@ -1079,7 +1079,7 @@ def partition_rename(partition_id, REQUEST=None):
|
|||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
H = ["<h2>Renommer une partition</h2>"]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("partition_id", {"default": partition_id, "input_type": "hidden"}),
|
||||
|
@ -1188,7 +1188,7 @@ def group_rename(group_id, REQUEST=None):
|
|||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
H = ["<h2>Renommer un groupe de %s</h2>" % group["partition_name"]]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("group_id", {"default": group_id, "input_type": "hidden"}),
|
||||
|
@ -1268,7 +1268,7 @@ def groups_auto_repartition(partition_id=None, REQUEST=None):
|
|||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
{},
|
||||
|
@ -1499,7 +1499,7 @@ def _sortgroups(groups):
|
|||
# Tri: place 'all' en tête, puis groupe par partition / nom de groupe
|
||||
R = [g for g in groups if g["partition_name"] is None]
|
||||
o = [g for g in groups if g["partition_name"] != None]
|
||||
o.sort(key=lambda x: (x["numero"], x["group_name"]))
|
||||
o.sort(key=lambda x: (x["numero"] or 0, x["group_name"]))
|
||||
|
||||
return R + o
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,7 @@
|
|||
import datetime
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -81,6 +81,7 @@ def list_authorized_etuds_by_sem(sem, delai=274):
|
|||
"title": src["titreannee"],
|
||||
"title_target": "formsemestre_status?formsemestre_id=%s"
|
||||
% src["formsemestre_id"],
|
||||
"filename": "etud_autorises",
|
||||
},
|
||||
}
|
||||
# ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest.
|
||||
|
@ -99,6 +100,7 @@ def list_authorized_etuds_by_sem(sem, delai=274):
|
|||
% sem["formsemestre_id"],
|
||||
"comment": " actuellement inscrits dans ce semestre",
|
||||
"help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.",
|
||||
"filename": "etud_inscrits",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -146,16 +148,15 @@ def list_inscrits_date(sem):
|
|||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"])
|
||||
cursor.execute(
|
||||
"""SELECT I.etudid
|
||||
FROM
|
||||
notes_formsemestre_inscription ins,
|
||||
notes_formsemestre S,
|
||||
identite i
|
||||
"""SELECT ins.etudid
|
||||
FROM
|
||||
notes_formsemestre_inscription ins,
|
||||
notes_formsemestre S
|
||||
WHERE ins.formsemestre_id = S.id
|
||||
AND S.id != %(formsemestre_id)s
|
||||
AND S.date_debut <= %(date_debut_iso)s
|
||||
AND S.date_fin >= %(date_debut_iso)s
|
||||
AND ins.dept_id = %(dept_id)
|
||||
AND S.dept_id = %(dept_id)s
|
||||
""",
|
||||
sem,
|
||||
)
|
||||
|
@ -413,9 +414,9 @@ def build_page(
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Passages dans le semestre", sem, with_page_header=False
|
||||
"Passages dans le semestre", sem, with_page_header=False
|
||||
),
|
||||
"""<form method="post" action="%s">""" % REQUEST.URL0,
|
||||
"""<form method="post" action="%s">""" % request.base_url,
|
||||
"""<input type="hidden" name="formsemestre_id" value="%(formsemestre_id)s"/>
|
||||
<input type="submit" name="submitted" value="Appliquer les modifications"/>
|
||||
<a href="#help">aide</a>
|
||||
|
@ -507,7 +508,12 @@ def etuds_select_boxes(
|
|||
</script>
|
||||
<div class="etuds_select_boxes">"""
|
||||
] # "
|
||||
|
||||
# Élimine les boites vides:
|
||||
auth_etuds_by_cat = {
|
||||
k: auth_etuds_by_cat[k]
|
||||
for k in auth_etuds_by_cat
|
||||
if auth_etuds_by_cat[k]["etuds"]
|
||||
}
|
||||
for src_cat in auth_etuds_by_cat.keys():
|
||||
infos = auth_etuds_by_cat[src_cat]["infos"]
|
||||
infos["comment"] = infos.get("comment", "") # commentaire dans sous-titre boite
|
||||
|
@ -550,10 +556,8 @@ def etuds_select_boxes(
|
|||
if with_checkbox or sel_inscrits:
|
||||
H.append(")")
|
||||
if base_url and etuds:
|
||||
H.append(
|
||||
'<a href="%s&export_cat_xls=%s">%s</a> '
|
||||
% (base_url, src_cat, scu.ICON_XLS)
|
||||
)
|
||||
url = scu.build_url_query(base_url, export_cat_xls=src_cat)
|
||||
H.append(f'<a href="{url}">{scu.ICON_XLS}</a> ')
|
||||
H.append("</div>")
|
||||
for etud in etuds:
|
||||
if etud.get("inscrit", False):
|
||||
|
@ -633,4 +637,4 @@ def etuds_select_box_xls(src_cat):
|
|||
caption="%(title)s. %(help)s" % src_cat["infos"],
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
return tab.excel()
|
||||
return tab.excel() # tab.make_page(filename=src_cat["infos"]["filename"])
|
||||
|
|
|
@ -27,11 +27,11 @@
|
|||
|
||||
"""Liste des notes d'une évaluation
|
||||
"""
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
from operator import itemgetter
|
||||
import urllib
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -177,7 +177,7 @@ def do_evaluation_listenotes(REQUEST):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cancelbutton=None,
|
||||
|
@ -482,7 +482,7 @@ def _make_table_notes(
|
|||
# html_generate_cells=False # la derniere ligne (moyennes) est incomplete
|
||||
)
|
||||
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
|
||||
|
@ -778,9 +778,7 @@ def evaluation_check_absences_html(
|
|||
|
||||
if with_header:
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Vérification absences à l'évaluation"
|
||||
),
|
||||
html_sco_header.html_sem_header("Vérification absences à l'évaluation"),
|
||||
sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
|
||||
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
|
||||
]
|
||||
|
@ -817,8 +815,8 @@ def evaluation_check_absences_html(
|
|||
'<a class="stdlink" href="Absences/doSignaleAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s&moduleimpl_id=%s">signaler cette absence</a>'
|
||||
% (
|
||||
etud["etudid"],
|
||||
six.moves.urllib.parse.quote(E["jour"]),
|
||||
six.moves.urllib.parse.quote(E["jour"]),
|
||||
urllib.parse.quote(E["jour"]),
|
||||
urllib.parse.quote(E["jour"]),
|
||||
demijournee,
|
||||
E["moduleimpl_id"],
|
||||
)
|
||||
|
@ -866,7 +864,6 @@ def formsemestre_check_absences_html(formsemestre_id, REQUEST=None):
|
|||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Vérification absences aux évaluations de ce semestre",
|
||||
sem,
|
||||
),
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
"""
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -84,8 +84,8 @@ def scodoc_table_etuds_lycees(format="html", REQUEST=None):
|
|||
sco_preferences.SemPreferences(),
|
||||
no_links=True,
|
||||
)
|
||||
tab.base_url = REQUEST.URL0
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
tab.base_url = request.base_url
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
H = [
|
||||
|
@ -187,12 +187,12 @@ def formsemestre_etuds_lycees(
|
|||
tab, etuds_by_lycee = formsemestre_table_etuds_lycees(
|
||||
formsemestre_id, only_primo=only_primo, group_lycees=not no_grouping
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=1"
|
||||
if no_grouping:
|
||||
tab.base_url += "&no_grouping=1"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
F = [
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
from operator import itemgetter
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -137,7 +138,7 @@ def moduleimpl_inscriptions_edit(
|
|||
|
||||
</script>"""
|
||||
)
|
||||
H.append("""<form method="post" id="mi_form" action="%s">""" % REQUEST.URL0)
|
||||
H.append("""<form method="post" id="mi_form" action="%s">""" % request.base_url)
|
||||
H.append(
|
||||
"""
|
||||
<input type="hidden" name="moduleimpl_id" value="%(moduleimpl_id)s"/>
|
||||
|
@ -250,7 +251,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
|
|||
tous sauf <liste d'au plus 7 noms>
|
||||
|
||||
"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||
|
@ -285,9 +286,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
|
|||
mod["nb_inscrits"] = nb_inscrits
|
||||
options.append(mod)
|
||||
# Page HTML:
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Inscriptions aux modules du semestre")
|
||||
]
|
||||
H = [html_sco_header.html_sem_header("Inscriptions aux modules du semestre")]
|
||||
|
||||
H.append("<h3>Inscrits au semestre: %d étudiants</h3>" % len(inscrits))
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"""Tableau de bord module
|
||||
"""
|
||||
import time
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
|
@ -64,7 +64,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
|
||||
if (
|
||||
sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
)
|
||||
and nbnotes != 0
|
||||
):
|
||||
|
@ -80,7 +80,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
|
||||
current_user, E["moduleimpl_id"]
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -90,7 +90,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
},
|
||||
"enabled": nbnotes == 0
|
||||
and sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -111,7 +111,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
|
@ -128,16 +128,15 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
|||
"args": {
|
||||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": nbnotes == 0
|
||||
and sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
current_user, E["moduleimpl_id"]
|
||||
),
|
||||
},
|
||||
{
|
||||
"title": "Absences ce jour",
|
||||
"endpoint": "absences.EtatAbsencesDate",
|
||||
"args": {
|
||||
"date": six.moves.urllib.parse.quote(E["jour"], safe=""),
|
||||
"date": urllib.parse.quote(E["jour"], safe=""),
|
||||
"group_ids": group_id,
|
||||
},
|
||||
"enabled": E["jour"],
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
"""
|
||||
from flask import url_for, g
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -144,7 +145,7 @@ def _menuScolarite(authuser, sem, etudid):
|
|||
|
||||
def ficheEtud(etudid=None, REQUEST=None):
|
||||
"fiche d'informations sur un etudiant"
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
cnx = ndb.GetDBConnexion()
|
||||
if etudid and REQUEST:
|
||||
# la sidebar est differente s'il y a ou pas un etudid
|
||||
|
@ -167,7 +168,7 @@ def ficheEtud(etudid=None, REQUEST=None):
|
|||
info["info_naissance"] += " à " + info["lieu_naissance"]
|
||||
if info["dept_naissance"]:
|
||||
info["info_naissance"] += " (%s)" % info["dept_naissance"]
|
||||
info["etudfoto"] = sco_photos.etud_photo_html(etud, REQUEST=REQUEST)
|
||||
info["etudfoto"] = sco_photos.etud_photo_html(etud)
|
||||
if (
|
||||
(not info["domicile"])
|
||||
and (not info["codepostaldomicile"])
|
||||
|
@ -491,7 +492,7 @@ def menus_etud(REQUEST=None):
|
|||
"""Menu etudiant (operations sur l'etudiant)"""
|
||||
if "etudid" not in REQUEST.form:
|
||||
return ""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
|
||||
|
@ -539,9 +540,7 @@ def etud_info_html(etudid, with_photo="1", REQUEST=None, debug=False):
|
|||
formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request()
|
||||
with_photo = int(with_photo)
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
photo_html = sco_photos.etud_photo_html(
|
||||
etud, title="fiche de " + etud["nom"], REQUEST=REQUEST
|
||||
)
|
||||
photo_html = sco_photos.etud_photo_html(etud, title="fiche de " + etud["nom"])
|
||||
# experimental: may be too slow to be here
|
||||
etud["codeparcours"], etud["decisions_jury"] = sco_report.get_codeparcoursetud(
|
||||
etud, prefix="S", separator=", "
|
||||
|
|
|
@ -588,7 +588,6 @@ class SituationEtudParcoursGeneric(object):
|
|||
self.etudid,
|
||||
decision.code_etat,
|
||||
decision.assiduite,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
# -- modification du code du semestre precedent
|
||||
if self.prev and decision.new_code_prev:
|
||||
|
@ -619,7 +618,6 @@ class SituationEtudParcoursGeneric(object):
|
|||
self.etudid,
|
||||
decision.new_code_prev,
|
||||
decision.assiduite, # attention: en toute rigueur il faudrait utiliser une indication de l'assiduite au sem. precedent, que nous n'avons pas...
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
sco_cache.invalidate_formsemestre(
|
||||
|
@ -897,9 +895,7 @@ def formsemestre_update_validation_sem(
|
|||
return to_invalidate
|
||||
|
||||
|
||||
def formsemestre_validate_ues(
|
||||
formsemestre_id, etudid, code_etat_sem, assiduite, REQUEST=None
|
||||
):
|
||||
def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite):
|
||||
"""Enregistre codes UE, selon état semestre.
|
||||
Les codes UE sont toujours calculés ici, et non passés en paramètres
|
||||
car ils ne dépendent que de la note d'UE et de la validation ou non du semestre.
|
||||
|
@ -933,14 +929,13 @@ def formsemestre_validate_ues(
|
|||
cnx, nt, formsemestre_id, etudid, ue_id, code_ue
|
||||
)
|
||||
|
||||
if REQUEST:
|
||||
logdb(
|
||||
cnx,
|
||||
method="validate_ue",
|
||||
etudid=etudid,
|
||||
msg="ue_id=%s code=%s" % (ue_id, code_ue),
|
||||
commit=False,
|
||||
)
|
||||
logdb(
|
||||
cnx,
|
||||
method="validate_ue",
|
||||
etudid=etudid,
|
||||
msg="ue_id=%s code=%s" % (ue_id, code_ue),
|
||||
commit=False,
|
||||
)
|
||||
cnx.commit()
|
||||
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ Les images sont servies par ScoDoc, via la méthode getphotofile?etudid=xxx
|
|||
|
||||
"""
|
||||
|
||||
from app.scodoc.sco_exceptions import ScoGenError
|
||||
import datetime
|
||||
import glob
|
||||
import io
|
||||
|
@ -52,6 +53,7 @@ import requests
|
|||
import time
|
||||
import traceback
|
||||
|
||||
import PIL
|
||||
from PIL import Image as PILImage
|
||||
|
||||
from flask import request, g
|
||||
|
@ -172,7 +174,7 @@ def etud_photo_is_local(etud, size="small"):
|
|||
return photo_pathname(etud, size=size)
|
||||
|
||||
|
||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small", REQUEST=None):
|
||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small"):
|
||||
"""HTML img tag for the photo, either in small size (h90)
|
||||
or original size (size=="orig")
|
||||
"""
|
||||
|
@ -209,9 +211,7 @@ def etud_photo_orig_html(etud=None, etudid=None, title=None, REQUEST=None):
|
|||
Full-size images are always stored locally in the filesystem.
|
||||
They are the original uploaded images, converted in jpeg.
|
||||
"""
|
||||
return etud_photo_html(
|
||||
etud=etud, etudid=etudid, title=title, size="orig", REQUEST=REQUEST
|
||||
)
|
||||
return etud_photo_html(etud=etud, etudid=etudid, title=title, size="orig")
|
||||
|
||||
|
||||
def photo_pathname(etud, size="orig"):
|
||||
|
@ -246,7 +246,10 @@ def store_photo(etud, data):
|
|||
filesize = len(data)
|
||||
if filesize < 10 or filesize > MAX_FILE_SIZE:
|
||||
return 0, "Fichier image de taille invalide ! (%d)" % filesize
|
||||
filename = save_image(etud["etudid"], data)
|
||||
try:
|
||||
filename = save_image(etud["etudid"], data)
|
||||
except PIL.UnidentifiedImageError:
|
||||
raise ScoGenError(msg="Fichier d'image invalide ou non format non supporté")
|
||||
# update database:
|
||||
etud["photo_filename"] = filename
|
||||
etud["foto"] = None
|
||||
|
@ -298,6 +301,7 @@ def save_image(etudid, data):
|
|||
filename = get_new_filename(etudid)
|
||||
path = os.path.join(PHOTO_DIR, filename)
|
||||
log("saving %dx%d jpeg to %s" % (img.size[0], img.size[1], path))
|
||||
img = img.convert("RGB")
|
||||
img.save(path + IMAGE_EXT, format="JPEG", quality=92)
|
||||
# resize:
|
||||
img = scale_height(img)
|
||||
|
@ -341,7 +345,7 @@ def find_new_dir():
|
|||
|
||||
def copy_portal_photo_to_fs(etud):
|
||||
"""Copy the photo from portal (distant website) to local fs.
|
||||
Returns rel. path or None if copy failed, with a diagnotic message
|
||||
Returns rel. path or None if copy failed, with a diagnostic message
|
||||
"""
|
||||
sco_etud.format_etud_ident(etud)
|
||||
url = photo_portal_url(etud)
|
||||
|
@ -353,11 +357,12 @@ def copy_portal_photo_to_fs(etud):
|
|||
log("copy_portal_photo_to_fs: getting %s" % url)
|
||||
r = requests.get(url, timeout=portal_timeout)
|
||||
except:
|
||||
log("download failed: exception:\n%s" % traceback.format_exc())
|
||||
log("called from:\n" + "".join(traceback.format_stack()))
|
||||
# log("download failed: exception:\n%s" % traceback.format_exc())
|
||||
# log("called from:\n" + "".join(traceback.format_stack()))
|
||||
log("copy_portal_photo_to_fs: error.")
|
||||
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
|
||||
if r.status_code != 200:
|
||||
log("download failed")
|
||||
log(f"copy_portal_photo_to_fs: download failed {r.status_code }")
|
||||
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
|
||||
data = r.content # image bytes
|
||||
try:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,7 @@ Recapitule tous les semestres validés dans une feuille excel.
|
|||
"""
|
||||
import collections
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_abs
|
||||
|
@ -211,12 +211,11 @@ def formsemestre_poursuite_report(formsemestre_id, format="html", REQUEST=None):
|
|||
)
|
||||
tab.caption = "Récapitulatif %s." % sem["titreannee"]
|
||||
tab.html_caption = "Récapitulatif %s." % sem["titreannee"]
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
return tab.make_page(
|
||||
title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
|
||||
init_qtip=True,
|
||||
javascripts=["js/etud_info.js"],
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=True,
|
||||
)
|
||||
|
|
|
@ -111,7 +111,8 @@ get_base_preferences(formsemestre_id)
|
|||
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.models import Departement
|
||||
from app.scodoc import sco_cache
|
||||
|
@ -180,7 +181,7 @@ def _convert_pref_type(p, pref_spec):
|
|||
|
||||
def _get_pref_default_value_from_config(name, pref_spec):
|
||||
"""get default value store in application level config.
|
||||
If not found, use defalut value hardcoded in pref_spec.
|
||||
If not found, use default value hardcoded in pref_spec.
|
||||
"""
|
||||
# XXX va changer avec la nouvelle base
|
||||
# search in scu.CONFIG
|
||||
|
@ -1408,7 +1409,7 @@ class BasePreferences(object):
|
|||
{
|
||||
"initvalue": 1,
|
||||
"title": "Indique si les bulletins sont publiés",
|
||||
"explanation": "décocher si vous n'avez pas de portal étudiant publiant les bulletins",
|
||||
"explanation": "décocher si vous n'avez pas de portail étudiant publiant les bulletins",
|
||||
"input_type": "boolcheckbox",
|
||||
"labels": ["non", "oui"],
|
||||
"category": "bul",
|
||||
|
@ -1891,7 +1892,7 @@ class BasePreferences(object):
|
|||
|
||||
def get(self, formsemestre_id, name):
|
||||
"""Returns preference value.
|
||||
If global_lookup, when no value defined for this semestre, returns global value.
|
||||
when no value defined for this semestre, returns global value.
|
||||
"""
|
||||
params = {
|
||||
"dept_id": self.dept_id,
|
||||
|
@ -1901,7 +1902,7 @@ class BasePreferences(object):
|
|||
cnx = ndb.GetDBConnexion()
|
||||
plist = self._editor.list(cnx, params)
|
||||
if not plist:
|
||||
del params["formsemestre_id"]
|
||||
params["formsemestre_id"] = None
|
||||
plist = self._editor.list(cnx, params)
|
||||
if not plist:
|
||||
return self.default[name]
|
||||
|
@ -2022,14 +2023,16 @@ class BasePreferences(object):
|
|||
html_sco_header.sco_header(page_title="Préférences"),
|
||||
"<h2>Préférences globales pour %s</h2>" % scu.ScoURL(),
|
||||
f"""<p><a href="{url_for("scolar.config_logos", scodoc_dept=g.scodoc_dept)
|
||||
}">modification des logos du département (pour documents pdf)</a></p>""",
|
||||
}">modification des logos du département (pour documents pdf)</a></p>"""
|
||||
if current_user.is_administrator()
|
||||
else "",
|
||||
"""<p class="help">Ces paramètres s'appliquent par défaut à tous les semestres, sauf si ceux-ci définissent des valeurs spécifiques.</p>
|
||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||
""",
|
||||
]
|
||||
form = self.build_tf_form()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
initvalues=self.prefs[None],
|
||||
|
@ -2151,7 +2154,7 @@ class SemPreferences(object):
|
|||
) # a bug !
|
||||
sem = sco_formsemestre.get_formsemestre(self.formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Préférences du semestre", sem),
|
||||
html_sco_header.html_sem_header("Préférences du semestre", sem),
|
||||
"""
|
||||
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
|
||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||
|
@ -2194,7 +2197,7 @@ function set_global_pref(el, pref_name) {
|
|||
form.append(("destination", {"input_type": "hidden"}))
|
||||
form.append(("formsemestre_id", {"input_type": "hidden"}))
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
initvalues=self,
|
||||
|
@ -2245,7 +2248,7 @@ function set_global_pref(el, pref_name) {
|
|||
return flask.redirect(dest_url + "&head_message=Préférences modifiées")
|
||||
elif destination == "again":
|
||||
return flask.redirect(
|
||||
REQUEST.URL0 + "?formsemestre_id=" + str(self.formsemestre_id)
|
||||
request.base_url + "?formsemestre_id=" + str(self.formsemestre_id)
|
||||
)
|
||||
elif destination == "global":
|
||||
return flask.redirect(scu.ScoURL() + "/edit_preferences")
|
||||
|
@ -2253,7 +2256,7 @@ function set_global_pref(el, pref_name) {
|
|||
|
||||
#
|
||||
def doc_preferences():
|
||||
""" Liste les preferences en MarkDown, pour la documentation"""
|
||||
"""Liste les preferences en MarkDown, pour la documentation"""
|
||||
L = []
|
||||
for cat, cat_descr in PREF_CATEGORIES:
|
||||
L.append([""])
|
||||
|
|
|
@ -31,6 +31,9 @@ import time
|
|||
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00
|
||||
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_groups
|
||||
|
@ -318,9 +321,14 @@ def feuille_preparation_jury(formsemestre_id, REQUEST):
|
|||
% (
|
||||
sco_version.SCONAME,
|
||||
time.strftime("%d/%m/%Y"),
|
||||
REQUEST.BASE0,
|
||||
REQUEST.AUTHENTICATED_USER,
|
||||
request.url_root,
|
||||
current_user,
|
||||
)
|
||||
)
|
||||
xls = ws.generate_standalone()
|
||||
return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")
|
||||
xls = ws.generate()
|
||||
return scu.send_file(
|
||||
xls,
|
||||
f"PrepaJury{sn}",
|
||||
scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
|
|
|
@ -52,7 +52,7 @@ from reportlab.platypus import Paragraph
|
|||
from reportlab.lib import styles
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -535,13 +535,11 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True, REQUEST=No
|
|||
return tab.make_page(
|
||||
format=format,
|
||||
with_html_headers=False,
|
||||
REQUEST=REQUEST,
|
||||
publish=publish,
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Décisions du jury pour le semestre",
|
||||
sem,
|
||||
init_qtip=True,
|
||||
|
@ -628,7 +626,6 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Edition du PV de jury %s" % etuddescr,
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
|
@ -658,7 +655,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
|||
else:
|
||||
menu_choix_groupe = "" # un seul etudiant à editer
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
|
@ -707,7 +704,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
|||
else:
|
||||
groups_filename = ""
|
||||
filename = "PV-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
def descrform_pvjury(sem):
|
||||
|
@ -806,8 +803,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Edition des lettres individuelles",
|
||||
"Édition des lettres individuelles",
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
|
@ -827,7 +823,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
|
@ -868,7 +864,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
|||
dt = time.strftime("%Y-%m-%d")
|
||||
groups_filename = "-" + groups_infos.groups_filename
|
||||
filename = "lettres-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
def descrform_lettres_individuelles():
|
||||
|
|
|
@ -27,10 +27,13 @@
|
|||
|
||||
"""Tableau recapitulatif des notes d'un semestre
|
||||
"""
|
||||
import time
|
||||
import datetime
|
||||
import json
|
||||
import time
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from flask import request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.scodoc import html_sco_header
|
||||
|
@ -100,7 +103,7 @@ def formsemestre_recapcomplet(
|
|||
sco_formsemestre_status.formsemestre_status_head(
|
||||
formsemestre_id=formsemestre_id, REQUEST=REQUEST
|
||||
),
|
||||
'<form name="f" method="get" action="%s">' % REQUEST.URL0,
|
||||
'<form name="f" method="get" action="%s">' % request.base_url,
|
||||
'<input type="hidden" name="formsemestre_id" value="%s"></input>'
|
||||
% formsemestre_id,
|
||||
'<input type="hidden" name="pref_override" value="0"></input>',
|
||||
|
@ -227,11 +230,14 @@ def do_formsemestre_recapcomplet(
|
|||
if format == "xml" or format == "html":
|
||||
return data
|
||||
elif format == "csv":
|
||||
return scu.sendCSVFile(REQUEST, data, filename)
|
||||
elif format[:3] == "xls":
|
||||
return sco_excel.send_excel_file(REQUEST, data, filename)
|
||||
return scu.send_file(data, filename=filename, mime=scu.CSV_MIMETYPE)
|
||||
elif format.startswith("xls") or format.startswith("xlsx"):
|
||||
return scu.send_file(data, filename=filename, mime=scu.XLSX_MIMETYPE)
|
||||
elif format == "json":
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
js = json.dumps(data, indent=1, cls=scu.ScoDocJSONEncoder)
|
||||
return scu.send_file(
|
||||
js, filename=filename, suffix=scu.JSON_SUFFIX, mime=scu.JSON_MIMETYPE
|
||||
)
|
||||
else:
|
||||
raise ValueError("unknown format %s" % format)
|
||||
|
||||
|
@ -967,4 +973,4 @@ def formsemestres_bulletins(annee_scolaire, REQUEST=None):
|
|||
)
|
||||
jslist.append(J)
|
||||
|
||||
return scu.sendJSON(REQUEST, jslist)
|
||||
return scu.sendJSON(jslist)
|
||||
|
|
|
@ -37,7 +37,7 @@ import time
|
|||
import datetime
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
import pydot
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -247,7 +247,7 @@ def formsemestre_report(
|
|||
sem["titreannee"],
|
||||
)
|
||||
tab.html_caption = "Répartition des résultats par %s." % category_name
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=on"
|
||||
return tab
|
||||
|
@ -322,7 +322,7 @@ def formsemestre_report_counts(
|
|||
F = [
|
||||
"""<form name="f" method="get" action="%s"><p>
|
||||
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
||||
% REQUEST.URL0
|
||||
% request.base_url
|
||||
]
|
||||
for k in keys:
|
||||
if k == result:
|
||||
|
@ -355,7 +355,6 @@ def formsemestre_report_counts(
|
|||
t = tab.make_page(
|
||||
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=False,
|
||||
)
|
||||
if format != "html":
|
||||
|
@ -718,15 +717,15 @@ def formsemestre_suivi_cohorte(
|
|||
)
|
||||
tab.base_url = (
|
||||
"%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s"
|
||||
% (REQUEST.URL0, formsemestre_id, percent, bac, bacspecialite, civilite)
|
||||
% (request.base_url, formsemestre_id, percent, bac, bacspecialite, civilite)
|
||||
)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=on"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
|
||||
base_url = REQUEST.URL0
|
||||
base_url = request.base_url
|
||||
burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % (
|
||||
base_url,
|
||||
formsemestre_id,
|
||||
|
@ -816,7 +815,7 @@ def _gen_form_selectetuds(
|
|||
<p>Bac: <select name="bac" onchange="javascript: submit(this);">
|
||||
<option value="" %s>tous</option>
|
||||
"""
|
||||
% (REQUEST.URL0, selected)
|
||||
% (request.base_url, selected)
|
||||
]
|
||||
for b in bacs:
|
||||
if bac == b:
|
||||
|
@ -1167,7 +1166,7 @@ def table_suivi_parcours(formsemestre_id, only_primo=False, grouped_parcours=Tru
|
|||
|
||||
def tsp_form_primo_group(REQUEST, only_primo, no_grouping, formsemestre_id, format):
|
||||
"""Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees"""
|
||||
F = ["""<form name="f" method="get" action="%s">""" % REQUEST.URL0]
|
||||
F = ["""<form name="f" method="get" action="%s">""" % request.base_url]
|
||||
if only_primo:
|
||||
checked = 'checked="1"'
|
||||
else:
|
||||
|
@ -1205,12 +1204,12 @@ def formsemestre_suivi_parcours(
|
|||
only_primo=only_primo,
|
||||
grouped_parcours=not no_grouping,
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=1"
|
||||
if no_grouping:
|
||||
tab.base_url += "&no_grouping=1"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
F = [
|
||||
|
@ -1492,7 +1491,7 @@ def formsemestre_graph_parcours(
|
|||
statut=statut,
|
||||
)
|
||||
filename = scu.make_filename("flux " + sem["titreannee"])
|
||||
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
|
||||
return scu.sendPDFFile(doc, filename + ".pdf")
|
||||
elif format == "png":
|
||||
#
|
||||
(
|
||||
|
|
|
@ -9,48 +9,50 @@ from app.scodoc.sco_permissions import Permission as p
|
|||
SCO_ROLES_DEFAULTS = {
|
||||
"Observateur": (p.ScoObservateur,),
|
||||
"Ens": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoEnsView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoAbsChange,
|
||||
p.ScoEnsView,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
"Secr": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoAbsChange,
|
||||
p.ScoEditApo,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
# Admin est le chef du département, pas le "super admin"
|
||||
# on doit donc lister toutes ses permissions:
|
||||
"Admin": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoEnsView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoAbsChange,
|
||||
p.ScoChangeFormation,
|
||||
p.ScoEditFormationTags,
|
||||
p.ScoEditAllNotes,
|
||||
p.ScoChangePreferences,
|
||||
p.ScoEditAllEvals,
|
||||
p.ScoImplement,
|
||||
p.ScoEditAllNotes,
|
||||
p.ScoEditApo,
|
||||
p.ScoEditFormationTags,
|
||||
p.ScoEnsView,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoEtudChangeGroups,
|
||||
p.ScoEtudInscrit,
|
||||
p.ScoImplement,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersAdmin,
|
||||
p.ScoChangePreferences,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
# RespPE est le responsable poursuites d'études
|
||||
# il peut ajouter des tags sur les formations:
|
||||
|
|
|
@ -35,7 +35,7 @@ import datetime
|
|||
import psycopg2
|
||||
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -168,7 +168,7 @@ def do_evaluation_upload_xls(REQUEST):
|
|||
"""
|
||||
Soumission d'un fichier XLS (evaluation_id, notefile)
|
||||
"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
evaluation_id = int(REQUEST.form["evaluation_id"])
|
||||
comment = REQUEST.form["comment"]
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
|
@ -494,9 +494,10 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
|||
}
|
||||
ndb.quote_dict(aa)
|
||||
cursor.execute(
|
||||
"""INSERT INTO notes_notes
|
||||
(etudid,evaluation_id,value,comment,date,uid)
|
||||
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)""",
|
||||
"""INSERT INTO notes_notes
|
||||
(etudid, evaluation_id, value, comment, date, uid)
|
||||
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)
|
||||
""",
|
||||
aa,
|
||||
)
|
||||
changed = True
|
||||
|
@ -515,10 +516,10 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
|||
# recopie l'ancienne note dans notes_notes_log, puis update
|
||||
if do_it:
|
||||
cursor.execute(
|
||||
"""INSERT INTO notes_notes_log
|
||||
"""INSERT INTO notes_notes_log
|
||||
(etudid,evaluation_id,value,comment,date,uid)
|
||||
SELECT etudid, evaluation_id, value, comment, date, uid
|
||||
FROM notes_notes
|
||||
FROM notes_notes
|
||||
WHERE etudid=%(etudid)s
|
||||
and evaluation_id=%(evaluation_id)s
|
||||
""",
|
||||
|
@ -536,8 +537,8 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
|||
if value != scu.NOTES_SUPPRESS:
|
||||
if do_it:
|
||||
cursor.execute(
|
||||
"""UPDATE notes_notes
|
||||
SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
|
||||
"""UPDATE notes_notes
|
||||
SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
|
||||
WHERE etudid = %(etudid)s
|
||||
and evaluation_id = %(evaluation_id)s
|
||||
""",
|
||||
|
@ -550,7 +551,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
|||
% (evaluation_id, etudid, oldval)
|
||||
)
|
||||
cursor.execute(
|
||||
"""DELETE FROM notes_notes
|
||||
"""DELETE FROM notes_notes
|
||||
WHERE etudid = %(etudid)s
|
||||
AND evaluation_id = %(evaluation_id)s
|
||||
""",
|
||||
|
@ -589,18 +590,17 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
|||
|
||||
def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
"""Saisie des notes via un fichier Excel"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authusername = str(authuser)
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = evals[0]
|
||||
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
||||
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>"
|
||||
% current_user.user_name
|
||||
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
|
||||
avez l'autorisation d'effectuer cette opération)</p>
|
||||
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
|
||||
|
@ -657,7 +657,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
|||
)
|
||||
|
||||
nf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
|
||||
|
@ -711,7 +711,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
|||
#
|
||||
H.append("""</div><h3>Autres opérations</h3><ul>""")
|
||||
if sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
):
|
||||
H.append(
|
||||
"""
|
||||
|
@ -829,7 +829,8 @@ def feuille_saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
|||
|
||||
filename = "notes_%s_%s.xlsx" % (evalname, gr_title_filename)
|
||||
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
|
||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
||||
# return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||
|
||||
|
||||
def has_existing_decision(M, E, etudid):
|
||||
|
@ -858,9 +859,7 @@ def has_existing_decision(M, E, etudid):
|
|||
|
||||
def saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
"""Formulaire saisie notes d'une évaluation pour un groupe"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authusername = str(authuser)
|
||||
|
||||
group_ids = [int(group_id) for group_id in group_ids]
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
|
@ -871,10 +870,11 @@ def saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
|||
formsemestre_id = M["formsemestre_id"]
|
||||
# Check access
|
||||
# (admin, respformation, and responsable_id)
|
||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
||||
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>"
|
||||
% current_user.user_name
|
||||
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
|
||||
avez l'autorisation d'effectuer cette opération)</p>
|
||||
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
|
||||
|
@ -1154,9 +1154,9 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
|||
"attributes": [
|
||||
'class="note%s"' % classdem,
|
||||
disabled_attr,
|
||||
"data-last-saved-value=%s" % e["val"],
|
||||
"data-orig-value=%s" % e["val"],
|
||||
"data-etudid=%s" % etudid,
|
||||
'data-last-saved-value="%s"' % e["val"],
|
||||
'data-orig-value="%s"' % e["val"],
|
||||
'data-etudid="%s"' % etudid,
|
||||
],
|
||||
"template": """<tr%(item_dom_attr)s class="etud_elem """
|
||||
+ " ".join(etud_classes)
|
||||
|
@ -1222,7 +1222,7 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
|||
|
||||
def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=None):
|
||||
"""Enregistre une note (ajax)"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
log(
|
||||
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
|
||||
% (evaluation_id, etudid, authuser, value)
|
||||
|
@ -1259,7 +1259,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=N
|
|||
else:
|
||||
result["history_menu"] = "" # no update needed
|
||||
result["status"] = "ok"
|
||||
return scu.sendJSON(REQUEST, result)
|
||||
return scu.sendJSON(result)
|
||||
|
||||
|
||||
def get_note_history_menu(evaluation_id, etudid):
|
||||
|
@ -1290,7 +1290,7 @@ def get_note_history_menu(evaluation_id, etudid):
|
|||
nv = "" # ne repete pas la valeur de la note courante
|
||||
else:
|
||||
# ancienne valeur
|
||||
nv = '<span class="histvalue">: %s</span>' % dispnote
|
||||
nv = ": %s" % dispnote
|
||||
first = False
|
||||
if i["comment"]:
|
||||
comment = ' <span class="histcomment">%s</span>' % i["comment"]
|
||||
|
|
|
@ -171,7 +171,7 @@ class SemSet(dict):
|
|||
def remove(self, formsemestre_id):
|
||||
ndb.SimpleQuery(
|
||||
"""DELETE FROM notes_semset_formsemestre
|
||||
WHERE id=%(semset_id)s
|
||||
WHERE semset_id=%(semset_id)s
|
||||
AND formsemestre_id=%(formsemestre_id)s
|
||||
""",
|
||||
{"formsemestre_id": formsemestre_id, "semset_id": self.semset_id},
|
||||
|
@ -418,7 +418,7 @@ def do_semset_remove_sem(semset_id, formsemestre_id):
|
|||
# ----------------------------------------
|
||||
|
||||
|
||||
def semset_page(format="html", REQUEST=None):
|
||||
def semset_page(format="html"):
|
||||
"""Page avec liste semsets:
|
||||
Table avec : date_debut date_fin titre liste des semestres
|
||||
"""
|
||||
|
@ -468,7 +468,7 @@ def semset_page(format="html", REQUEST=None):
|
|||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
page_title = "Ensembles de semestres"
|
||||
H = [
|
||||
|
|
|
@ -32,7 +32,7 @@ import time
|
|||
import pprint
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import g, url_for, send_file
|
||||
from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -112,6 +112,7 @@ def formsemestre_synchro_etuds(
|
|||
base_url = url_for(
|
||||
"notes.formsemestre_synchro_etuds",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
anneeapogee=anneeapogee or None, # si None, le param n'est pas dans l'URL
|
||||
)
|
||||
|
||||
|
@ -146,11 +147,11 @@ def formsemestre_synchro_etuds(
|
|||
base_url=base_url,
|
||||
read_only=read_only,
|
||||
)
|
||||
return send_file(
|
||||
return scu.send_file(
|
||||
xls,
|
||||
mimetype=scu.XLS_MIMETYPE,
|
||||
download_name=scu.sanitize_filename(filename + scu.XLSX_SUFFIX),
|
||||
as_attachment=True,
|
||||
mime=scu.XLS_MIMETYPE,
|
||||
filename=filename,
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
)
|
||||
|
||||
H = [header]
|
||||
|
@ -317,7 +318,8 @@ def build_page(
|
|||
"""
|
||||
% sem,
|
||||
"""
|
||||
Année Apogée: <select id="anneeapogee" name="anneeapogee" onchange="document.location='formsemestre_synchro_etuds?formsemestre_id=%s&anneeapogee='+document.getElementById('anneeapogee').value">"""
|
||||
Année Apogée: <select id="anneeapogee" name="anneeapogee"
|
||||
onchange="document.location='formsemestre_synchro_etuds?formsemestre_id=%s&anneeapogee='+document.getElementById('anneeapogee').value">"""
|
||||
% (sem["formsemestre_id"]),
|
||||
"\n".join(options),
|
||||
"""
|
||||
|
@ -430,17 +432,20 @@ def list_synch(sem, anneeapogee=None):
|
|||
return etuds
|
||||
|
||||
#
|
||||
r = {
|
||||
"etuds_ok": {
|
||||
"etuds": set_to_sorted_list(etuds_ok, is_inscrit=True),
|
||||
boites = {
|
||||
"etuds_a_importer": {
|
||||
"etuds": set_to_sorted_list(a_importer, is_inscrit=True, etud_apo=True),
|
||||
"infos": {
|
||||
"id": "etuds_ok",
|
||||
"title": "Etudiants dans Apogée et déjà inscrits",
|
||||
"help": "Ces etudiants sont inscrits dans le semestre ScoDoc et sont présents dans Apogée: tout est donc correct. Décocher les étudiants que vous souhaitez désinscrire.",
|
||||
"id": "etuds_a_importer",
|
||||
"title": "Etudiants dans Apogée à importer",
|
||||
"help": """Ces étudiants sont inscrits dans cette étape Apogée mais ne sont pas connus par ScoDoc:
|
||||
cocher les noms à importer et inscrire puis appuyer sur le bouton "Appliquer".""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"etud_key": EKEY_APO, # clé à stocker dans le formulaire html
|
||||
"filename": "etuds_a_importer",
|
||||
},
|
||||
"nomprenoms": etudsapo_ident,
|
||||
},
|
||||
"etuds_noninscrits": {
|
||||
"etuds": set_to_sorted_list(etuds_noninscrits, is_inscrit=True),
|
||||
|
@ -453,20 +458,9 @@ def list_synch(sem, anneeapogee=None):
|
|||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_non_inscrits",
|
||||
},
|
||||
},
|
||||
"etuds_a_importer": {
|
||||
"etuds": set_to_sorted_list(a_importer, is_inscrit=True, etud_apo=True),
|
||||
"infos": {
|
||||
"id": "etuds_a_importer",
|
||||
"title": "Etudiants dans Apogée à importer",
|
||||
"help": """Ces étudiants sont inscrits dans cette étape Apogée mais ne sont pas connus par ScoDoc: cocher les noms à importer et inscrire puis appuyer sur le bouton "Appliquer".""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_APO, # clé à stocker dans le formulaire html
|
||||
},
|
||||
"nomprenoms": etudsapo_ident,
|
||||
},
|
||||
"etuds_nonapogee": {
|
||||
"etuds": set_to_sorted_list(etuds_nonapogee, is_inscrit=True),
|
||||
"infos": {
|
||||
|
@ -478,6 +472,7 @@ def list_synch(sem, anneeapogee=None):
|
|||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_non_apogee",
|
||||
},
|
||||
},
|
||||
"inscrits_without_key": {
|
||||
|
@ -489,11 +484,25 @@ def list_synch(sem, anneeapogee=None):
|
|||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"checkbox_name": "inscrits_without_key",
|
||||
"filename": "inscrits_without_key",
|
||||
},
|
||||
},
|
||||
"etuds_ok": {
|
||||
"etuds": set_to_sorted_list(etuds_ok, is_inscrit=True),
|
||||
"infos": {
|
||||
"id": "etuds_ok",
|
||||
"title": "Etudiants dans Apogée et déjà inscrits",
|
||||
"help": """Ces etudiants sont inscrits dans le semestre ScoDoc et sont présents dans Apogée:
|
||||
tout est donc correct. Décocher les étudiants que vous souhaitez désinscrire.""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_inscrits_ok_apo",
|
||||
},
|
||||
},
|
||||
}
|
||||
return (
|
||||
r,
|
||||
boites,
|
||||
a_importer,
|
||||
etuds_noninscrits,
|
||||
inscrits_set,
|
||||
|
|
|
@ -214,7 +214,7 @@ def module_tag_search(term, REQUEST=None):
|
|||
)
|
||||
data = [x["title"] for x in r]
|
||||
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
return scu.sendJSON(data)
|
||||
|
||||
|
||||
def module_tag_list(module_id=""):
|
||||
|
|
|
@ -44,7 +44,7 @@ from reportlab.lib import colors
|
|||
from PIL import Image as PILImage
|
||||
|
||||
import flask
|
||||
from flask import url_for, g, send_file
|
||||
from flask import url_for, g, send_file, request
|
||||
|
||||
from app import log
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -104,7 +104,7 @@ def _trombino_html_header():
|
|||
return html_sco_header.sco_header(javascripts=["js/trombino.js"])
|
||||
|
||||
|
||||
def trombino_html(groups_infos, REQUEST=None):
|
||||
def trombino_html(groups_infos):
|
||||
"HTML snippet for trombino (with title and menu)"
|
||||
menuTrombi = [
|
||||
{
|
||||
|
@ -150,7 +150,7 @@ def trombino_html(groups_infos, REQUEST=None):
|
|||
% t["etudid"]
|
||||
)
|
||||
if sco_photos.etud_photo_is_local(t, size="small"):
|
||||
foto = sco_photos.etud_photo_html(t, title="", REQUEST=REQUEST)
|
||||
foto = sco_photos.etud_photo_html(t, title="")
|
||||
else: # la photo n'est pas immédiatement dispo
|
||||
foto = (
|
||||
'<span class="unloaded_img" id="%s"><img border="0" height="90" alt="en cours" src="/ScoDoc/static/icons/loading.jpg"/></span>'
|
||||
|
@ -466,7 +466,7 @@ def _listeappel_photos_pdf(groups_infos, REQUEST):
|
|||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
||||
|
||||
# --------------------- Upload des photos de tout un groupe
|
||||
|
@ -486,7 +486,10 @@ def photos_generate_excel_sample(group_ids=[], REQUEST=None):
|
|||
],
|
||||
extra_cols=["fichier_photo"],
|
||||
)
|
||||
return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
||||
return scu.send_file(
|
||||
data, "ImportPhotos", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||
)
|
||||
# return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
||||
|
||||
|
||||
def photos_import_files_form(group_ids=[], REQUEST=None):
|
||||
|
@ -516,7 +519,7 @@ def photos_import_files_form(group_ids=[], REQUEST=None):
|
|||
F = html_sco_header.sco_footer()
|
||||
REQUEST.form["group_ids"] = groups_infos.group_ids
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),
|
||||
|
|
|
@ -272,7 +272,7 @@ def pdf_trombino_tours(
|
|||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
||||
|
||||
# Feuille d'absences en pdf avec photos:
|
||||
|
@ -466,4 +466,4 @@ def pdf_feuille_releve_absences(
|
|||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
|
|
@ -54,6 +54,7 @@ Solution proposée (nov 2014):
|
|||
|
||||
"""
|
||||
import flask
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
|
@ -168,7 +169,7 @@ def external_ue_inscrit_et_note(
|
|||
)
|
||||
# Saisie des notes
|
||||
_, _, _ = sco_saisie_notes._notes_add(
|
||||
REQUEST.AUTHENTICATED_USER,
|
||||
current_user,
|
||||
evaluation_id,
|
||||
list(notes_etuds.items()),
|
||||
do_it=True,
|
||||
|
@ -221,7 +222,6 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Ajout d'une UE externe pour %(nomprenom)s" % etud,
|
||||
sem,
|
||||
javascripts=["js/sco_ue_external.js"],
|
||||
|
@ -248,7 +248,7 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
|||
default_label = "Aucune UE externe existante"
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("formsemestre_id", {"input_type": "hidden"}),
|
||||
|
|
|
@ -46,6 +46,8 @@ Opérations:
|
|||
"""
|
||||
|
||||
import datetime
|
||||
from flask import request
|
||||
|
||||
from app.scodoc.intervals import intervalmap
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
@ -167,7 +169,7 @@ def evaluation_list_operations(evaluation_id, REQUEST=None):
|
|||
% (E["description"], E["jour"]),
|
||||
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
|
||||
)
|
||||
return tab.make_page(REQUEST=REQUEST)
|
||||
return tab.make_page()
|
||||
|
||||
|
||||
def formsemestre_list_saisies_notes(formsemestre_id, format="html", REQUEST=None):
|
||||
|
@ -217,12 +219,12 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html", REQUEST=None
|
|||
html_sortable=True,
|
||||
caption="Saisies de notes dans %s" % sem["titreannee"],
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
|
||||
origin="Généré par %s le " % sco_version.SCONAME
|
||||
+ scu.timedate_human_repr()
|
||||
+ "",
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def get_note_history(evaluation_id, etudid, REQUEST=None, fmt=""):
|
||||
|
@ -261,7 +263,7 @@ def get_note_history(evaluation_id, etudid, REQUEST=None, fmt=""):
|
|||
x["user_name"] = sco_users.user_info(x["uid"])["nomcomplet"]
|
||||
|
||||
if fmt == "json":
|
||||
return scu.sendJSON(REQUEST, history)
|
||||
return scu.sendJSON(history)
|
||||
else:
|
||||
return history
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
# Anciennement ZScoUsers.py, fonctions de gestion des données réécrite avec flask/SQLAlchemy
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import cracklib # pylint: disable=import-error
|
||||
|
@ -51,11 +51,7 @@ import app.scodoc.sco_utils as scu
|
|||
|
||||
from app.scodoc.sco_exceptions import (
|
||||
AccessDenied,
|
||||
ScoException,
|
||||
ScoValueError,
|
||||
ScoInvalidDateError,
|
||||
ScoLockedFormError,
|
||||
ScoGenError,
|
||||
)
|
||||
|
||||
|
||||
|
@ -86,7 +82,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
|||
all_depts = int(all_depts)
|
||||
with_inactives = int(with_inactives)
|
||||
|
||||
H = [html_sco_header.html_sem_header(REQUEST, "Gestion des utilisateurs")]
|
||||
H = [html_sco_header.html_sem_header("Gestion des utilisateurs")]
|
||||
|
||||
if current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept):
|
||||
H.append(
|
||||
|
@ -117,7 +113,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
|||
<input type="checkbox" name="all_depts" value="1" onchange="document.f.submit();" %s>Tous les départements</input>
|
||||
<input type="checkbox" name="with_inactives" value="1" onchange="document.f.submit();" %s>Avec anciens utilisateurs</input>
|
||||
</form></p>"""
|
||||
% (REQUEST.URL0, checked, olds_checked)
|
||||
% (request.base_url, checked, olds_checked)
|
||||
)
|
||||
|
||||
L = list_users(
|
||||
|
@ -212,12 +208,12 @@ def list_users(
|
|||
html_class="table_leftalign list_users",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url="%s?all=%s" % (REQUEST.URL0, all),
|
||||
base_url="%s?all=%s" % (request.base_url, all),
|
||||
pdf_link=False, # table is too wide to fit in a paper page => disable pdf
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
return tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format, with_html_headers=False)
|
||||
|
||||
|
||||
def get_user_list(dept=None, with_inactives=False):
|
||||
|
|
|
@ -39,21 +39,18 @@ import os
|
|||
import pydot
|
||||
import re
|
||||
import requests
|
||||
import six
|
||||
import six.moves._thread
|
||||
import sys
|
||||
import _thread
|
||||
import time
|
||||
import traceback
|
||||
import types
|
||||
import unicodedata
|
||||
import urllib
|
||||
from xml.etree import ElementTree
|
||||
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
||||
|
||||
from flask import g, current_app
|
||||
|
||||
from PIL import Image as PILImage
|
||||
|
||||
from flask import g, url_for, request
|
||||
from flask import g, url_for, request, make_response
|
||||
from werkzeug.wrappers import response
|
||||
|
||||
from config import Config
|
||||
from app import log
|
||||
|
@ -217,7 +214,7 @@ def group_by_key(d, key):
|
|||
|
||||
|
||||
# ----- Global lock for critical sections (except notes_tables caches)
|
||||
GSL = six.moves._thread.allocate_lock() # Global ScoDoc Lock
|
||||
GSL = _thread.allocate_lock() # Global ScoDoc Lock
|
||||
|
||||
SCODOC_DIR = Config.SCODOC_DIR
|
||||
|
||||
|
@ -228,7 +225,7 @@ SCODOC_CFG_DIR = os.path.join(Config.SCODOC_VAR_DIR, "config")
|
|||
SCODOC_VERSION_DIR = os.path.join(SCODOC_CFG_DIR, "version")
|
||||
# ----- Repertoire tmp : /opt/scodoc-data/tmp
|
||||
SCO_TMP_DIR = os.path.join(Config.SCODOC_VAR_DIR, "tmp")
|
||||
if not os.path.exists(SCO_TMP_DIR):
|
||||
if not os.path.exists(SCO_TMP_DIR) and os.path.exists(Config.SCODOC_VAR_DIR):
|
||||
os.mkdir(SCO_TMP_DIR, 0o755)
|
||||
# ----- Les logos: /opt/scodoc-data/config/logos
|
||||
SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos")
|
||||
|
@ -296,16 +293,39 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer
|
|||
# Adresse pour l'envoi des dumps (pour assistance technnique):
|
||||
# ne pas changer (ou vous perdez le support)
|
||||
SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump"
|
||||
# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump"
|
||||
|
||||
CSV_FIELDSEP = ";"
|
||||
CSV_LINESEP = "\n"
|
||||
CSV_MIMETYPE = "text/comma-separated-values"
|
||||
CSV_SUFFIX = ".csv"
|
||||
JSON_MIMETYPE = "application/json"
|
||||
JSON_SUFFIX = ".json"
|
||||
PDF_MIMETYPE = "application/pdf"
|
||||
PDF_SUFFIX = ".pdf"
|
||||
XLS_MIMETYPE = "application/vnd.ms-excel"
|
||||
XLS_SUFFIX = ".xls"
|
||||
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
XLSX_SUFFIX = ".xlsx"
|
||||
PDF_MIMETYPE = "application/pdf"
|
||||
XML_MIMETYPE = "text/xml"
|
||||
JSON_MIMETYPE = "application/json"
|
||||
XML_SUFFIX = ".xml"
|
||||
|
||||
|
||||
def get_mime_suffix(format_code: str) -> tuple[str, str]:
|
||||
"""Returns (MIME, SUFFIX) from format_code == "xls", "xml", ...
|
||||
SUFFIX includes the dot: ".xlsx", ".xml", ...
|
||||
"xls" and "xlsx" format codes give XLSX
|
||||
"""
|
||||
d = {
|
||||
"csv": (CSV_MIMETYPE, CSV_SUFFIX),
|
||||
"xls": (XLSX_MIMETYPE, XLSX_SUFFIX),
|
||||
"xlsx": (XLSX_MIMETYPE, XLSX_SUFFIX),
|
||||
"pdf": (PDF_MIMETYPE, PDF_SUFFIX),
|
||||
"xml": (XML_MIMETYPE, XML_SUFFIX),
|
||||
"json": (JSON_MIMETYPE, JSON_SUFFIX),
|
||||
}
|
||||
return d[format_code]
|
||||
|
||||
|
||||
# Admissions des étudiants
|
||||
# Différents types de voies d'admission:
|
||||
|
@ -388,6 +408,18 @@ def unescape_html(s):
|
|||
return s
|
||||
|
||||
|
||||
def build_url_query(url: str, **params) -> str:
|
||||
"""Add parameters to existing url, as a query string"""
|
||||
url_parse = urlparse(url)
|
||||
query = url_parse.query
|
||||
url_dict = dict(parse_qsl(query))
|
||||
url_dict.update(params)
|
||||
url_new_query = urlencode(url_dict)
|
||||
url_parse = url_parse._replace(query=url_new_query)
|
||||
new_url = urlunparse(url_parse)
|
||||
return new_url
|
||||
|
||||
|
||||
# test if obj is iterable (but not a string)
|
||||
isiterable = lambda obj: getattr(obj, "__iter__", False)
|
||||
|
||||
|
@ -457,14 +489,17 @@ def sanitize_string(s):
|
|||
return suppress_accents(s.translate(trans)).replace(" ", "_").replace("\t", "_")
|
||||
|
||||
|
||||
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\")
|
||||
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\&[]*?'")
|
||||
|
||||
|
||||
def make_filename(name):
|
||||
"""Try to convert name to a reasonable filename
|
||||
without spaces, (back)slashes, : and without accents
|
||||
"""
|
||||
return suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
|
||||
return (
|
||||
suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
|
||||
or "scodoc"
|
||||
)
|
||||
|
||||
|
||||
VALID_CARS = (
|
||||
|
@ -490,7 +525,15 @@ def is_valid_filename(filename):
|
|||
return VALID_EXP.match(filename)
|
||||
|
||||
|
||||
def sendCSVFile(REQUEST, data, filename):
|
||||
def bul_filename(sem, etud, format):
|
||||
"""Build a filename for this bulletin"""
|
||||
dt = time.strftime("%Y-%m-%d")
|
||||
filename = f"bul-{sem['titre_num']}-{dt}-{etud['nom']}.{format}"
|
||||
filename = make_filename(filename)
|
||||
return filename
|
||||
|
||||
|
||||
def sendCSVFile(REQUEST, data, filename): # DEPRECATED ne plus utiliser
|
||||
"""publication fichier.
|
||||
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
|
||||
"""
|
||||
|
@ -504,16 +547,9 @@ def sendCSVFile(REQUEST, data, filename):
|
|||
return data
|
||||
|
||||
|
||||
def sendPDFFile(REQUEST, data, filename):
|
||||
filename = (
|
||||
unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_")
|
||||
)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", PDF_MIMETYPE)
|
||||
REQUEST.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
)
|
||||
return data
|
||||
def sendPDFFile(data, filename):
|
||||
filename = make_filename(filename)
|
||||
return send_file(data, filename=filename, mime=PDF_MIMETYPE, attached=True)
|
||||
|
||||
|
||||
class ScoDocJSONEncoder(json.JSONEncoder):
|
||||
|
@ -526,38 +562,54 @@ class ScoDocJSONEncoder(json.JSONEncoder):
|
|||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def sendJSON(REQUEST, data):
|
||||
def sendJSON(data, attached=False):
|
||||
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
|
||||
return js
|
||||
return send_file(js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=attached)
|
||||
|
||||
|
||||
def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True):
|
||||
def sendXML(data, tagname=None, force_outer_xml_tag=True, attached=False):
|
||||
if type(data) != list:
|
||||
data = [data] # always list-of-dicts
|
||||
if force_outer_xml_tag:
|
||||
data = [{tagname: data}]
|
||||
tagname += "_list"
|
||||
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
||||
return doc
|
||||
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=attached)
|
||||
|
||||
|
||||
def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True):
|
||||
def sendResult(data, name=None, format=None, force_outer_xml_tag=True, attached=False):
|
||||
if (format is None) or (format == "html"):
|
||||
return data
|
||||
elif format == "xml": # name is outer tagname
|
||||
return sendXML(
|
||||
REQUEST, data, tagname=name, force_outer_xml_tag=force_outer_xml_tag
|
||||
)
|
||||
return sendXML(data, tagname=name, force_outer_xml_tag=force_outer_xml_tag, attached=attached)
|
||||
elif format == "json":
|
||||
return sendJSON(REQUEST, data)
|
||||
return sendJSON(data, attached=attached)
|
||||
else:
|
||||
raise ValueError("invalid format: %s" % format)
|
||||
|
||||
|
||||
def send_file(data, filename="", suffix="", mime=None, attached=None):
|
||||
"""Build Flask Response for file download of given type
|
||||
By default (attached is None), json and xml are inlined and otrher types are atteched.
|
||||
"""
|
||||
if attached is None:
|
||||
if mime == XML_MIMETYPE or mime == JSON_MIMETYPE:
|
||||
attached = False
|
||||
else:
|
||||
attached = True
|
||||
# if attached and not filename:
|
||||
# raise ValueError("send_file: missing attachement filename")
|
||||
if filename:
|
||||
if suffix:
|
||||
filename += suffix
|
||||
filename = make_filename(filename)
|
||||
response = make_response(data)
|
||||
response.headers["Content-Type"] = mime
|
||||
if attached and filename:
|
||||
response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename
|
||||
return response
|
||||
|
||||
|
||||
def get_scodoc_version():
|
||||
"return a string identifying ScoDoc version"
|
||||
return sco_version.SCOVERSION
|
||||
|
@ -759,42 +811,13 @@ def AnneeScolaire(sco_year=None):
|
|||
return year
|
||||
|
||||
|
||||
def log_unknown_etud(REQUEST=None, format="html"):
|
||||
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
|
||||
etudid = REQUEST.form.get("etudid", "?")
|
||||
code_nip = REQUEST.form.get("code_nip", "?")
|
||||
code_ine = REQUEST.form.get("code_ine", "?")
|
||||
log(
|
||||
"unknown student: etudid=%s code_nip=%s code_ine=%s"
|
||||
% (etudid, code_nip, code_ine)
|
||||
)
|
||||
return _sco_error_response("unknown student", format=format, REQUEST=REQUEST)
|
||||
|
||||
|
||||
# XXX #sco8 à tester ou ré-écrire
|
||||
def _sco_error_response(msg, format="html", REQUEST=None):
|
||||
"""Send an error message to the client, in html or xml format."""
|
||||
REQUEST.RESPONSE.setStatus(404, reason=msg)
|
||||
if format == "html" or format == "pdf":
|
||||
raise sco_exceptions.ScoValueError(msg)
|
||||
elif format == "xml":
|
||||
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
||||
doc = ElementTree.Element("error", msg=msg)
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(SCO_ENCODING)
|
||||
elif format == "json":
|
||||
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
|
||||
return "undefined" # XXX voir quoi faire en cas d'erreur json
|
||||
else:
|
||||
raise ValueError("ScoErrorResponse: invalid format")
|
||||
|
||||
|
||||
def return_text_if_published(val, REQUEST):
|
||||
"""Pour les méthodes publiées qui ramènent soit du texte (HTML) soit du JSON
|
||||
sauf quand elles sont appellées depuis python.
|
||||
La présence de l'argument REQUEST indique la publication.
|
||||
"""
|
||||
if REQUEST and not isinstance(val, str):
|
||||
return sendJSON(REQUEST, val)
|
||||
return sendJSON(val)
|
||||
return val
|
||||
|
||||
|
||||
|
@ -823,9 +846,10 @@ def confirm_dialog(
|
|||
action = f'action="{dest_url}"'
|
||||
|
||||
H = [
|
||||
f"""<form {action} method="post">""",
|
||||
message,
|
||||
"""<input type="submit" value="%s"/>""" % OK,
|
||||
f"""<form {action} method="POST">
|
||||
{message}
|
||||
<input type="submit" value="{OK}"/>
|
||||
""",
|
||||
]
|
||||
if cancel_url:
|
||||
H.append(
|
||||
|
|
|
@ -154,7 +154,7 @@ div.scovalueerror {
|
|||
p.footer {
|
||||
font-size: 80%;
|
||||
color: rgb(60,60,60);
|
||||
margin-top: 10px;
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid rgb(60,60,60);
|
||||
}
|
||||
|
||||
|
@ -1092,13 +1092,13 @@ h2.formsemestre, .gtrcontent h2 {
|
|||
#formnotes td.tf-fieldlabel {
|
||||
border-bottom: 1px dotted #fdcaca;
|
||||
}
|
||||
|
||||
/* Formulaires ScoDoc 9 */
|
||||
form.sco-form {
|
||||
margin-top: 1em;
|
||||
.wtf-field li {
|
||||
display: inline;
|
||||
}.wtf-field ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
div.sco-submit {
|
||||
margin-top: 2em;
|
||||
.wtf-field .errors {
|
||||
color: red ; font-weight: bold;
|
||||
}
|
||||
/*
|
||||
.formsemestre_menubar {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
ou écrire la liste "notes" <a href="mailto:notes@listes.univ-paris13.fr">notes@listes.univ-paris13.fr</a> en
|
||||
indiquant la version du logiciel
|
||||
<br />
|
||||
(plus d'informations sur les listes de diffusion<a href="https://scodoc.org/ListesDeDiffusion/">voir
|
||||
(plus d'informations sur les listes de diffusion <a href="https://scodoc.org/ListesDeDiffusion/">voir
|
||||
cette page</a>).
|
||||
</p>
|
||||
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
|
||||
<h2>Erreur !</h2>
|
||||
|
||||
<p>{{ exc }}</p>
|
||||
{{ exc | safe }}
|
||||
|
||||
<p>
|
||||
<p class="footer">
|
||||
{% if g.scodoc_dept %}
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">retour page d'accueil
|
||||
departement {{ g.scodoc_dept }}</a>
|
||||
{% else %}
|
||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
|
||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
{% import 'bootstrap/wtf.html' as wtf %}
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<tr>
|
||||
<td class="wtf-field">{{ field.label }}</td>
|
||||
<td class="wtf-field">{{ field()|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="saisienote_etape1 form_placement">
|
||||
<form method=post>
|
||||
{{ form.evaluation_id }}
|
||||
{{ form.csrf_token }}
|
||||
<table class="tf">
|
||||
<tbody>
|
||||
{{ render_field(form.surveillants) }}
|
||||
{{ render_field(form.batiment) }}
|
||||
{{ render_field(form.salle) }}
|
||||
{{ render_field(form.nb_rangs) }}
|
||||
{{ render_field(form.etiquetage) }}
|
||||
{% if form.has_groups %}
|
||||
{# {{ render_field(form.groups) }}#}
|
||||
{% for partition in form.groups_tree %}
|
||||
<tr>
|
||||
{% if partition == 'Tous' %}
|
||||
<td rowspan="{{ form.nb_partitions }}">Groupes</td>
|
||||
{% endif %}
|
||||
<td>{{ partition }}</td>
|
||||
<td>
|
||||
{% for groupe in form.groups_tree[partition] %}
|
||||
{{ groupe }}{{ form.get_checkbox(partition, groupe)() }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{{ render_field(form.file_format) }}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<input id="gr_submit" type=submit value="Ok">
|
||||
<input id="gr_cancel" type=submit value="Annuler">
|
||||
</script>
|
||||
</form>
|
||||
<h3>Explications</h3>
|
||||
<ul>
|
||||
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer la largeur de la salle (nombre
|
||||
de colonnes);</li>
|
||||
<li>deux types de placements sont possibles :
|
||||
<ul>
|
||||
<li>continue suppose que les tables ont toutes un numéro unique;</li>
|
||||
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Il est possible de choisir un ou plusieurs groupes (shift/ctrl click) ou de choisir 'tous'.</li>
|
||||
<li>Choisir le format du fichier résultat :
|
||||
<ul>
|
||||
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;
|
||||
</li>
|
||||
<li>le format xls produit un classeur avec deux onglets:
|
||||
<ul>
|
||||
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
|
||||
peut servir de feuille d'émargement;</li>
|
||||
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<h2 class="insidebar">Dépt. {{ prefs["DeptName"] }}</h2>
|
||||
<a href="{{ url_for('scodoc.index') }}" class="sidebar">Accueil</a> <br/>
|
||||
{% if prefs["DeptIntranetURL"] %}
|
||||
<a href="{{ prefs["DeptIntranetURL"] }}" class="sidebar">
|
||||
{{ prefs["DeptIntranetTitle"] }}</a>
|
||||
{% endif %}
|
||||
<br/>
|
||||
|
||||
{#
|
||||
# Entreprises pas encore supporté en ScoDoc8
|
||||
# <br/><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br/>
|
||||
#}
|
|
@ -47,20 +47,18 @@ L'API de plus bas niveau est en gros:
|
|||
"""
|
||||
|
||||
import calendar
|
||||
import cgi
|
||||
import datetime
|
||||
import dateutil
|
||||
import dateutil.parser
|
||||
import re
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import string
|
||||
import time
|
||||
import urllib
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask import url_for
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
|
||||
from app.decorators import (
|
||||
scodoc,
|
||||
|
@ -123,11 +121,11 @@ def sco_publish(route, function, permission, methods=["GET"]):
|
|||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def index_html(REQUEST=None):
|
||||
def index_html():
|
||||
"""Gestionnaire absences, page principale"""
|
||||
# crude portage from 1999 DTML
|
||||
sems = sco_formsemestre.do_formsemestre_list()
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
|
@ -164,7 +162,7 @@ def index_html(REQUEST=None):
|
|||
Saisie par semaine </span> - Choix du groupe:
|
||||
<input name="datelundi" type="hidden" value="x"/>
|
||||
"""
|
||||
% REQUEST.URL0,
|
||||
% request.base_url,
|
||||
sco_abs_views.formChoixSemestreGroupe(),
|
||||
"</p>",
|
||||
cal_select_week(),
|
||||
|
@ -270,7 +268,6 @@ def doSignaleAbsenceGrSemestre(
|
|||
dates="",
|
||||
etudids="",
|
||||
destination=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Enregistre absences aux dates indiquees (abslist et dates).
|
||||
dates est une liste de dates ISO (séparées par des ',').
|
||||
|
@ -295,10 +292,10 @@ def doSignaleAbsenceGrSemestre(
|
|||
|
||||
# 2- Ajoute les absences
|
||||
if abslist:
|
||||
sco_abs._add_abslist(abslist, REQUEST, moduleimpl_id)
|
||||
sco_abs.add_abslist(abslist, moduleimpl_id)
|
||||
return "Absences ajoutées"
|
||||
|
||||
return ""
|
||||
return ("", 204)
|
||||
|
||||
|
||||
# ------------ HTML Interfaces
|
||||
|
@ -307,14 +304,14 @@ def doSignaleAbsenceGrSemestre(
|
|||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def SignaleAbsenceGrHebdo(
|
||||
datelundi, group_ids=[], destination="", moduleimpl_id=None, REQUEST=None
|
||||
datelundi, group_ids=[], destination="", moduleimpl_id=None, formsemestre_id=None
|
||||
):
|
||||
"Saisie hebdomadaire des absences"
|
||||
if not moduleimpl_id:
|
||||
moduleimpl_id = None
|
||||
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids, moduleimpl_id=moduleimpl_id
|
||||
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
||||
)
|
||||
if not groups_infos.members:
|
||||
return (
|
||||
|
@ -326,7 +323,7 @@ def SignaleAbsenceGrHebdo(
|
|||
base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % (
|
||||
datelundi,
|
||||
groups_infos.groups_query_args,
|
||||
six.moves.urllib.parse.quote(destination),
|
||||
urllib.parse.quote(destination),
|
||||
)
|
||||
|
||||
formsemestre_id = groups_infos.formsemestre_id
|
||||
|
@ -473,7 +470,6 @@ def SignaleAbsenceGrSemestre(
|
|||
group_ids=[], # list of groups to display
|
||||
nbweeks=4, # ne montre que les nbweeks dernieres semaines
|
||||
moduleimpl_id=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier"""
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
|
@ -510,7 +506,7 @@ def SignaleAbsenceGrSemestre(
|
|||
datedebut,
|
||||
datefin,
|
||||
groups_infos.groups_query_args,
|
||||
six.moves.urllib.parse.quote(destination),
|
||||
urllib.parse.quote(destination),
|
||||
)
|
||||
)
|
||||
base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id
|
||||
|
@ -810,7 +806,7 @@ def _gen_form_saisie_groupe(
|
|||
H.append('<input type="hidden" name="dates" value="%s"/>' % ",".join(dates))
|
||||
H.append(
|
||||
'<input type="hidden" name="destination" value="%s"/>'
|
||||
% six.moves.urllib.parse.quote(destination)
|
||||
% urllib.parse.quote(destination)
|
||||
)
|
||||
#
|
||||
# version pour formulaire avec AJAX (Yann LB)
|
||||
|
@ -843,7 +839,6 @@ def EtatAbsencesGr(
|
|||
fin="",
|
||||
with_boursier=True, # colonne boursier
|
||||
format="html",
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Liste les absences de groupes"""
|
||||
datedebut = ndb.DateDMYtoISO(debut)
|
||||
|
@ -938,7 +933,7 @@ def EtatAbsencesGr(
|
|||
javascripts=["js/etud_info.js"],
|
||||
),
|
||||
html_title=html_sco_header.html_sem_header(
|
||||
REQUEST, "%s" % title, sem, with_page_header=False
|
||||
"%s" % title, sem, with_page_header=False
|
||||
)
|
||||
+ "<p>Période du %s au %s (nombre de <b>demi-journées</b>)<br/>" % (debut, fin),
|
||||
base_url="%s&formsemestre_id=%s&debut=%s&fin=%s"
|
||||
|
@ -964,9 +959,9 @@ ou entrez une date pour visualiser les absents un jour donné :
|
|||
<input type="submit" name="" value="visualiser les absences">
|
||||
</form></div>
|
||||
"""
|
||||
% (REQUEST.URL0, formsemestre_id, groups_infos.get_form_elem()),
|
||||
% (request.base_url, formsemestre_id, groups_infos.get_form_elem()),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
@bp.route("/EtatAbsencesDate")
|
||||
|
@ -1062,7 +1057,6 @@ def AddBilletAbsence(
|
|||
code_nip=None,
|
||||
code_ine=None,
|
||||
justified=True,
|
||||
REQUEST=None,
|
||||
xml_reply=True,
|
||||
):
|
||||
"""Memorise un "billet"
|
||||
|
@ -1072,7 +1066,9 @@ def AddBilletAbsence(
|
|||
# check etudid
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid, code_nip=code_nip, filled=True)
|
||||
if not etuds:
|
||||
return scu.log_unknown_etud(REQUEST=REQUEST)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
# check dates
|
||||
begin_date = dateutil.parser.isoparse(begin) # may raises ValueError
|
||||
|
@ -1096,13 +1092,10 @@ def AddBilletAbsence(
|
|||
)
|
||||
if xml_reply:
|
||||
# Renvoie le nouveau billet en XML
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
log("AddBilletAbsence: new billet_id=%s (%gs)" % (billet_id, time.time() - t0))
|
||||
return tab.make_page(REQUEST=REQUEST, format="xml")
|
||||
return tab.make_page(format="xml")
|
||||
else:
|
||||
return billet_id
|
||||
|
||||
|
@ -1122,7 +1115,7 @@ def AddBilletAbsenceForm(etudid, REQUEST=None):
|
|||
)
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
|
@ -1223,17 +1216,18 @@ def _tableBillets(billets, etud=None, title=""):
|
|||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
|
||||
def listeBilletsEtud(etudid=False, format="html"):
|
||||
"""Liste billets pour un etudiant"""
|
||||
etuds = sco_etud.get_etud_info(filled=True, etudid=etudid)
|
||||
if not etuds:
|
||||
return scu.log_unknown_etud(format=format, REQUEST=REQUEST)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
return tab.make_page(REQUEST=REQUEST, format=format)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
@bp.route(
|
||||
|
@ -1242,12 +1236,12 @@ def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
|
|||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def XMLgetBilletsEtud(etudid=False, REQUEST=None):
|
||||
def XMLgetBilletsEtud(etudid=False):
|
||||
"""Liste billets pour un etudiant"""
|
||||
if not sco_preferences.get_preference("handle_billets_abs"):
|
||||
return ""
|
||||
t0 = time.time()
|
||||
r = listeBilletsEtud(etudid, REQUEST=REQUEST, format="xml")
|
||||
r = listeBilletsEtud(etudid, format="xml")
|
||||
log("XMLgetBilletsEtud (%gs)" % (time.time() - t0))
|
||||
return r
|
||||
|
||||
|
@ -1259,7 +1253,7 @@ def XMLgetBilletsEtud(etudid=False, REQUEST=None):
|
|||
def listeBillets(REQUEST=None):
|
||||
"""Page liste des billets non traités et formulaire recherche d'un billet"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etat": 0})
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etat": False})
|
||||
tab = _tableBillets(billets)
|
||||
T = tab.html()
|
||||
H = [
|
||||
|
@ -1268,7 +1262,7 @@ def listeBillets(REQUEST=None):
|
|||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("billet_id", {"input_type": "text", "title": "Numéro du billet"}),),
|
||||
submitbutton=False,
|
||||
|
@ -1281,11 +1275,11 @@ def listeBillets(REQUEST=None):
|
|||
)
|
||||
|
||||
|
||||
@bp.route("/deleteBilletAbsence")
|
||||
@bp.route("/deleteBilletAbsence", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def deleteBilletAbsence(billet_id, REQUEST=None, dialog_confirmed=False):
|
||||
def deleteBilletAbsence(billet_id, dialog_confirmed=False):
|
||||
"""Supprime un billet."""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
|
@ -1307,7 +1301,7 @@ def deleteBilletAbsence(billet_id, REQUEST=None, dialog_confirmed=False):
|
|||
return flask.redirect("listeBillets?head_message=Billet%20supprimé")
|
||||
|
||||
|
||||
def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
def _ProcessBilletAbsence(billet, estjust, description):
|
||||
"""Traite un billet: ajoute absence(s) et éventuellement justificatifs,
|
||||
et change l'état du billet à 1.
|
||||
NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après-midi.
|
||||
|
@ -1329,7 +1323,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
|||
dates[0],
|
||||
0,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 1
|
||||
|
@ -1341,7 +1334,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
|||
dates[-1],
|
||||
1,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 1
|
||||
|
@ -1353,7 +1345,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
|||
jour,
|
||||
0,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
sco_abs.add_absence(
|
||||
|
@ -1361,7 +1352,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
|||
jour,
|
||||
1,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 2
|
||||
|
@ -1372,7 +1362,7 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
|||
return n
|
||||
|
||||
|
||||
@bp.route("/ProcessBilletAbsenceForm")
|
||||
@bp.route("/ProcessBilletAbsenceForm", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
|
@ -1401,7 +1391,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
|||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("billet_id", {"input_type": "hidden"}),
|
||||
|
@ -1439,9 +1429,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
|||
elif tf[0] == -1:
|
||||
return flask.redirect(scu.ScoURL())
|
||||
else:
|
||||
n = _ProcessBilletAbsence(
|
||||
billet, tf[2]["estjust"], tf[2]["description"], REQUEST
|
||||
)
|
||||
n = _ProcessBilletAbsence(billet, tf[2]["estjust"], tf[2]["description"])
|
||||
if tf[2]["estjust"]:
|
||||
j = "justifiées"
|
||||
else:
|
||||
|
@ -1477,7 +1465,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
|||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
||||
def XMLgetAbsEtud(beg_date="", end_date=""):
|
||||
"""returns list of absences in date interval"""
|
||||
t0 = time.time()
|
||||
etuds = sco_etud.get_etud_info(filled=False)
|
||||
|
@ -1493,7 +1481,6 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
|||
|
||||
abs_list = sco_abs.list_abs_date(etud["etudid"], beg_date, end_date)
|
||||
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
doc = ElementTree.Element(
|
||||
"absences", etudid=str(etud["etudid"]), beg_date=beg_date, end_date=end_date
|
||||
)
|
||||
|
@ -1509,4 +1496,5 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
|||
)
|
||||
)
|
||||
log("XMLgetAbsEtud (%gs)" % (time.time() - t0))
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
data = sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
return scu.send_file(data, mime=scu.XML_MIMETYPE, attached=False)
|
||||
|
|
|
@ -38,6 +38,9 @@ import re
|
|||
import time
|
||||
import calendar
|
||||
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
# MIGRATION EN COURS => MODULE DESACTIVE !
|
||||
|
||||
# A REVOIR
|
||||
|
@ -79,7 +82,7 @@ def sidebar(REQUEST):
|
|||
<ul class="insidebar">"""
|
||||
% params,
|
||||
]
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
|
||||
if current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
H.append(
|
||||
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_create" class="sidebar">Nouvelle entreprise</a> </li>"""
|
||||
% params
|
||||
|
@ -104,9 +107,7 @@ def sidebar(REQUEST):
|
|||
<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_correspondant_list?entreprise_id=%(entreprise_id)s" class="sidebar">Corresp.</a></li>"""
|
||||
% params
|
||||
) # """
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
):
|
||||
if current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
H.append(
|
||||
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_correspondant_create?entreprise_id=%(entreprise_id)s" class="sidebar">Nouveau Corresp.</a></li>"""
|
||||
% params
|
||||
|
@ -115,9 +116,7 @@ def sidebar(REQUEST):
|
|||
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_contact_list?entreprise_id=%(entreprise_id)s" class="sidebar">Contacts</a></li>"""
|
||||
% params
|
||||
)
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
):
|
||||
if current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
H.append(
|
||||
"""<li class="insidebar"><a href="%(ScoURL)s/Entreprises/entreprise_contact_create?entreprise_id=%(entreprise_id)s" class="sidebar">Nouveau "contact"</a></li>"""
|
||||
% params
|
||||
|
@ -126,7 +125,7 @@ def sidebar(REQUEST):
|
|||
|
||||
#
|
||||
H.append("""<br/><br/>%s""" % scu.icontag("entreprise_side_img"))
|
||||
if not REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
|
||||
if not current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
H.append("""<br/><em>(Lecture seule)</em>""")
|
||||
H.append("""</div> </div> <!-- end of sidebar -->""")
|
||||
return "".join(H)
|
||||
|
@ -166,14 +165,14 @@ def index_html(REQUEST=None, etud_nom=None, limit=50, offset="", format="html"):
|
|||
if offset:
|
||||
webparams["offset"] = max((offset or 0) - limit, 0)
|
||||
prev_lnk = '<a class="stdlink" href="%s">précédentes</a>' % (
|
||||
REQUEST.URL0 + "?" + six.moves.urllib.parse.urlencode(webparams)
|
||||
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
|
||||
)
|
||||
else:
|
||||
prev_lnk = ""
|
||||
if len(entreprises) >= limit:
|
||||
webparams["offset"] = (offset or 0) + limit
|
||||
next_lnk = '<a class="stdlink" href="%s">suivantes</a>' % (
|
||||
REQUEST.URL0 + "?" + six.moves.urllib.parse.urlencode(webparams)
|
||||
request.base_url + "?" + six.moves.urllib.parse.urlencode(webparams)
|
||||
)
|
||||
else:
|
||||
next_lnk = ""
|
||||
|
@ -220,11 +219,11 @@ def index_html(REQUEST=None, etud_nom=None, limit=50, offset="", format="html"):
|
|||
html_class="entreprise_list table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
html_next_section=table_navigation,
|
||||
base_url=REQUEST.URL0 + "?",
|
||||
base_url=request.base_url + "?",
|
||||
preferences=context.get_preferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
else:
|
||||
H = [
|
||||
entreprise_header(REQUEST=REQUEST, page_title="Suivi entreprises"),
|
||||
|
@ -293,15 +292,15 @@ def entreprise_contact_list(entreprise_id=None, format="html", REQUEST=None):
|
|||
html_sortable=True,
|
||||
html_class="contact_list table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
base_url=REQUEST.URL0 + "?",
|
||||
base_url=request.base_url + "?",
|
||||
preferences=context.get_preferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H.append(tab.html())
|
||||
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
|
||||
if current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
if entreprise_id:
|
||||
H.append(
|
||||
"""<p class="entreprise_create"><a class="entreprise_create" href="entreprise_contact_create?entreprise_id=%(entreprise_id)s">nouveau "contact"</a></p>
|
||||
|
@ -399,15 +398,15 @@ def entreprise_correspondant_list(
|
|||
html_sortable=True,
|
||||
html_class="contact_list table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
base_url=REQUEST.URL0 + "?",
|
||||
base_url=request.base_url + "?",
|
||||
preferences=context.get_preferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H.append(tab.html())
|
||||
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoEntrepriseChange):
|
||||
if current_user.has_permission(Permission.ScoEntrepriseChange):
|
||||
H.append(
|
||||
"""<p class="entreprise_create"><a class="entreprise_create" href="entreprise_correspondant_create?entreprise_id=%(entreprise_id)s">Ajouter un correspondant dans l'entreprise %(nom)s</a></p>
|
||||
"""
|
||||
|
@ -444,7 +443,7 @@ def entreprise_contact_edit(entreprise_contact_id, REQUEST=None):
|
|||
% E,
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -515,14 +514,12 @@ def entreprise_contact_edit(entreprise_contact_id, REQUEST=None):
|
|||
cancelbutton="Annuler",
|
||||
initvalues=c,
|
||||
submitlabel="Modifier les valeurs",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
if REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
if current_user.has_permission(
|
||||
Permission.ScoEntrepriseChange,
|
||||
):
|
||||
H.append(
|
||||
|
@ -560,7 +557,7 @@ def entreprise_correspondant_edit(entreprise_corresp_id, REQUEST=None):
|
|||
"""<h2 class="entreprise_correspondant">Édition contact entreprise</h2>""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -641,9 +638,7 @@ def entreprise_correspondant_edit(entreprise_corresp_id, REQUEST=None):
|
|||
cancelbutton="Annuler",
|
||||
initvalues=c,
|
||||
submitlabel="Modifier les valeurs",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -684,7 +679,7 @@ def entreprise_contact_create(entreprise_id, REQUEST=None):
|
|||
% E,
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("entreprise_id", {"input_type": "hidden", "default": entreprise_id}),
|
||||
|
@ -750,9 +745,7 @@ def entreprise_contact_create(entreprise_id, REQUEST=None):
|
|||
),
|
||||
cancelbutton="Annuler",
|
||||
submitlabel="Ajouter ce contact",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -783,13 +776,13 @@ def entreprise_contact_delete(entreprise_contact_id, REQUEST=None):
|
|||
"""<h2>Suppression du contact</h2>""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("entreprise_contact_id", {"input_type": "hidden"}),),
|
||||
initvalues=c,
|
||||
submitlabel="Confirmer la suppression",
|
||||
cancelbutton="Annuler",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(ScoEntrepriseChange),
|
||||
readonly=not current_user.has_permission(ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -814,7 +807,7 @@ def entreprise_correspondant_create(entreprise_id, REQUEST=None):
|
|||
% E,
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("entreprise_id", {"input_type": "hidden", "default": entreprise_id}),
|
||||
|
@ -892,9 +885,7 @@ def entreprise_correspondant_create(entreprise_id, REQUEST=None):
|
|||
),
|
||||
cancelbutton="Annuler",
|
||||
submitlabel="Ajouter ce correspondant",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -920,15 +911,13 @@ def entreprise_correspondant_delete(entreprise_corresp_id, REQUEST=None):
|
|||
"""<h2>Suppression du correspondant %(nom)s %(prenom)s</h2>""" % c,
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("entreprise_corresp_id", {"input_type": "hidden"}),),
|
||||
initvalues=c,
|
||||
submitlabel="Confirmer la suppression",
|
||||
cancelbutton="Annuler",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -976,15 +965,13 @@ def entreprise_delete(entreprise_id, REQUEST=None):
|
|||
H.append("""<li>%(date)s %(description)s</li>""" % c)
|
||||
H.append("""</ul>""")
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("entreprise_id", {"input_type": "hidden"}),),
|
||||
initvalues=E,
|
||||
submitlabel="Confirmer la suppression",
|
||||
cancelbutton="Annuler",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
H.append(tf[1])
|
||||
|
@ -1008,7 +995,7 @@ def entreprise_create(REQUEST=None):
|
|||
"""<h2 class="entreprise_new">Création d'une entreprise</h2>""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("nom", {"size": 25, "title": "Nom de l'entreprise"}),
|
||||
|
@ -1079,9 +1066,7 @@ def entreprise_create(REQUEST=None):
|
|||
),
|
||||
cancelbutton="Annuler",
|
||||
submitlabel="Ajouter cette entreprise",
|
||||
readonly=not REQUEST.AUTHENTICATED_USER.has_permission(
|
||||
Permission.ScoEntrepriseChange
|
||||
),
|
||||
readonly=not current_user.has_permission(Permission.ScoEntrepriseChange),
|
||||
)
|
||||
if tf[0] == 0:
|
||||
return "\n".join(H) + tf[1] + entreprise_footer(REQUEST)
|
||||
|
@ -1097,7 +1082,7 @@ security.declareProtected(ScoEntrepriseView, "entreprise_edit")
|
|||
|
||||
def entreprise_edit(entreprise_id, REQUEST=None, start=1):
|
||||
"""Form. edit entreprise"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
readonly = not authuser.has_permission(Permission.ScoEntrepriseChange)
|
||||
F = sco_entreprises.do_entreprise_list(args={"entreprise_id": entreprise_id})[0]
|
||||
H = [
|
||||
|
@ -1105,7 +1090,7 @@ def entreprise_edit(entreprise_id, REQUEST=None, start=1):
|
|||
"""<h2 class="entreprise">%(nom)s</h2>""" % F,
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("entreprise_id", {"default": entreprise_id, "input_type": "hidden"}),
|
||||
|
|
|
@ -38,8 +38,8 @@ from operator import itemgetter
|
|||
from xml.etree import ElementTree
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import current_app
|
||||
from flask import url_for
|
||||
from flask import current_app, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
from config import Config
|
||||
|
@ -146,23 +146,31 @@ def sco_publish(route, function, permission, methods=["GET"]):
|
|||
|
||||
|
||||
# --------------------- Quelques essais élémentaires:
|
||||
@bp.route("/essai")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def essai(REQUEST=None):
|
||||
return essai_(REQUEST)
|
||||
# @bp.route("/essai")
|
||||
# @scodoc
|
||||
# @permission_required(Permission.ScoView)
|
||||
# @scodoc7func
|
||||
# def essai(REQUEST=None):
|
||||
# return essai_(REQUEST)
|
||||
|
||||
|
||||
def essai_(REQUEST):
|
||||
return "<html><body><h2>essai !</h2><p>%s</p></body></html>" % (REQUEST,)
|
||||
# def essai_(REQUEST):
|
||||
# return "<html><body><h2>essai !</h2><p>%s</p></body></html>" % (REQUEST,)
|
||||
|
||||
|
||||
def essai2():
|
||||
return essai_("sans request")
|
||||
# def essai2():
|
||||
# err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
||||
# <p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
|
||||
# laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
||||
# </p>
|
||||
# <a href="url_for('notes.ue_list', scodoc-dept=g.scodoc_dept, formation_id='XXX')">reprendre</a>
|
||||
# """
|
||||
# raise ScoGenError(err_page)
|
||||
# # raise ScoGenError("une erreur banale")
|
||||
# return essai_("sans request")
|
||||
|
||||
|
||||
sco_publish("/essai2", essai2, Permission.ScoImplement)
|
||||
# sco_publish("/essai2", essai2, Permission.ScoImplement)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
@ -406,14 +414,14 @@ sco_publish(
|
|||
def index_html(REQUEST=None):
|
||||
"Page accueil formations"
|
||||
|
||||
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
|
||||
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Programmes formations"),
|
||||
"""<h2>Programmes pédagogiques</h2>
|
||||
""",
|
||||
]
|
||||
T = sco_formations.formation_list_table(REQUEST=REQUEST)
|
||||
T = sco_formations.formation_list_table()
|
||||
|
||||
H.append(T.html())
|
||||
|
||||
|
@ -456,22 +464,22 @@ sco_publish(
|
|||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formation_list(format=None, REQUEST=None, formation_id=None, args={}):
|
||||
def formation_list(format=None, formation_id=None, args={}):
|
||||
"""List formation(s) with given id, or matching args
|
||||
(when args is given, formation_id is ignored).
|
||||
"""
|
||||
r = sco_formations.formation_list(formation_id=formation_id, args=args)
|
||||
return scu.sendResult(REQUEST, r, name="formation", format=format)
|
||||
return scu.sendResult(r, name="formation", format=format)
|
||||
|
||||
|
||||
@bp.route("/formation_export")
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formation_export(formation_id, export_ids=False, format=None, REQUEST=None):
|
||||
def formation_export(formation_id, export_ids=False, format=None):
|
||||
"Export de la formation au format indiqué (xml ou json)"
|
||||
return sco_formations.formation_export(
|
||||
formation_id, export_ids=export_ids, format=format, REQUEST=REQUEST
|
||||
formation_id, export_ids=export_ids, format=format
|
||||
)
|
||||
|
||||
|
||||
|
@ -501,7 +509,7 @@ def formation_import_xml_form(REQUEST):
|
|||
]
|
||||
footer = html_sco_header.sco_footer()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("xmlfile", {"input_type": "file", "title": "Fichier XML", "size": 30}),),
|
||||
submitlabel="Importer",
|
||||
|
@ -606,8 +614,7 @@ sco_publish("/ue_move", sco_edit_formation.ue_move, Permission.ScoChangeFormatio
|
|||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def formsemestre_list(
|
||||
format=None,
|
||||
REQUEST=None,
|
||||
format="json",
|
||||
formsemestre_id=None,
|
||||
formation_id=None,
|
||||
etape_apo=None,
|
||||
|
@ -624,7 +631,7 @@ def formsemestre_list(
|
|||
args[argname] = L[argname]
|
||||
sems = sco_formsemestre.do_formsemestre_list(args=args)
|
||||
# log('formsemestre_list: format="%s", %s semestres found' % (format,len(sems)))
|
||||
return scu.sendResult(REQUEST, sems, name="formsemestre", format=format)
|
||||
return scu.sendResult(sems, name="formsemestre", format=format)
|
||||
|
||||
|
||||
@bp.route(
|
||||
|
@ -637,7 +644,7 @@ def XMLgetFormsemestres(etape_apo=None, formsemestre_id=None, REQUEST=None):
|
|||
"""List all formsemestres matching etape, XML format
|
||||
DEPRECATED: use formsemestre_list()
|
||||
"""
|
||||
log("Warning: calling deprecated XMLgetFormsemestres")
|
||||
current_app.logger.debug("Warning: calling deprecated XMLgetFormsemestres")
|
||||
args = {}
|
||||
if etape_apo:
|
||||
args["etape_apo"] = etape_apo
|
||||
|
@ -708,7 +715,6 @@ def edit_enseignants_form(REQUEST, moduleimpl_id):
|
|||
M, sem = sco_moduleimpl.can_change_ens(moduleimpl_id)
|
||||
# --
|
||||
header = html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
'Enseignants du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
|
||||
% (moduleimpl_id, M["module"]["titre"]),
|
||||
page_title="Enseignants du module %s" % M["module"]["titre"],
|
||||
|
@ -778,7 +784,7 @@ def edit_enseignants_form(REQUEST, moduleimpl_id):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
modform,
|
||||
submitlabel="Ajouter enseignant",
|
||||
|
@ -830,7 +836,6 @@ def edit_moduleimpl_resp(REQUEST, moduleimpl_id):
|
|||
M, sem = sco_moduleimpl.can_change_module_resp(REQUEST, moduleimpl_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
'Modification du responsable du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
|
||||
% (moduleimpl_id, M["module"]["titre"]),
|
||||
sem,
|
||||
|
@ -875,7 +880,7 @@ def edit_moduleimpl_resp(REQUEST, moduleimpl_id):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
submitlabel="Changer responsable",
|
||||
|
@ -952,7 +957,6 @@ def edit_moduleimpl_expr(REQUEST, moduleimpl_id):
|
|||
M, sem = sco_moduleimpl.can_change_ens(moduleimpl_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
'Modification règle de calcul du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
|
||||
% (moduleimpl_id, M["module"]["titre"]),
|
||||
sem,
|
||||
|
@ -979,7 +983,7 @@ def edit_moduleimpl_expr(REQUEST, moduleimpl_id):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
submitlabel="Modifier formule de calcul",
|
||||
|
@ -1059,7 +1063,6 @@ def view_module_abs(REQUEST, moduleimpl_id, format="html"):
|
|||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
'Absences du <a href="moduleimpl_status?moduleimpl_id=%s">module %s</a>'
|
||||
% (moduleimpl_id, M["module"]["titre"]),
|
||||
page_title="Absences du module %s" % (M["module"]["titre"]),
|
||||
|
@ -1083,14 +1086,14 @@ def view_module_abs(REQUEST, moduleimpl_id, format="html"):
|
|||
columns_ids=("nomprenom", "just", "nojust", "total"),
|
||||
rows=T,
|
||||
html_class="table_leftalign",
|
||||
base_url="%s?moduleimpl_id=%s" % (REQUEST.URL0, moduleimpl_id),
|
||||
base_url="%s?moduleimpl_id=%s" % (request.base_url, moduleimpl_id),
|
||||
filename="absmodule_" + scu.make_filename(M["module"]["titre"]),
|
||||
caption="Absences dans le module %s" % M["module"]["titre"],
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
return "\n".join(H) + tab.html() + html_sco_header.sco_footer()
|
||||
|
||||
|
@ -1110,7 +1113,6 @@ def edit_ue_expr(REQUEST, formsemestre_id, ue_id):
|
|||
ue = sco_edit_ue.do_ue_list({"ue_id": ue_id})[0]
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Modification règle de calcul de l'UE %s (%s)"
|
||||
% (ue["acronyme"], ue["titre"]),
|
||||
sem,
|
||||
|
@ -1139,7 +1141,7 @@ def edit_ue_expr(REQUEST, formsemestre_id, ue_id):
|
|||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
form,
|
||||
submitlabel="Modifier formule de calcul",
|
||||
|
@ -1251,13 +1253,13 @@ def formsemestre_enseignants_list(REQUEST, formsemestre_id, format="html"):
|
|||
html_class="table_leftalign",
|
||||
filename=scu.make_filename("Enseignants-" + sem["titreannee"]),
|
||||
html_title=html_sco_header.html_sem_header(
|
||||
REQUEST, "Enseignants du semestre", sem, with_page_header=False
|
||||
"Enseignants du semestre", sem, with_page_header=False
|
||||
),
|
||||
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
|
||||
caption="Tous les enseignants (responsables ou associés aux modules de ce semestre) apparaissent. Le nombre de saisies d'absences est le nombre d'opérations d'ajout effectuées sur ce semestre, sans tenir compte des annulations ou double saisies.",
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
)
|
||||
return T.make_page(page_title=title, title=title, REQUEST=REQUEST, format=format)
|
||||
return T.make_page(page_title=title, title=title, format=format)
|
||||
|
||||
|
||||
@bp.route("/edit_enseignants_form_delete", methods=["GET", "POST"])
|
||||
|
@ -1320,7 +1322,7 @@ def do_formsemestre_inscription_listinscrits(
|
|||
r = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(
|
||||
formsemestre_id
|
||||
)
|
||||
return scu.sendResult(REQUEST, r, format=format, name="inscrits")
|
||||
return scu.sendResult(r, format=format, name="inscrits")
|
||||
|
||||
|
||||
@bp.route("/formsemestre_desinscription", methods=["GET", "POST"])
|
||||
|
@ -1506,7 +1508,7 @@ def evaluation_delete(REQUEST, evaluation_id):
|
|||
tit = "Suppression de l'évaluation %(description)s (%(jour)s)" % E
|
||||
etat = sco_evaluations.do_evaluation_etat(evaluation_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, tit, with_h2=False),
|
||||
html_sco_header.html_sem_header(tit, with_h2=False),
|
||||
"""<h2 class="formsemestre">Module <tt>%(code)s</tt> %(titre)s</h2>""" % Mod,
|
||||
"""<h3>%s</h3>""" % tit,
|
||||
"""<p class="help">Opération <span class="redboldtext">irréversible</span>. Si vous supprimez l'évaluation, vous ne pourrez pas retrouver les notes associées.</p>""",
|
||||
|
@ -1538,7 +1540,7 @@ def evaluation_delete(REQUEST, evaluation_id):
|
|||
H.append("""</div>""")
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(("evaluation_id", {"input_type": "hidden"}),),
|
||||
initvalues=E,
|
||||
|
@ -1645,8 +1647,8 @@ sco_publish(
|
|||
"/placement_eval_selectetuds",
|
||||
sco_placement.placement_eval_selectetuds,
|
||||
Permission.ScoEnsView,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish("/do_placement", sco_placement.do_placement, Permission.ScoEnsView)
|
||||
|
||||
# --- Saisie des notes
|
||||
sco_publish(
|
||||
|
@ -1691,7 +1693,7 @@ def formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedevals"
|
|||
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
|
||||
formsemestre_id, REQUEST, version=version
|
||||
)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
_EXPL_BULL = """Versions des bulletins:<ul><li><bf>courte</bf>: moyennes des modules</li><li><bf>intermédiaire</bf>: moyennes des modules et notes des évaluations sélectionnées</li><li><bf>complète</bf>: toutes les notes</li><ul>"""
|
||||
|
@ -1707,7 +1709,7 @@ def formsemestre_bulletins_pdf_choice(REQUEST, formsemestre_id, version=None):
|
|||
pdfdoc, filename = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
|
||||
formsemestre_id, REQUEST, version=version
|
||||
)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
return formsemestre_bulletins_choice(
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
|
@ -1725,7 +1727,7 @@ def etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
|
|||
pdfdoc, filename = sco_bulletins_pdf.get_etud_bulletins_pdf(
|
||||
etudid, REQUEST, version=version
|
||||
)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
@bp.route("/formsemestre_bulletins_mailetuds_choice")
|
||||
|
@ -1771,12 +1773,12 @@ def formsemestre_bulletins_choice(
|
|||
"""Choix d'une version de bulletin"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, title, sem),
|
||||
html_sco_header.html_sem_header(title, sem),
|
||||
"""
|
||||
<form name="f" method="GET" action="%s">
|
||||
<input type="hidden" name="formsemestre_id" value="%s"></input>
|
||||
"""
|
||||
% (REQUEST.URL0, formsemestre_id),
|
||||
% (request.base_url, formsemestre_id),
|
||||
]
|
||||
H.append("""<select name="version" class="noprint">""")
|
||||
for (v, e) in (
|
||||
|
@ -1928,7 +1930,7 @@ def appreciation_add_form(
|
|||
else:
|
||||
initvalues = {}
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
|
@ -2010,8 +2012,7 @@ def formsemestre_validation_etud(
|
|||
"Enregistre choix jury pour un étudiant"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
|
||||
|
@ -2043,8 +2044,7 @@ def formsemestre_validation_etud_manu(
|
|||
"Enregistre choix jury pour un étudiant"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
|
||||
|
@ -2069,8 +2069,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid=None, REQUEST=None
|
|||
"Form. saisie UE validée hors ScoDoc"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
return sco_formsemestre_validation.formsemestre_validate_previous_ue(
|
||||
|
@ -2094,8 +2093,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid=None, REQUEST=N
|
|||
"Form. edition UE semestre extérieur"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
return sco_formsemestre_exterieurs.formsemestre_ext_edit_ue_validations(
|
||||
|
@ -2118,8 +2116,7 @@ def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id, REQUEST=None):
|
|||
"""Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
return sco_formsemestre_validation.etud_ue_suppress_validation(
|
||||
|
@ -2135,8 +2132,7 @@ def formsemestre_validation_auto(formsemestre_id, REQUEST):
|
|||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
|
||||
|
@ -2153,8 +2149,7 @@ def do_formsemestre_validation_auto(formsemestre_id, REQUEST):
|
|||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
|
||||
|
@ -2173,8 +2168,7 @@ def formsemestre_validation_suppress_etud(
|
|||
"""Suppression des decisions de jury pour un etudiant."""
|
||||
if not sco_permissions_check.can_validate_sem(formsemestre_id):
|
||||
return scu.confirm_dialog(
|
||||
message="<p>Opération non autorisée pour %s</h2>"
|
||||
% REQUEST.AUTHENTICATED_USER,
|
||||
message="<p>Opération non autorisée pour %s</h2>" % current_user,
|
||||
dest_url=scu.ScoURL(),
|
||||
)
|
||||
if not dialog_confirmed:
|
||||
|
@ -2260,11 +2254,13 @@ sco_publish(
|
|||
"/view_apo_csv_store",
|
||||
sco_etape_apogee_view.view_apo_csv_store,
|
||||
Permission.ScoEditApo,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish(
|
||||
"/view_apo_csv_download_and_store",
|
||||
sco_etape_apogee_view.view_apo_csv_download_and_store,
|
||||
Permission.ScoEditApo,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish(
|
||||
"/view_apo_csv_delete",
|
||||
|
@ -2510,7 +2506,7 @@ def check_form_integrity(formation_id, fix=False, REQUEST=None):
|
|||
log("check_form_integrity: formation_id=%s\ninconsistencies:" % formation_id)
|
||||
log(txt)
|
||||
# Notify by e-mail
|
||||
sendAlarm("Notes: formation incoherente !", txt)
|
||||
send_scodoc_alarm("Notes: formation incoherente !", txt)
|
||||
else:
|
||||
txth = "OK"
|
||||
log("ok")
|
||||
|
|
|
@ -68,13 +68,8 @@ from app.scodoc.sco_exceptions import (
|
|||
AccessDenied,
|
||||
ScoException,
|
||||
ScoValueError,
|
||||
ScoInvalidDateError,
|
||||
ScoLockedFormError,
|
||||
ScoGenError,
|
||||
ScoInvalidDept,
|
||||
)
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||
import sco_version
|
||||
import app
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import html_sco_header
|
||||
|
@ -267,7 +262,7 @@ def showEtudLog(etudid, format="html", REQUEST=None):
|
|||
rows=ops,
|
||||
html_sortable=True,
|
||||
html_class="table_leftalign",
|
||||
base_url="%s?etudid=%s" % (REQUEST.URL0, etudid),
|
||||
base_url="%s?etudid=%s" % (request.base_url, etudid),
|
||||
page_title="Opérations sur %(nomprenom)s" % etud,
|
||||
html_title="<h2>Opérations effectuées sur l'étudiant %(nomprenom)s</h2>" % etud,
|
||||
filename="log_" + scu.make_filename(etud["nomprenom"]),
|
||||
|
@ -279,7 +274,7 @@ def showEtudLog(etudid, format="html", REQUEST=None):
|
|||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
# ---------- PAGE ACCUEIL (listes) --------------
|
||||
|
@ -341,7 +336,7 @@ def getEtudInfo(etudid=False, code_nip=False, filled=False, REQUEST=None, format
|
|||
if format is None:
|
||||
return etud
|
||||
else:
|
||||
return scu.sendResult(REQUEST, etud, name="etud", format=format)
|
||||
return scu.sendResult(etud, name="etud", format=format)
|
||||
|
||||
|
||||
sco_publish(
|
||||
|
@ -396,7 +391,7 @@ def etud_info(etudid=None, format="xml", REQUEST=None):
|
|||
"error": "code etudiant inconnu",
|
||||
}
|
||||
return scu.sendResult(
|
||||
REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False
|
||||
d, name="etudiant", format=format, force_outer_xml_tag=False
|
||||
)
|
||||
d = {}
|
||||
etud = etuds[0]
|
||||
|
@ -464,9 +459,7 @@ def etud_info(etudid=None, format="xml", REQUEST=None):
|
|||
)
|
||||
|
||||
log("etud_info (%gs)" % (time.time() - t0))
|
||||
return scu.sendResult(
|
||||
REQUEST, d, name="etudiant", format=format, force_outer_xml_tag=False
|
||||
)
|
||||
return scu.sendResult(d, name="etudiant", format=format, force_outer_xml_tag=False)
|
||||
|
||||
|
||||
# -------------------------- FICHE ETUDIANT --------------------------
|
||||
|
@ -617,7 +610,7 @@ def formChangeCoordonnees(etudid, REQUEST):
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("adresse_id", {"input_type": "hidden"}),
|
||||
|
@ -817,13 +810,13 @@ def formChangePhoto(etudid=None, REQUEST=None):
|
|||
<p>Photo actuelle (%(photoloc)s):
|
||||
"""
|
||||
% etud,
|
||||
sco_photos.etud_photo_html(etud, title="photo actuelle", REQUEST=REQUEST),
|
||||
sco_photos.etud_photo_html(etud, title="photo actuelle"),
|
||||
"""</p><p>Le fichier ne doit pas dépasser 500Ko (recadrer l'image, format "portrait" de préférence).</p>
|
||||
<p>L'image sera automagiquement réduite pour obtenir une hauteur de 90 pixels.</p>
|
||||
""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
("etudid", {"default": etudid, "input_type": "hidden"}),
|
||||
|
@ -858,7 +851,7 @@ def formChangePhoto(etudid=None, REQUEST=None):
|
|||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
@bp.route("/formSuppressPhoto")
|
||||
@bp.route("/formSuppressPhoto", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoEtudChangeAdr)
|
||||
@scodoc7func
|
||||
|
@ -1496,7 +1489,7 @@ def _etudident_create_or_edit_form(REQUEST, edit):
|
|||
]
|
||||
initvalues["dont_check_homonyms"] = False
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
submitlabel=submitlabel,
|
||||
|
@ -1647,7 +1640,6 @@ def check_group_apogee(group_id, REQUEST=None, etat=None, fix=False, fixmail=Fal
|
|||
cnx = ndb.GetDBConnexion()
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Etudiants du %s" % (group["group_name"] or "semestre"),
|
||||
sem,
|
||||
),
|
||||
|
@ -1748,7 +1740,7 @@ def check_group_apogee(group_id, REQUEST=None, etat=None, fix=False, fixmail=Fal
|
|||
<p><a href="Notes/formsemestre_status?formsemestre_id=%s"> Retour au semestre</a>
|
||||
"""
|
||||
% (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
scu.strnone(group_id),
|
||||
scu.strnone(etat),
|
||||
|
@ -1767,7 +1759,7 @@ def check_group_apogee(group_id, REQUEST=None, etat=None, fix=False, fixmail=Fal
|
|||
<p><a href="Notes/formsemestre_status?formsemestre_id=%s"> Retour au semestre</a>
|
||||
"""
|
||||
% (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
scu.strnone(group_id),
|
||||
scu.strnone(etat),
|
||||
|
@ -1856,7 +1848,7 @@ def form_students_import_excel(REQUEST, formsemestre_id=None):
|
|||
|
||||
F = html_sco_header.sco_footer()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -1937,7 +1929,10 @@ def import_generate_excel_sample(REQUEST, with_codesemestre="1"):
|
|||
data = sco_import_etuds.sco_import_generate_excel_sample(
|
||||
format, with_codesemestre, exclude_cols=["photo_filename"]
|
||||
)
|
||||
return sco_excel.send_excel_file(REQUEST, data, "ImportEtudiants" + scu.XLSX_SUFFIX)
|
||||
return scu.send_file(
|
||||
data, "ImportEtudiants", scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE
|
||||
)
|
||||
# return sco_excel.send_excel_file(REQUEST, data, "ImportEtudiants" + scu.XLSX_SUFFIX)
|
||||
|
||||
|
||||
# --- Données admission
|
||||
|
@ -1955,9 +1950,10 @@ def import_generate_admission_sample(REQUEST, formsemestre_id):
|
|||
exclude_cols=["nationalite", "foto", "photo_filename"],
|
||||
group_ids=[group["group_id"]],
|
||||
)
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, "AdmissionEtudiants" + scu.XLSX_SUFFIX
|
||||
return scu.send_file(
|
||||
data, "AdmissionEtudiants", scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE
|
||||
)
|
||||
# return sco_excel.send_excel_file(REQUEST, data, "AdmissionEtudiants" + scu.XLSX_SUFFIX)
|
||||
|
||||
|
||||
# --- Données admission depuis fichier excel (version nov 2016)
|
||||
|
@ -1967,7 +1963,7 @@ def import_generate_admission_sample(REQUEST, formsemestre_id):
|
|||
@scodoc7func
|
||||
def form_students_import_infos_admissions(REQUEST, formsemestre_id=None):
|
||||
"formulaire import xls"
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
F = html_sco_header.sco_footer()
|
||||
if not authuser.has_permission(Permission.ScoEtudInscrit):
|
||||
# autorise juste l'export
|
||||
|
@ -2021,7 +2017,7 @@ def form_students_import_infos_admissions(REQUEST, formsemestre_id=None):
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -2084,7 +2080,7 @@ def formsemestre_import_etud_admission(
|
|||
formsemestre_id, import_identite=True, import_email=import_email
|
||||
)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Reimport données admission"),
|
||||
html_sco_header.html_sem_header("Reimport données admission"),
|
||||
"<h3>Opération effectuée</h3>",
|
||||
]
|
||||
if no_nip:
|
||||
|
|
|
@ -65,8 +65,6 @@ from app import log
|
|||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc.sco_permissions_check import can_handle_passwd
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator, tf_error_message
|
||||
from app.scodoc.sco_excel import send_excel_file
|
||||
from app.scodoc.sco_import_users import generate_excel_sample
|
||||
from app.views import users_bp as bp
|
||||
|
||||
|
||||
|
@ -90,7 +88,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
|||
@scodoc7func
|
||||
def user_info(user_name, format="json", REQUEST=None):
|
||||
info = sco_users.user_info(user_name)
|
||||
return scu.sendResult(REQUEST, info, name="user", format=format)
|
||||
return scu.sendResult(info, name="user", format=format)
|
||||
|
||||
|
||||
@bp.route("/create_user_form", methods=["GET", "POST"])
|
||||
|
@ -344,7 +342,7 @@ def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1):
|
|||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
|
@ -490,9 +488,7 @@ def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1):
|
|||
def import_users_generate_excel_sample(REQUEST):
|
||||
"une feuille excel pour importation utilisateurs"
|
||||
data = sco_import_users.generate_excel_sample()
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, "ImportUtilisateurs" + scu.XLSX_SUFFIX
|
||||
)
|
||||
return scu.send_file(data, "ImportUtilisateurs", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
||||
|
||||
|
||||
@bp.route("/import_users_form", methods=["GET", "POST"])
|
||||
|
@ -532,7 +528,7 @@ def import_users_form(REQUEST=None):
|
|||
)
|
||||
F = html_sco_header.sco_footer()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
REQUEST.form,
|
||||
(
|
||||
(
|
||||
|
@ -546,7 +542,7 @@ def import_users_form(REQUEST=None):
|
|||
if tf[0] == 0:
|
||||
return "\n".join(H) + tf[1] + "</li></ol>" + help + F
|
||||
elif tf[0] == -1:
|
||||
return flask.redirect(back_url)
|
||||
return flask.redirect(url_for("scolar.index_html", docodc_dept=g.scodoc_dept))
|
||||
else:
|
||||
# IMPORT
|
||||
ok, diag, nb_created = sco_import_users.import_excel_file(tf[2]["xlsfile"])
|
||||
|
@ -654,8 +650,8 @@ def change_password(user_name, password, password2, REQUEST):
|
|||
if not can_handle_passwd(u):
|
||||
# access denied
|
||||
log(
|
||||
"change_password: access denied (authuser=%s, user_name=%s, ip=%s)"
|
||||
% (REQUEST.AUTHENTICATED_USER, user_name, REQUEST.REMOTE_ADDR)
|
||||
"change_password: access denied (authuser=%s, user_name=%s)"
|
||||
% (current_user, user_name)
|
||||
)
|
||||
raise AccessDenied("vous n'avez pas la permission de changer ce mot de passe")
|
||||
H = []
|
||||
|
|
|
@ -30,6 +30,8 @@ class Config:
|
|||
SCODOC_DIR = os.environ.get("SCODOC_DIR", "/opt/scodoc")
|
||||
SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "/opt/scodoc-data")
|
||||
SCODOC_LOG_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc.log")
|
||||
# evite confusion avec le log nginx scodoc_error.log:
|
||||
SCODOC_ERR_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc_exc.log")
|
||||
#
|
||||
MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # Flask uploads
|
||||
|
||||
|
@ -53,7 +55,7 @@ class DevConfig(Config):
|
|||
DEBUG = True
|
||||
TESTING = False
|
||||
SQLALCHEMY_DATABASE_URI = (
|
||||
os.environ.get("SCODOC_DEV_DATABASE_URI") or "postgresql:///SCODOC_DEV"
|
||||
os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC_DEV"
|
||||
)
|
||||
SECRET_KEY = os.environ.get("DEV_SECRET_KEY") or "bb3faec7d9a34eb68a8e3e710087d87a"
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
"""Flag bloquage calcul moyennes
|
||||
|
||||
Revision ID: 669065fb2d20
|
||||
Revises: a217bf588f4c
|
||||
Create Date: 2021-09-16 22:04:11.624632
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '669065fb2d20'
|
||||
down_revision = 'a217bf588f4c'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('notes_formsemestre', sa.Column('block_moyennes', sa.Boolean(), server_default='false', nullable=False))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('notes_formsemestre', 'block_moyennes')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,30 @@
|
|||
"""modif contrainte sur formations
|
||||
|
||||
Revision ID: f86c013c9fbd
|
||||
Revises: 669065fb2d20
|
||||
Create Date: 2021-09-19 21:30:42.240422
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f86c013c9fbd'
|
||||
down_revision = '669065fb2d20'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', type_='unique')
|
||||
op.create_unique_constraint(None, 'notes_formations', ['dept_id', 'acronyme', 'titre', 'version'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_constraint(None, 'notes_formations', type_='unique')
|
||||
op.create_unique_constraint('notes_formations_acronyme_titre_version_key', 'notes_formations', ['acronyme', 'titre', 'version'])
|
||||
# ### end Alembic commands ###
|
|
@ -1,68 +1,69 @@
|
|||
|
||||
Notes sur la restauration de la base SQL complete
|
||||
Notes sur la restauration de la base SQL complete (ScoDoc 9)
|
||||
(dans le cas d'une réinstallation sur une autre machine, par exemple)
|
||||
|
||||
|
||||
1) Sur la machine origine, faire un dump complet:
|
||||
su postgres
|
||||
cd /tmp # ou ailleurs...
|
||||
pg_dumpall > scodoc.dump.txt
|
||||
pg_dumpall | gzip > scodoc.dump.txt.gz
|
||||
|
||||
On obtient un fichier texte assez volumineux (on peut utiliser gzip pour le compresser avant transfert).
|
||||
On obtient un fichier texte assez volumineux (comprimé par gzip ci-dessus)
|
||||
|
||||
Le copier sur la machine destination.
|
||||
Le copier sur la machine destination, et le décompresser (gunzip).
|
||||
|
||||
2) Sur la machine destination:
|
||||
|
||||
Avant toute chose, stopper scodoc:
|
||||
/etc/init.d/scodoc stop (ou systemctl stop scodoc))
|
||||
|
||||
systemctl stop scodoc9
|
||||
|
||||
1.1) Supprimer toutes les bases ScoDoc existantes s'il y en a:
|
||||
su postgres
|
||||
su scodoc
|
||||
psql -l
|
||||
liste les bases: celles de ScoDoc sont SCO*
|
||||
|
||||
Pour chaque base SCO*, faire dropdb
|
||||
dropdb SCOUSERS
|
||||
dropdb SCOGEII
|
||||
dropdb SCODOC
|
||||
dropdb SCODOC_DEV
|
||||
...
|
||||
|
||||
Pour les feignants, voici un script (à lancer comme utilisateur postgres):
|
||||
Pour les gens pressés, voici un script (à lancer comme utilisateur postgres):
|
||||
for f in $(psql -l --no-align --field-separator . | grep SCO | cut -f 1 -d.); do
|
||||
echo dropping $f
|
||||
dropdb $f
|
||||
done
|
||||
|
||||
1.2) Charger le dump (toujours comme utilisateur postgres):
|
||||
psql -f scodoc.dump.txt postgres
|
||||
1.2) Charger le dump (toujours comme utilisateur scodoc):
|
||||
psql -f scodoc.dump.txt scodoc
|
||||
|
||||
1.3) Recopier les fichiers (photos, config, archives): copier le repertoire complet
|
||||
/opt/scodoc/instance/var
|
||||
/opt/scodoc-data
|
||||
de la machine origine vers la nouvelle
|
||||
|
||||
|
||||
Puis redemarrer ScoDoc:
|
||||
en tant que root: /etc/init.d/scodoc start (ou systemctl start scodoc)
|
||||
en tant que root: systemctl start scodoc9
|
||||
|
||||
NB: si la version des sources a changée, lancer imperativement le script de mise a jour
|
||||
avant de redemarrer scodoc, afin qu'il change si besoin la base de donnees:
|
||||
(en tant que root):
|
||||
cd /opt/scodoc/instance/Products/ScoDoc/config
|
||||
./upgrade.sh
|
||||
apt-get update &&
|
||||
|
||||
|
||||
----
|
||||
Cas d'une seule base à copier: (eg un seul département, mais faire
|
||||
attention aux utilisateurs definis dans la base SCOUSERS):
|
||||
Cas d'une seule base à copier (base production ou dev. par exemple)
|
||||
|
||||
En tant qu'utilisateur "postgres":
|
||||
Dump: (script avec commande de creation de la base)
|
||||
pg_dump --create SCOINFO > /tmp/scoinfo.dump
|
||||
En tant qu'utilisateur "scodoc":
|
||||
Dump: permettant de la recharger en changeant son nom
|
||||
pg_dump --format=custom --file=/tmp/SCODOC.dump SCODOC
|
||||
|
||||
Restore: (si necessaire, utiliser dropdb avant)
|
||||
psql -f /tmp/scoinfo.dump postgres
|
||||
createdb SCODOC_IUTV
|
||||
pg_restore -d SCODOC_IUTV SCODOC.dump
|
||||
(si on veut garder le même nom de base que l'origine, utiliser --create )
|
||||
|
||||
---
|
||||
--- à revoir
|
||||
Cas d'un dump via sco_dump_db (assistance):
|
||||
createdb -E UTF-8 SCOXXX
|
||||
zcat xxx | psql SCOXXX
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
-- Creation des tables pour gestion notes
|
||||
-- Creation des tables pour gestion notes ScoDoc 7 (OBSOLETE !)
|
||||
-- E. Viennet, Sep 2005
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
SCOVERSION = "9.0.23"
|
||||
SCOVERSION = "9.0.37"
|
||||
|
||||
SCONAME = "ScoDoc"
|
||||
|
||||
|
|
21
scodoc.py
21
scodoc.py
|
@ -264,6 +264,17 @@ def create_dept(dept): # create-dept
|
|||
return 0
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.argument("depts", nargs=-1)
|
||||
def list_depts(depts=""): # list-dept
|
||||
"""If dept exists, print it, else nothing.
|
||||
Called without arguments, list all depts along with their ids.
|
||||
"""
|
||||
for dept in models.Departement.query.order_by(models.Departement.id):
|
||||
if not depts or dept.acronym in depts:
|
||||
print(f"{dept.id}\t{dept.acronym}")
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@with_appcontext
|
||||
def import_scodoc7_users(): # import-scodoc7-users
|
||||
|
@ -282,12 +293,20 @@ def import_scodoc7_users(): # import-scodoc7-users
|
|||
@click.argument("dept")
|
||||
@click.argument("dept_db_name")
|
||||
@with_appcontext
|
||||
def import_scodoc7_dept(dept: str, dept_db_name: str): # import-scodoc7-dept
|
||||
def import_scodoc7_dept(dept: str, dept_db_name: str = ""): # import-scodoc7-dept
|
||||
"""Import département ScoDoc 7: dept: InfoComm, dept_db_name: SCOINFOCOMM"""
|
||||
dept_db_uri = f"postgresql:///{dept_db_name}"
|
||||
tools.import_scodoc7_dept(dept, dept_db_uri)
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@click.argument("dept", default="")
|
||||
@with_appcontext
|
||||
def migrate_scodoc7_dept_archive(dept: str): # migrate-scodoc7-dept-archive
|
||||
"""Post-migration: renomme les archives en fonction des id de ScoDoc 9"""
|
||||
tools.migrate_scodoc7_dept_archive(dept)
|
||||
|
||||
|
||||
@app.cli.command()
|
||||
@with_appcontext
|
||||
def clear_cache(): # clear-cache
|
||||
|
|
|
@ -1,62 +1,62 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Scenario: préparation base de données pour tests Selenium
|
||||
|
||||
S'utilise comme un test avec pytest, mais n'est pas un test !
|
||||
Modifie la base de données du département TEST00
|
||||
|
||||
Usage: pytest tests/scenarios/test_scenario1_formation.py
|
||||
"""
|
||||
# code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
|
||||
|
||||
import random
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_moduleimpl
|
||||
|
||||
|
||||
def test_scenario1(test_client):
|
||||
"""Applique "scenario 1"""
|
||||
run_scenario1()
|
||||
|
||||
|
||||
def run_scenario1():
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# Lecture fichier XML local:
|
||||
with open("tests/unit/formation-exemple-1.xml") as f:
|
||||
doc = f.read()
|
||||
|
||||
# --- Création de la formation
|
||||
f = sco_formations.formation_import_xml(doc=doc)
|
||||
|
||||
# --- Création des semestres
|
||||
formation_id = f[0]
|
||||
# --- Mise en place de 4 semestres
|
||||
sems = [
|
||||
G.create_formsemestre(
|
||||
formation_id=formation_id,
|
||||
semestre_id=x[0],
|
||||
date_debut=x[1],
|
||||
date_fin=x[2],
|
||||
)
|
||||
for x in (
|
||||
(1, "01/09/2020", "01/02/2021"),
|
||||
(2, "02/02/2021", "01/06/2021"),
|
||||
(3, "01/09/2020", "01/02/2021"),
|
||||
(4, "02/02/2021", "01/06/2021"),
|
||||
)
|
||||
]
|
||||
|
||||
# --- Implémentation des modules
|
||||
modules = sco_edit_module.do_module_list({"formation_id": formation_id})
|
||||
mods_imp = []
|
||||
for mod in modules:
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"],
|
||||
)
|
||||
mods_imp.append(mi)
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Scenario: préparation base de données pour tests Selenium
|
||||
|
||||
S'utilise comme un test avec pytest, mais n'est pas un test !
|
||||
Modifie la base de données du département TEST00
|
||||
|
||||
Usage: pytest tests/scenarios/test_scenario1_formation.py
|
||||
"""
|
||||
# code écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en août 2021
|
||||
|
||||
import random
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_moduleimpl
|
||||
|
||||
|
||||
def test_scenario1(test_client):
|
||||
"""Applique "scenario 1"""
|
||||
run_scenario1()
|
||||
|
||||
|
||||
def run_scenario1():
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# Lecture fichier XML local:
|
||||
with open("tests/unit/formation-exemple-1.xml") as f:
|
||||
doc = f.read()
|
||||
|
||||
# --- Création de la formation
|
||||
f = sco_formations.formation_import_xml(doc=doc)
|
||||
|
||||
# --- Création des semestres
|
||||
formation_id = f[0]
|
||||
# --- Mise en place de 4 semestres
|
||||
sems = [
|
||||
G.create_formsemestre(
|
||||
formation_id=formation_id,
|
||||
semestre_id=x[0],
|
||||
date_debut=x[1],
|
||||
date_fin=x[2],
|
||||
)
|
||||
for x in (
|
||||
(1, "01/09/2020", "01/02/2021"),
|
||||
(2, "02/02/2021", "01/06/2021"),
|
||||
(3, "01/09/2020", "01/02/2021"),
|
||||
(4, "02/02/2021", "01/06/2021"),
|
||||
)
|
||||
]
|
||||
|
||||
# --- Implémentation des modules
|
||||
modules = sco_edit_module.do_module_list({"formation_id": formation_id})
|
||||
mods_imp = []
|
||||
for mod in modules:
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"],
|
||||
)
|
||||
mods_imp.append(mi)
|
||||
|
|
|
@ -219,6 +219,7 @@ class ScoFake(object):
|
|||
etat=None,
|
||||
gestion_compensation=None,
|
||||
bul_hide_xml=None,
|
||||
block_moyennes=None,
|
||||
gestion_semestrielle=None,
|
||||
bul_bgcolor=None,
|
||||
modalite=NotesFormModalite.DEFAULT_MODALITE,
|
||||
|
|
|
@ -1,123 +1,123 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Comptage des absences
|
||||
"""
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
import json
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_groups
|
||||
from app.views import absences
|
||||
|
||||
|
||||
def test_abs_counts(test_client):
|
||||
"""Comptage des absences"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etud = G.create_etud(code_nip=None)
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etud["etudid"]
|
||||
|
||||
for debut, fin, demijournee in [
|
||||
("01/01/2020", "31/01/2020", 2), # hors semestre
|
||||
("15/01/2021", "15/01/2021", 1),
|
||||
("18/01/2021", "18/01/2021", 0),
|
||||
("19/01/2021", "19/01/2021", 2),
|
||||
("22/01/2021", "22/01/2021", 1),
|
||||
("30/06/2021", "30/06/2021", 2), # dernier jour
|
||||
]:
|
||||
sco_abs_views.doSignaleAbsence(
|
||||
datedebut=debut,
|
||||
datefin=fin,
|
||||
demijournee=demijournee,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
for debut, fin, demijournee in [
|
||||
("15/01/2021", "15/01/2021", 1),
|
||||
("18/01/2021", "18/01/2021", 0),
|
||||
("19/01/2021", "19/01/2021", 2),
|
||||
]:
|
||||
sco_abs_views.doJustifAbsence(
|
||||
datedebut=debut,
|
||||
datefin=fin,
|
||||
demijournee=demijournee,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Utilisation de get_abs_count() de sco_abs
|
||||
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
|
||||
# --- Utilisation de sco_abs.count_abs()
|
||||
|
||||
nb_abs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
|
||||
nb_absj2 = sco_abs.count_abs_just(
|
||||
etudid=etudid, debut="2021-01-01", fin="2021-06-30"
|
||||
)
|
||||
|
||||
assert nbabs == nb_abs2 == 7
|
||||
assert nbabsjust == nb_absj2 == 4
|
||||
|
||||
# --- Nombre de justificatifs:
|
||||
justifs = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs) == 4
|
||||
|
||||
# --- Suppression d'absence
|
||||
_ = sco_abs_views.doAnnuleAbsence("19/01/2021", "19/01/2021", 2, etudid=etudid)
|
||||
|
||||
# --- Vérification
|
||||
justifs_2 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs_2) == len(justifs)
|
||||
new_nbabs, _ = sco_abs.get_abs_count(etudid, sem) # version cachée
|
||||
new_nbabs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
|
||||
|
||||
assert new_nbabs == new_nbabs2
|
||||
assert new_nbabs == (nbabs - 2) # on a supprimé deux absences
|
||||
|
||||
# --- annulation absence sans supprimer le justificatif
|
||||
sco_abs_views.AnnuleAbsencesDatesNoJust(etudid, ["2021-01-15"])
|
||||
nbabs_3, nbjust_3 = sco_abs.get_abs_count(etudid, sem)
|
||||
assert nbabs_3 == new_nbabs
|
||||
justifs_3 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs_3) == len(justifs_2)
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Comptage des absences
|
||||
"""
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
import json
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_groups
|
||||
from app.views import absences
|
||||
|
||||
|
||||
def test_abs_counts(test_client):
|
||||
"""Comptage des absences"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etud = G.create_etud(code_nip=None)
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etud["etudid"]
|
||||
|
||||
for debut, fin, demijournee in [
|
||||
("01/01/2020", "31/01/2020", 2), # hors semestre
|
||||
("15/01/2021", "15/01/2021", 1),
|
||||
("18/01/2021", "18/01/2021", 0),
|
||||
("19/01/2021", "19/01/2021", 2),
|
||||
("22/01/2021", "22/01/2021", 1),
|
||||
("30/06/2021", "30/06/2021", 2), # dernier jour
|
||||
]:
|
||||
sco_abs_views.doSignaleAbsence(
|
||||
datedebut=debut,
|
||||
datefin=fin,
|
||||
demijournee=demijournee,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
for debut, fin, demijournee in [
|
||||
("15/01/2021", "15/01/2021", 1),
|
||||
("18/01/2021", "18/01/2021", 0),
|
||||
("19/01/2021", "19/01/2021", 2),
|
||||
]:
|
||||
sco_abs_views.doJustifAbsence(
|
||||
datedebut=debut,
|
||||
datefin=fin,
|
||||
demijournee=demijournee,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Utilisation de get_abs_count() de sco_abs
|
||||
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
|
||||
# --- Utilisation de sco_abs.count_abs()
|
||||
|
||||
nb_abs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
|
||||
nb_absj2 = sco_abs.count_abs_just(
|
||||
etudid=etudid, debut="2021-01-01", fin="2021-06-30"
|
||||
)
|
||||
|
||||
assert nbabs == nb_abs2 == 7
|
||||
assert nbabsjust == nb_absj2 == 4
|
||||
|
||||
# --- Nombre de justificatifs:
|
||||
justifs = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs) == 4
|
||||
|
||||
# --- Suppression d'absence
|
||||
_ = sco_abs_views.doAnnuleAbsence("19/01/2021", "19/01/2021", 2, etudid=etudid)
|
||||
|
||||
# --- Vérification
|
||||
justifs_2 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs_2) == len(justifs)
|
||||
new_nbabs, _ = sco_abs.get_abs_count(etudid, sem) # version cachée
|
||||
new_nbabs2 = sco_abs.count_abs(etudid=etudid, debut="2021-01-01", fin="2021-06-30")
|
||||
|
||||
assert new_nbabs == new_nbabs2
|
||||
assert new_nbabs == (nbabs - 2) # on a supprimé deux absences
|
||||
|
||||
# --- annulation absence sans supprimer le justificatif
|
||||
sco_abs_views.AnnuleAbsencesDatesNoJust(etudid, ["2021-01-15"])
|
||||
nbabs_3, nbjust_3 = sco_abs.get_abs_count(etudid, sem)
|
||||
assert nbabs_3 == new_nbabs
|
||||
justifs_3 = sco_abs.list_abs_justifs(etudid, "2021-01-01", datefin="2021-06-30")
|
||||
assert len(justifs_3) == len(justifs_2)
|
||||
# XXX à continuer
|
|
@ -1,335 +1,339 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Créer et justifier des absences en utilisant le parametre demijournee
|
||||
"""
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
import json
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_groups
|
||||
from app.views import absences
|
||||
|
||||
|
||||
def test_abs_demijournee(test_client):
|
||||
"""Opération élémentaires sur les absences, tests demi-journées
|
||||
Travaille dans base TEST00 (defaut)
|
||||
"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etud = G.create_etud(code_nip=None)
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etud["etudid"]
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# NE JUSTIFIE QUE LE MATIN MALGRES LE PARAMETRE demijournee = 2
|
||||
|
||||
# --- Test
|
||||
|
||||
nbabs, nbabs_just = sco_abs.get_abs_count(etudid, sem)
|
||||
assert (
|
||||
nbabs == 4
|
||||
) # l'étudiant a été absent le 15 journée compléte (2 abs : 1 matin, 1 apres midi) et le 18 (1 matin), et le 19 (1 apres midi).
|
||||
assert nbabs_just == 2 # Justifie abs du matin + abs après midi
|
||||
|
||||
|
||||
def test_abs_basic(test_client):
|
||||
"""creation de 10 étudiants, formation, semestre, ue, module, absences le matin, l'apres midi, la journée compléte
|
||||
et justification d'absences, supression d'absences, création d'une liste etat absences, creation d'un groupe afin
|
||||
de tester la fonction EtatAbsencesGroupes
|
||||
|
||||
Fonctions de l'API utilisé :
|
||||
- doSignaleAbsence
|
||||
- doAnnuleAbsence
|
||||
- doJustifAbsence
|
||||
- get_partition_groups
|
||||
- get_partitions_list
|
||||
- sco_abs.get_abs_count(etudid, sem)
|
||||
- ListeAbsEtud
|
||||
- partition_create
|
||||
- createGroup
|
||||
- set_group
|
||||
- EtatAbsenceGr
|
||||
- AddBilletAbsence
|
||||
- listeBilletsEtud
|
||||
"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etuds = [G.create_etud(code_nip=None) for _ in range(10)]
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
for etud in etuds:
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Création d'une évaluation
|
||||
e = G.create_evaluation(
|
||||
moduleimpl_id=mi["moduleimpl_id"],
|
||||
jour="22/01/2021",
|
||||
description="evaluation test",
|
||||
coefficient=1.0,
|
||||
)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etuds[0]["etudid"]
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"22/01/2021",
|
||||
"22/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Test
|
||||
|
||||
b = sco_abs.is_work_saturday()
|
||||
assert b == 0 # samedi ne sont pas compris
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
# l'étudiant a été absent le 15 (apres midi) , (16 et 17 we),
|
||||
# 18 (matin) et 19 janvier (matin et apres midi), et 22 (matin)
|
||||
assert nbabs == 5
|
||||
# l'étudiant justifie ses abs du 15, 18 et 19
|
||||
assert nbabsjust == 4
|
||||
|
||||
# --- Suppression d'une absence et d'une justification
|
||||
|
||||
_ = sco_abs_views.doAnnuleAbsence("19/01/2021", "19/01/2021", 2, etudid=etudid)
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
assert nbabs == 3
|
||||
assert nbabsjust == 2
|
||||
|
||||
# --- suppression d'une justification pas encore disponible à l'aide de python.
|
||||
|
||||
# --- Création d'une liste d'abs
|
||||
|
||||
liste_abs = sco_abs_views.ListeAbsEtud(
|
||||
etudid, format="json", absjust_only=1, sco_year="2020"
|
||||
)
|
||||
liste_abs2 = sco_abs_views.ListeAbsEtud(etudid, format="json", sco_year="2020")
|
||||
|
||||
load_liste_abs = json.loads(liste_abs)
|
||||
load_liste_abs2 = json.loads(liste_abs2)
|
||||
|
||||
assert len(load_liste_abs2) == 1
|
||||
assert len(load_liste_abs) == 2
|
||||
assert load_liste_abs2[0]["ampm"] == 1
|
||||
assert load_liste_abs2[0]["datedmy"] == "22/01/2021"
|
||||
assert load_liste_abs2[0]["exams"] == mod["code"]
|
||||
# absjust_only -> seulement les abs justifiés
|
||||
|
||||
# --- Création d'un groupe
|
||||
|
||||
_ = sco_groups.partition_create(
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
partition_name="Eleve",
|
||||
)
|
||||
li1 = sco_groups.get_partitions_list(sem["formsemestre_id"])
|
||||
_ = sco_groups.createGroup(li1[0]["partition_id"], "Groupe 1")
|
||||
|
||||
# --- Affectation des élèves dans des groupes
|
||||
|
||||
li_grp1 = sco_groups.get_partition_groups(li1[0])
|
||||
for etud in etuds:
|
||||
sco_groups.set_group(etud["etudid"], li_grp1[0]["group_id"])
|
||||
|
||||
# --- Test de EtatAbsencesGroupes
|
||||
|
||||
grp1_abs = absences.EtatAbsencesGr(
|
||||
group_ids=[li_grp1[0]["group_id"]],
|
||||
debut="01/01/2021",
|
||||
fin="30/06/2021",
|
||||
format="json",
|
||||
)
|
||||
# grp1_abs est une Response car on a appelé une vue (1er appel)
|
||||
load_grp1_abs = json.loads(grp1_abs.get_data().decode("utf-8"))
|
||||
|
||||
assert len(load_grp1_abs) == 10
|
||||
|
||||
tab_id = [] # tab des id present dans load_grp1_abs
|
||||
for un_etud in load_grp1_abs:
|
||||
tab_id.append(un_etud["etudid"])
|
||||
|
||||
for (
|
||||
etud
|
||||
) in (
|
||||
etuds
|
||||
): # verification si tous les etudiants sont present dans la liste du groupe d'absence
|
||||
assert etud["etudid"] in tab_id
|
||||
|
||||
for un_etud in load_grp1_abs:
|
||||
if un_etud["etudid"] == etudid:
|
||||
assert un_etud["nbabs"] == 3
|
||||
assert un_etud["nbjustifs_noabs"] == 2
|
||||
assert un_etud["nbabsjust"] == 2
|
||||
assert un_etud["nbabsnonjust"] == 1
|
||||
assert un_etud["nomprenom"] == etuds[0]["nomprenom"]
|
||||
|
||||
# --- Création de billets
|
||||
|
||||
b1 = absences.AddBilletAbsence(
|
||||
begin="2021-01-22 00:00",
|
||||
end="2021-01-22 23:59",
|
||||
etudid=etudid,
|
||||
description="abs du 22",
|
||||
justified=False,
|
||||
code_nip=etuds[0]["code_nip"],
|
||||
code_ine=etuds[0]["code_ine"],
|
||||
)
|
||||
|
||||
b2 = absences.AddBilletAbsence(
|
||||
begin="2021-01-15 00:00",
|
||||
end="2021-01-15 23:59",
|
||||
etudid=etudid,
|
||||
description="abs du 15",
|
||||
code_nip=etuds[0]["code_nip"],
|
||||
code_ine=etuds[0]["code_ine"],
|
||||
)
|
||||
|
||||
li_bi = absences.listeBilletsEtud(etudid=etudid, format="json")
|
||||
assert isinstance(li_bi, str)
|
||||
load_li_bi = json.loads(li_bi)
|
||||
|
||||
assert len(load_li_bi) == 2
|
||||
assert load_li_bi[1]["description"] == "abs du 22"
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Créer et justifier des absences en utilisant le parametre demijournee
|
||||
"""
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
import json
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_abs_views
|
||||
from app.scodoc import sco_groups
|
||||
from app.views import absences
|
||||
|
||||
|
||||
def test_abs_demijournee(test_client):
|
||||
"""Opération élémentaires sur les absences, tests demi-journées
|
||||
Travaille dans base TEST00 (defaut)
|
||||
"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etud = G.create_etud(code_nip=None)
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etud["etudid"]
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# NE JUSTIFIE QUE LE MATIN MALGRES LE PARAMETRE demijournee = 2
|
||||
|
||||
# --- Test
|
||||
|
||||
nbabs, nbabs_just = sco_abs.get_abs_count(etudid, sem)
|
||||
assert (
|
||||
nbabs == 4
|
||||
) # l'étudiant a été absent le 15 journée compléte (2 abs : 1 matin, 1 apres midi) et le 18 (1 matin), et le 19 (1 apres midi).
|
||||
assert nbabs_just == 2 # Justifie abs du matin + abs après midi
|
||||
|
||||
|
||||
def test_abs_basic(test_client):
|
||||
"""creation de 10 étudiants, formation, semestre, ue, module, absences le matin, l'apres midi, la journée compléte
|
||||
et justification d'absences, supression d'absences, création d'une liste etat absences, creation d'un groupe afin
|
||||
de tester la fonction EtatAbsencesGroupes
|
||||
|
||||
Fonctions de l'API utilisé :
|
||||
- doSignaleAbsence
|
||||
- doAnnuleAbsence
|
||||
- doJustifAbsence
|
||||
- get_partition_groups
|
||||
- get_partitions_list
|
||||
- sco_abs.get_abs_count(etudid, sem)
|
||||
- ListeAbsEtud
|
||||
- partition_create
|
||||
- createGroup
|
||||
- set_group
|
||||
- EtatAbsenceGr
|
||||
- AddBilletAbsence
|
||||
- listeBilletsEtud
|
||||
"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création d'étudiants
|
||||
etuds = [G.create_etud(code_nip=None) for _ in range(10)]
|
||||
|
||||
# --- Création d'une formation
|
||||
f = G.create_formation(acronyme="")
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Mise place d'un semestre
|
||||
sem = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Inscription des étudiants
|
||||
for etud in etuds:
|
||||
G.inscrit_etudiant(sem, etud)
|
||||
|
||||
# --- Création d'une évaluation
|
||||
e = G.create_evaluation(
|
||||
moduleimpl_id=mi["moduleimpl_id"],
|
||||
jour="22/01/2021",
|
||||
description="evaluation test",
|
||||
coefficient=1.0,
|
||||
)
|
||||
|
||||
# --- Saisie absences
|
||||
etudid = etuds[0]["etudid"]
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doSignaleAbsence(
|
||||
"22/01/2021",
|
||||
"22/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Justification de certaines absences
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"15/01/2021",
|
||||
"15/01/2021",
|
||||
demijournee=1,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"18/01/2021",
|
||||
"18/01/2021",
|
||||
demijournee=0,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
_ = sco_abs_views.doJustifAbsence(
|
||||
"19/01/2021",
|
||||
"19/01/2021",
|
||||
demijournee=2,
|
||||
etudid=etudid,
|
||||
)
|
||||
|
||||
# --- Test
|
||||
|
||||
b = sco_abs.is_work_saturday()
|
||||
assert b == 0 # samedi ne sont pas compris
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
# l'étudiant a été absent le 15 (apres midi) , (16 et 17 we),
|
||||
# 18 (matin) et 19 janvier (matin et apres midi), et 22 (matin)
|
||||
assert nbabs == 5
|
||||
# l'étudiant justifie ses abs du 15, 18 et 19
|
||||
assert nbabsjust == 4
|
||||
|
||||
# --- Suppression d'une absence et d'une justification
|
||||
|
||||
_ = sco_abs_views.doAnnuleAbsence("19/01/2021", "19/01/2021", 2, etudid=etudid)
|
||||
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
|
||||
assert nbabs == 3
|
||||
assert nbabsjust == 2
|
||||
|
||||
# --- suppression d'une justification pas encore disponible à l'aide de python.
|
||||
|
||||
# --- Création d'une liste d'abs
|
||||
|
||||
liste_abs = sco_abs_views.ListeAbsEtud(
|
||||
etudid, format="json", absjust_only=1, sco_year="2020"
|
||||
).get_data(as_text=True)
|
||||
liste_abs2 = sco_abs_views.ListeAbsEtud(
|
||||
etudid, format="json", sco_year="2020"
|
||||
).get_data(as_text=True)
|
||||
|
||||
load_liste_abs = json.loads(liste_abs)
|
||||
load_liste_abs2 = json.loads(liste_abs2)
|
||||
|
||||
assert len(load_liste_abs2) == 1
|
||||
assert len(load_liste_abs) == 2
|
||||
assert load_liste_abs2[0]["ampm"] == 1
|
||||
assert load_liste_abs2[0]["datedmy"] == "22/01/2021"
|
||||
assert load_liste_abs2[0]["exams"] == mod["code"]
|
||||
# absjust_only -> seulement les abs justifiés
|
||||
|
||||
# --- Création d'un groupe
|
||||
|
||||
_ = sco_groups.partition_create(
|
||||
formsemestre_id=sem["formsemestre_id"],
|
||||
partition_name="Eleve",
|
||||
)
|
||||
li1 = sco_groups.get_partitions_list(sem["formsemestre_id"])
|
||||
_ = sco_groups.createGroup(li1[0]["partition_id"], "Groupe 1")
|
||||
|
||||
# --- Affectation des élèves dans des groupes
|
||||
|
||||
li_grp1 = sco_groups.get_partition_groups(li1[0])
|
||||
for etud in etuds:
|
||||
sco_groups.set_group(etud["etudid"], li_grp1[0]["group_id"])
|
||||
|
||||
# --- Test de EtatAbsencesGroupes
|
||||
|
||||
grp1_abs = absences.EtatAbsencesGr(
|
||||
group_ids=[li_grp1[0]["group_id"]],
|
||||
debut="01/01/2021",
|
||||
fin="30/06/2021",
|
||||
format="json",
|
||||
)
|
||||
# grp1_abs est une Response car on a appelé une vue (1er appel)
|
||||
load_grp1_abs = json.loads(grp1_abs.get_data(as_text=True))
|
||||
|
||||
assert len(load_grp1_abs) == 10
|
||||
|
||||
tab_id = [] # tab des id present dans load_grp1_abs
|
||||
for un_etud in load_grp1_abs:
|
||||
tab_id.append(un_etud["etudid"])
|
||||
|
||||
for (
|
||||
etud
|
||||
) in (
|
||||
etuds
|
||||
): # verification si tous les etudiants sont present dans la liste du groupe d'absence
|
||||
assert etud["etudid"] in tab_id
|
||||
|
||||
for un_etud in load_grp1_abs:
|
||||
if un_etud["etudid"] == etudid:
|
||||
assert un_etud["nbabs"] == 3
|
||||
assert un_etud["nbjustifs_noabs"] == 2
|
||||
assert un_etud["nbabsjust"] == 2
|
||||
assert un_etud["nbabsnonjust"] == 1
|
||||
assert un_etud["nomprenom"] == etuds[0]["nomprenom"]
|
||||
|
||||
# --- Création de billets
|
||||
|
||||
b1 = absences.AddBilletAbsence(
|
||||
begin="2021-01-22 00:00",
|
||||
end="2021-01-22 23:59",
|
||||
etudid=etudid,
|
||||
description="abs du 22",
|
||||
justified=False,
|
||||
code_nip=etuds[0]["code_nip"],
|
||||
code_ine=etuds[0]["code_ine"],
|
||||
)
|
||||
|
||||
b2 = absences.AddBilletAbsence(
|
||||
begin="2021-01-15 00:00",
|
||||
end="2021-01-15 23:59",
|
||||
etudid=etudid,
|
||||
description="abs du 15",
|
||||
code_nip=etuds[0]["code_nip"],
|
||||
code_ine=etuds[0]["code_ine"],
|
||||
)
|
||||
|
||||
li_bi = absences.listeBilletsEtud(etudid=etudid, format="json").get_data(
|
||||
as_text=True
|
||||
)
|
||||
assert isinstance(li_bi, str)
|
||||
load_li_bi = json.loads(li_bi)
|
||||
|
||||
assert len(load_li_bi) == 2
|
||||
assert load_li_bi[1]["description"] == "abs du 22"
|
||||
|
|
|
@ -1,375 +1,379 @@
|
|||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Test creation/edition/import/export formations
|
||||
"""
|
||||
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
# Créer 2 formations, une test et une normale. Créer 2 semestres dans la formation normale et un
|
||||
# dans la formation test, créer 2 semestres dans la formation normale (un test et un normal),
|
||||
# 2 ue (un test et un normal), 2 modules (un test et un normal) et 2 matieres (test et normal).
|
||||
# Et dans la formations test, un semestre, un module, un ue et une matiere.
|
||||
# Afficher la liste de tout ca puis supprimer les ue, mod, mat et sem test ainsi
|
||||
# que la formation test. Afficher la liste des UE, formations et modules restante.
|
||||
#
|
||||
# Vérification :
|
||||
#
|
||||
# - Les listes initiales comprennent bien tout les éléments créés avec les bon noms etc
|
||||
# - La supression s'est bien effectué au niveau de scodoc web et en python
|
||||
# - Vérifier que les fonctions listes font bien la mise à jour après supression
|
||||
#
|
||||
# Fonction de l'API utilisé :
|
||||
#
|
||||
# - create_formation
|
||||
# - create_ue
|
||||
# - create_matiere
|
||||
# - create_module
|
||||
# - create_formsemestre
|
||||
# - create_moduleimpl
|
||||
# - formation_list
|
||||
# - formation_export
|
||||
# - formsemestre_list
|
||||
# - do_moduleimpl_list
|
||||
# - do_module_impl_with_module_list
|
||||
# - do_formsemestre_delete
|
||||
# - do_module_list
|
||||
# - do_module_delete
|
||||
# - do_matiere_list
|
||||
# - do_matiere_delete
|
||||
# - do_ue_list
|
||||
# - do_ue_delete
|
||||
# - do_formation_delete
|
||||
|
||||
import json
|
||||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_edit_formation
|
||||
from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_formsemestre_edit
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.views import notes
|
||||
|
||||
|
||||
def test_formations(test_client):
|
||||
"""Test création/édition/import/export formations"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création de formations
|
||||
|
||||
f = G.create_formation(
|
||||
acronyme="F1", titre="Formation 1", titre_officiel="Titre officiel 1"
|
||||
)
|
||||
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
ue2 = G.create_ue(formation_id=f["formation_id"], acronyme="TST2", titre="ue test2")
|
||||
mat2 = G.create_matiere(ue_id=ue2["ue_id"], titre="matière test2")
|
||||
mod2 = G.create_module(
|
||||
matiere_id=mat2["matiere_id"],
|
||||
code="TSM2",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue2["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
uet = G.create_ue(formation_id=f["formation_id"], acronyme="TSTt", titre="ue testt")
|
||||
matt = G.create_matiere(ue_id=uet["ue_id"], titre="matière testt")
|
||||
modt = G.create_module(
|
||||
matiere_id=matt["matiere_id"],
|
||||
code="TSMt",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=uet["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
f2 = G.create_formation(acronyme="", titre="Formation test")
|
||||
|
||||
ue3 = G.create_ue(
|
||||
formation_id=f2["formation_id"], acronyme="TST3", titre="ue test3"
|
||||
)
|
||||
mat3 = G.create_matiere(ue_id=ue3["ue_id"], titre="matière test3")
|
||||
mod3 = G.create_module(
|
||||
matiere_id=mat3["matiere_id"],
|
||||
code="TSM3",
|
||||
coefficient=1.0,
|
||||
titre="module test3",
|
||||
ue_id=ue3["ue_id"], # faiblesse de l'API
|
||||
formation_id=f2["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Création et implémentation des semestres
|
||||
|
||||
sem1 = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
sem2 = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=2,
|
||||
date_debut="01/09/2020",
|
||||
date_fin="31/12/2020",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem1["formsemestre_id"],
|
||||
)
|
||||
|
||||
mi2 = G.create_moduleimpl(
|
||||
module_id=mod2["module_id"],
|
||||
formsemestre_id=sem1["formsemestre_id"],
|
||||
)
|
||||
|
||||
mit = G.create_moduleimpl(
|
||||
module_id=modt["module_id"],
|
||||
formsemestre_id=sem2["formsemestre_id"],
|
||||
)
|
||||
|
||||
semt = G.create_formsemestre(
|
||||
formation_id=f2["formation_id"],
|
||||
semestre_id=3,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi3 = G.create_moduleimpl(
|
||||
module_id=mod3["module_id"],
|
||||
formsemestre_id=semt["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Afficher la liste des formations
|
||||
|
||||
lif = notes.formation_list(format="json", formation_id=f["formation_id"])
|
||||
# lif est une Response car on a appelé une vue (1er appel)
|
||||
assert isinstance(lif, flask.Response)
|
||||
load_lif = json.loads(lif.get_data().decode("utf-8"))
|
||||
assert len(load_lif) == 1
|
||||
assert load_lif[0]["acronyme"] == f["acronyme"]
|
||||
assert load_lif[0]["titre_officiel"] == f["titre_officiel"]
|
||||
assert load_lif[0]["formation_id"] == f["formation_id"]
|
||||
assert load_lif[0]["titre"] == f["titre"]
|
||||
|
||||
lif2 = notes.formation_list(format="json")
|
||||
# lif2 est un chaine
|
||||
assert isinstance(lif2, str)
|
||||
load_lif2 = json.loads(lif2)
|
||||
assert len(load_lif2) == 2
|
||||
assert load_lif2[0] == load_lif[0]
|
||||
assert load_lif2[1]["titre"] == f2["titre"]
|
||||
|
||||
# --- Export de formation_id
|
||||
|
||||
exp = sco_formations.formation_export(formation_id=f["formation_id"], format="json")
|
||||
assert isinstance(exp, str)
|
||||
load_exp = json.loads(exp)
|
||||
|
||||
assert load_exp["acronyme"] == "F1"
|
||||
assert load_exp["titre_officiel"] == "Titre officiel 1"
|
||||
assert load_exp["titre"] == "Formation 1"
|
||||
assert load_exp["formation_code"] == f["formation_code"]
|
||||
assert len(load_exp["ue"]) == 3
|
||||
assert load_exp["ue"][0]["acronyme"] == "TST1"
|
||||
assert load_exp["ue"][0]["titre"] == "ue test"
|
||||
assert load_exp["formation_id"] == f["formation_id"]
|
||||
assert load_exp["formation_code"] == f["formation_code"]
|
||||
|
||||
# --- Liste des semestres
|
||||
|
||||
li_sem1 = notes.formsemestre_list(
|
||||
formsemestre_id=sem1["formsemestre_id"], format="json"
|
||||
)
|
||||
assert isinstance(li_sem1, str)
|
||||
load_li_sem1 = json.loads(li_sem1) # uniquement le semestre 1 dans la liste
|
||||
|
||||
assert len(load_li_sem1) == 1
|
||||
assert load_li_sem1[0]["date_fin"] == sem1["date_fin"]
|
||||
assert load_li_sem1[0]["semestre_id"] == sem1["semestre_id"]
|
||||
assert load_li_sem1[0]["formation_id"] == sem1["formation_id"]
|
||||
|
||||
li_semf = notes.formsemestre_list(
|
||||
formation_id=f["formation_id"],
|
||||
format="json",
|
||||
)
|
||||
assert isinstance(li_semf, str)
|
||||
load_li_semf = json.loads(li_semf)
|
||||
|
||||
assert load_li_sem1[0] in load_li_semf
|
||||
assert len(load_li_semf) == 2
|
||||
assert load_li_semf[1]["semestre_id"] == sem2["semestre_id"]
|
||||
|
||||
li_sem = notes.formsemestre_list(format="json")
|
||||
load_li_sem = json.loads(li_sem)
|
||||
|
||||
assert len(load_li_sem) == 3
|
||||
assert load_li_semf[0] and load_li_semf[1] in load_li_sem
|
||||
assert load_li_sem[0]["semestre_id"] == semt["semestre_id"]
|
||||
|
||||
# --- Liste des modules
|
||||
|
||||
lim_sem1 = sco_moduleimpl.do_moduleimpl_list(
|
||||
formsemestre_id=sem1["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(lim_sem1) == 2
|
||||
assert mod["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
|
||||
assert mod2["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
|
||||
|
||||
lim_modid = sco_moduleimpl.do_moduleimpl_list(module_id=mod["module_id"])
|
||||
|
||||
assert len(lim_modid) == 1
|
||||
|
||||
lim_modimpl_id = sco_moduleimpl.do_moduleimpl_list(
|
||||
moduleimpl_id=mi["moduleimpl_id"]
|
||||
)
|
||||
# print(lim_modimpl_id)
|
||||
|
||||
# ---- Test de do_moduleimpl_withmodule_list
|
||||
|
||||
assert lim_modid == lim_modimpl_id # doit etre le meme resultat
|
||||
|
||||
liimp_sem1 = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem1["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(liimp_sem1) == 2
|
||||
assert mod["module_id"] in (liimp_sem1[0]["module_id"], liimp_sem1[1]["module_id"])
|
||||
assert mod2["module_id"] in (
|
||||
liimp_sem1[0]["module_id"],
|
||||
liimp_sem1[1]["module_id"],
|
||||
)
|
||||
liimp_sem2 = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
assert modt["module_id"] == liimp_sem2[0]["module_id"]
|
||||
liimp_modid = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
module_id=mod["module_id"]
|
||||
)
|
||||
assert len(liimp_modid) == 1
|
||||
|
||||
liimp_modimplid = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
moduleimpl_id=mi["moduleimpl_id"]
|
||||
)
|
||||
|
||||
assert liimp_modid == liimp_modimplid
|
||||
|
||||
# --- Suppression du module, matiere et ue test du semestre 2
|
||||
|
||||
# on doit d'abbord supprimer le semestre
|
||||
|
||||
# sco_formsemestre_edit.formsemestre_delete( formsemestre_id=sem2["formsemestre_id"], REQUEST=REQUEST)
|
||||
# sco_formsemestre_edit.formsemestre_createwithmodules( formsemestre_id=sem2["formsemestre_id"], REQUEST=REQUEST)
|
||||
|
||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
||||
|
||||
sco_formsemestre_edit.do_formsemestre_delete(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
|
||||
# sco_edit_module.module_delete( module_id=modt["module_id"], REQUEST=REQUEST)
|
||||
# sco_edit_matiere.matiere_delete( matiere_id=matt["matiere_id"], REQUEST=REQUEST)
|
||||
# sco_edit_ue.ue_delete( ue_id=uet["ue_id"], REQUEST=REQUEST)
|
||||
|
||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
||||
|
||||
li_module = sco_edit_module.do_module_list()
|
||||
assert len(li_module) == 4
|
||||
sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre
|
||||
# sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]])
|
||||
# deuxieme methode de supression d'un module
|
||||
li_module2 = sco_edit_module.do_module_list()
|
||||
|
||||
assert len(li_module2) == 3 # verification de la suppression du module
|
||||
|
||||
lim_sem2 = sco_moduleimpl.do_moduleimpl_list(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup
|
||||
|
||||
li_mat = sco_edit_matiere.do_matiere_list()
|
||||
assert len(li_mat) == 4
|
||||
sco_edit_matiere.do_matiere_delete(oid=matt["matiere_id"]) # on supprime la matiere
|
||||
li_mat2 = sco_edit_matiere.do_matiere_list()
|
||||
assert len(li_mat2) == 3 # verification de la suppression de la matiere
|
||||
|
||||
li_ue = sco_edit_ue.do_ue_list()
|
||||
assert len(li_ue) == 4
|
||||
sco_edit_ue.ue_delete(ue_id=uet["ue_id"], dialog_confirmed=True)
|
||||
li_ue2 = sco_edit_ue.do_ue_list()
|
||||
assert len(li_ue2) == 3 # verification de la suppression de l'UE
|
||||
|
||||
# --- Suppression d'une formation
|
||||
# Il faut d'abbord supprimer le semestre aussi.
|
||||
sco_formsemestre_edit.do_formsemestre_delete(
|
||||
formsemestre_id=semt["formsemestre_id"]
|
||||
)
|
||||
|
||||
sco_edit_formation.do_formation_delete(oid=f2["formation_id"])
|
||||
lif3 = notes.formation_list(format="json")
|
||||
assert isinstance(lif3, str)
|
||||
load_lif3 = json.loads(lif3)
|
||||
assert len(load_lif3) == 1
|
||||
|
||||
|
||||
def test_import_formation(test_client):
|
||||
"""Test import/export formations"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
# Lecture fichier XML local:
|
||||
with open("tests/unit/formation-exemple-1.xml") as f:
|
||||
doc = f.read()
|
||||
|
||||
# --- Création de la formation
|
||||
f = sco_formations.formation_import_xml(doc)
|
||||
assert len(f) == 3 # 3-uple
|
||||
formation_id = f[0]
|
||||
# --- Mise en place de 4 semestres
|
||||
sems = [
|
||||
G.create_formsemestre(
|
||||
formation_id=formation_id,
|
||||
semestre_id=x[0],
|
||||
date_debut=x[1],
|
||||
date_fin=x[2],
|
||||
)
|
||||
for x in (
|
||||
(1, "05/09/2019", "05/01/2020"),
|
||||
(2, "06/01/2020", "30/06/2020"),
|
||||
(3, "01/09/2020", "05/01/2021"),
|
||||
(4, "06/01/2021", "30/06/2021"),
|
||||
)
|
||||
]
|
||||
# et les modules
|
||||
modules = sco_edit_module.do_module_list({"formation_id": formation_id})
|
||||
for mod in modules:
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"],
|
||||
)
|
||||
assert mi["ens"] == []
|
||||
assert mi["module_id"] == mod["module_id"]
|
||||
|
||||
# --- Export formation en XML
|
||||
doc1 = sco_formations.formation_export(formation_id, format="xml")
|
||||
assert isinstance(doc1, str)
|
||||
# -*- mode: python -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Test creation/edition/import/export formations
|
||||
"""
|
||||
|
||||
# test écrit par Fares Amer, mai 2021 et porté sur ScoDoc 8 en juillet 2021
|
||||
|
||||
# Créer 2 formations, une test et une normale. Créer 2 semestres dans la formation normale et un
|
||||
# dans la formation test, créer 2 semestres dans la formation normale (un test et un normal),
|
||||
# 2 ue (un test et un normal), 2 modules (un test et un normal) et 2 matieres (test et normal).
|
||||
# Et dans la formations test, un semestre, un module, un ue et une matiere.
|
||||
# Afficher la liste de tout ca puis supprimer les ue, mod, mat et sem test ainsi
|
||||
# que la formation test. Afficher la liste des UE, formations et modules restante.
|
||||
#
|
||||
# Vérification :
|
||||
#
|
||||
# - Les listes initiales comprennent bien tout les éléments créés avec les bon noms etc
|
||||
# - La supression s'est bien effectué au niveau de scodoc web et en python
|
||||
# - Vérifier que les fonctions listes font bien la mise à jour après supression
|
||||
#
|
||||
# Fonction de l'API utilisé :
|
||||
#
|
||||
# - create_formation
|
||||
# - create_ue
|
||||
# - create_matiere
|
||||
# - create_module
|
||||
# - create_formsemestre
|
||||
# - create_moduleimpl
|
||||
# - formation_list
|
||||
# - formation_export
|
||||
# - formsemestre_list
|
||||
# - do_moduleimpl_list
|
||||
# - do_module_impl_with_module_list
|
||||
# - do_formsemestre_delete
|
||||
# - do_module_list
|
||||
# - do_module_delete
|
||||
# - do_matiere_list
|
||||
# - do_matiere_delete
|
||||
# - do_ue_list
|
||||
# - do_ue_delete
|
||||
# - do_formation_delete
|
||||
|
||||
import json
|
||||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
|
||||
from tests.unit import sco_fake_gen
|
||||
|
||||
from app.scodoc import sco_edit_formation
|
||||
from app.scodoc import sco_edit_matiere
|
||||
from app.scodoc import sco_edit_module
|
||||
from app.scodoc import sco_edit_ue
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_formsemestre_edit
|
||||
from app.scodoc import sco_moduleimpl
|
||||
from app.views import notes
|
||||
|
||||
|
||||
def test_formations(test_client):
|
||||
"""Test création/édition/import/export formations"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
|
||||
# --- Création de formations
|
||||
|
||||
f = G.create_formation(
|
||||
acronyme="F1", titre="Formation 1", titre_officiel="Titre officiel 1"
|
||||
)
|
||||
|
||||
ue = G.create_ue(formation_id=f["formation_id"], acronyme="TST1", titre="ue test")
|
||||
mat = G.create_matiere(ue_id=ue["ue_id"], titre="matière test")
|
||||
mod = G.create_module(
|
||||
matiere_id=mat["matiere_id"],
|
||||
code="TSM1",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
ue2 = G.create_ue(formation_id=f["formation_id"], acronyme="TST2", titre="ue test2")
|
||||
mat2 = G.create_matiere(ue_id=ue2["ue_id"], titre="matière test2")
|
||||
mod2 = G.create_module(
|
||||
matiere_id=mat2["matiere_id"],
|
||||
code="TSM2",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=ue2["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
uet = G.create_ue(formation_id=f["formation_id"], acronyme="TSTt", titre="ue testt")
|
||||
matt = G.create_matiere(ue_id=uet["ue_id"], titre="matière testt")
|
||||
modt = G.create_module(
|
||||
matiere_id=matt["matiere_id"],
|
||||
code="TSMt",
|
||||
coefficient=1.0,
|
||||
titre="module test",
|
||||
ue_id=uet["ue_id"], # faiblesse de l'API
|
||||
formation_id=f["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
f2 = G.create_formation(acronyme="", titre="Formation test")
|
||||
|
||||
ue3 = G.create_ue(
|
||||
formation_id=f2["formation_id"], acronyme="TST3", titre="ue test3"
|
||||
)
|
||||
mat3 = G.create_matiere(ue_id=ue3["ue_id"], titre="matière test3")
|
||||
mod3 = G.create_module(
|
||||
matiere_id=mat3["matiere_id"],
|
||||
code="TSM3",
|
||||
coefficient=1.0,
|
||||
titre="module test3",
|
||||
ue_id=ue3["ue_id"], # faiblesse de l'API
|
||||
formation_id=f2["formation_id"], # faiblesse de l'API
|
||||
)
|
||||
|
||||
# --- Création et implémentation des semestres
|
||||
|
||||
sem1 = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=1,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
sem2 = G.create_formsemestre(
|
||||
formation_id=f["formation_id"],
|
||||
semestre_id=2,
|
||||
date_debut="01/09/2020",
|
||||
date_fin="31/12/2020",
|
||||
)
|
||||
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sem1["formsemestre_id"],
|
||||
)
|
||||
|
||||
mi2 = G.create_moduleimpl(
|
||||
module_id=mod2["module_id"],
|
||||
formsemestre_id=sem1["formsemestre_id"],
|
||||
)
|
||||
|
||||
mit = G.create_moduleimpl(
|
||||
module_id=modt["module_id"],
|
||||
formsemestre_id=sem2["formsemestre_id"],
|
||||
)
|
||||
|
||||
semt = G.create_formsemestre(
|
||||
formation_id=f2["formation_id"],
|
||||
semestre_id=3,
|
||||
date_debut="01/01/2021",
|
||||
date_fin="30/06/2021",
|
||||
)
|
||||
|
||||
mi3 = G.create_moduleimpl(
|
||||
module_id=mod3["module_id"],
|
||||
formsemestre_id=semt["formsemestre_id"],
|
||||
)
|
||||
|
||||
# --- Afficher la liste des formations
|
||||
|
||||
lif = notes.formation_list(format="json", formation_id=f["formation_id"])
|
||||
# lif est une Response car on a appelé une vue (1er appel)
|
||||
assert isinstance(lif, flask.Response)
|
||||
load_lif = json.loads(lif.get_data().decode("utf-8"))
|
||||
assert len(load_lif) == 1
|
||||
assert load_lif[0]["acronyme"] == f["acronyme"]
|
||||
assert load_lif[0]["titre_officiel"] == f["titre_officiel"]
|
||||
assert load_lif[0]["formation_id"] == f["formation_id"]
|
||||
assert load_lif[0]["titre"] == f["titre"]
|
||||
|
||||
lif2 = notes.formation_list(format="json").get_data(as_text=True)
|
||||
# lif2 est un chaine
|
||||
assert isinstance(lif2, str)
|
||||
load_lif2 = json.loads(lif2)
|
||||
assert len(load_lif2) == 2
|
||||
assert load_lif2[0] == load_lif[0]
|
||||
assert load_lif2[1]["titre"] == f2["titre"]
|
||||
|
||||
# --- Export de formation_id
|
||||
|
||||
exp = sco_formations.formation_export(
|
||||
formation_id=f["formation_id"], format="json"
|
||||
).get_data(as_text=True)
|
||||
assert isinstance(exp, str)
|
||||
load_exp = json.loads(exp)
|
||||
|
||||
assert load_exp["acronyme"] == "F1"
|
||||
assert load_exp["titre_officiel"] == "Titre officiel 1"
|
||||
assert load_exp["titre"] == "Formation 1"
|
||||
assert load_exp["formation_code"] == f["formation_code"]
|
||||
assert len(load_exp["ue"]) == 3
|
||||
assert load_exp["ue"][0]["acronyme"] == "TST1"
|
||||
assert load_exp["ue"][0]["titre"] == "ue test"
|
||||
assert load_exp["formation_id"] == f["formation_id"]
|
||||
assert load_exp["formation_code"] == f["formation_code"]
|
||||
|
||||
# --- Liste des semestres
|
||||
|
||||
li_sem1 = notes.formsemestre_list(
|
||||
formsemestre_id=sem1["formsemestre_id"], format="json"
|
||||
).get_data(as_text=True)
|
||||
assert isinstance(li_sem1, str)
|
||||
load_li_sem1 = json.loads(li_sem1) # uniquement le semestre 1 dans la liste
|
||||
|
||||
assert len(load_li_sem1) == 1
|
||||
assert load_li_sem1[0]["date_fin"] == sem1["date_fin"]
|
||||
assert load_li_sem1[0]["semestre_id"] == sem1["semestre_id"]
|
||||
assert load_li_sem1[0]["formation_id"] == sem1["formation_id"]
|
||||
|
||||
li_semf = notes.formsemestre_list(
|
||||
formation_id=f["formation_id"],
|
||||
format="json",
|
||||
).get_data(as_text=True)
|
||||
assert isinstance(li_semf, str)
|
||||
load_li_semf = json.loads(li_semf)
|
||||
|
||||
assert load_li_sem1[0] in load_li_semf
|
||||
assert len(load_li_semf) == 2
|
||||
assert load_li_semf[1]["semestre_id"] == sem2["semestre_id"]
|
||||
|
||||
li_sem = notes.formsemestre_list(format="json").get_data(as_text=True)
|
||||
load_li_sem = json.loads(li_sem)
|
||||
|
||||
assert len(load_li_sem) == 3
|
||||
assert load_li_semf[0] and load_li_semf[1] in load_li_sem
|
||||
assert load_li_sem[0]["semestre_id"] == semt["semestre_id"]
|
||||
|
||||
# --- Liste des modules
|
||||
|
||||
lim_sem1 = sco_moduleimpl.do_moduleimpl_list(
|
||||
formsemestre_id=sem1["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(lim_sem1) == 2
|
||||
assert mod["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
|
||||
assert mod2["module_id"] in (lim_sem1[0]["module_id"], lim_sem1[1]["module_id"])
|
||||
|
||||
lim_modid = sco_moduleimpl.do_moduleimpl_list(module_id=mod["module_id"])
|
||||
|
||||
assert len(lim_modid) == 1
|
||||
|
||||
lim_modimpl_id = sco_moduleimpl.do_moduleimpl_list(
|
||||
moduleimpl_id=mi["moduleimpl_id"]
|
||||
)
|
||||
# print(lim_modimpl_id)
|
||||
|
||||
# ---- Test de do_moduleimpl_withmodule_list
|
||||
|
||||
assert lim_modid == lim_modimpl_id # doit etre le meme resultat
|
||||
|
||||
liimp_sem1 = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem1["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(liimp_sem1) == 2
|
||||
assert mod["module_id"] in (liimp_sem1[0]["module_id"], liimp_sem1[1]["module_id"])
|
||||
assert mod2["module_id"] in (
|
||||
liimp_sem1[0]["module_id"],
|
||||
liimp_sem1[1]["module_id"],
|
||||
)
|
||||
liimp_sem2 = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
assert modt["module_id"] == liimp_sem2[0]["module_id"]
|
||||
liimp_modid = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
module_id=mod["module_id"]
|
||||
)
|
||||
assert len(liimp_modid) == 1
|
||||
|
||||
liimp_modimplid = sco_moduleimpl.do_moduleimpl_withmodule_list(
|
||||
moduleimpl_id=mi["moduleimpl_id"]
|
||||
)
|
||||
|
||||
assert liimp_modid == liimp_modimplid
|
||||
|
||||
# --- Suppression du module, matiere et ue test du semestre 2
|
||||
|
||||
# on doit d'abbord supprimer le semestre
|
||||
|
||||
# sco_formsemestre_edit.formsemestre_delete( formsemestre_id=sem2["formsemestre_id"], REQUEST=REQUEST)
|
||||
# sco_formsemestre_edit.formsemestre_createwithmodules( formsemestre_id=sem2["formsemestre_id"], REQUEST=REQUEST)
|
||||
|
||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
||||
|
||||
sco_formsemestre_edit.do_formsemestre_delete(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
|
||||
# sco_edit_module.module_delete( module_id=modt["module_id"], REQUEST=REQUEST)
|
||||
# sco_edit_matiere.matiere_delete( matiere_id=matt["matiere_id"], REQUEST=REQUEST)
|
||||
# sco_edit_ue.ue_delete( ue_id=uet["ue_id"], REQUEST=REQUEST)
|
||||
|
||||
# RIEN NE SE PASSE AVEC CES FONCTIONS
|
||||
|
||||
li_module = sco_edit_module.do_module_list()
|
||||
assert len(li_module) == 4
|
||||
sco_edit_module.do_module_delete(oid=modt["module_id"]) # on supprime le semestre
|
||||
# sco_formsemestre_edit.formsemestre_delete_moduleimpls( formsemestre_id=sem2["formsemestre_id"], module_ids_to_del=[modt["module_id"]])
|
||||
# deuxieme methode de supression d'un module
|
||||
li_module2 = sco_edit_module.do_module_list()
|
||||
|
||||
assert len(li_module2) == 3 # verification de la suppression du module
|
||||
|
||||
lim_sem2 = sco_moduleimpl.do_moduleimpl_list(
|
||||
formsemestre_id=sem2["formsemestre_id"]
|
||||
)
|
||||
|
||||
assert len(lim_sem2) == 0 # deuxieme vérification si le module s'est bien sup
|
||||
|
||||
li_mat = sco_edit_matiere.do_matiere_list()
|
||||
assert len(li_mat) == 4
|
||||
sco_edit_matiere.do_matiere_delete(oid=matt["matiere_id"]) # on supprime la matiere
|
||||
li_mat2 = sco_edit_matiere.do_matiere_list()
|
||||
assert len(li_mat2) == 3 # verification de la suppression de la matiere
|
||||
|
||||
li_ue = sco_edit_ue.do_ue_list()
|
||||
assert len(li_ue) == 4
|
||||
sco_edit_ue.ue_delete(ue_id=uet["ue_id"], dialog_confirmed=True)
|
||||
li_ue2 = sco_edit_ue.do_ue_list()
|
||||
assert len(li_ue2) == 3 # verification de la suppression de l'UE
|
||||
|
||||
# --- Suppression d'une formation
|
||||
# Il faut d'abbord supprimer le semestre aussi.
|
||||
sco_formsemestre_edit.do_formsemestre_delete(
|
||||
formsemestre_id=semt["formsemestre_id"]
|
||||
)
|
||||
|
||||
sco_edit_formation.do_formation_delete(oid=f2["formation_id"])
|
||||
lif3 = notes.formation_list(format="json").get_data(as_text=True)
|
||||
assert isinstance(lif3, str)
|
||||
load_lif3 = json.loads(lif3)
|
||||
assert len(load_lif3) == 1
|
||||
|
||||
|
||||
def test_import_formation(test_client):
|
||||
"""Test import/export formations"""
|
||||
G = sco_fake_gen.ScoFake(verbose=False)
|
||||
# Lecture fichier XML local:
|
||||
with open("tests/unit/formation-exemple-1.xml") as f:
|
||||
doc = f.read()
|
||||
|
||||
# --- Création de la formation
|
||||
f = sco_formations.formation_import_xml(doc)
|
||||
assert len(f) == 3 # 3-uple
|
||||
formation_id = f[0]
|
||||
# --- Mise en place de 4 semestres
|
||||
sems = [
|
||||
G.create_formsemestre(
|
||||
formation_id=formation_id,
|
||||
semestre_id=x[0],
|
||||
date_debut=x[1],
|
||||
date_fin=x[2],
|
||||
)
|
||||
for x in (
|
||||
(1, "05/09/2019", "05/01/2020"),
|
||||
(2, "06/01/2020", "30/06/2020"),
|
||||
(3, "01/09/2020", "05/01/2021"),
|
||||
(4, "06/01/2021", "30/06/2021"),
|
||||
)
|
||||
]
|
||||
# et les modules
|
||||
modules = sco_edit_module.do_module_list({"formation_id": formation_id})
|
||||
for mod in modules:
|
||||
mi = G.create_moduleimpl(
|
||||
module_id=mod["module_id"],
|
||||
formsemestre_id=sems[mod["semestre_id"] - 1]["formsemestre_id"],
|
||||
)
|
||||
assert mi["ens"] == []
|
||||
assert mi["module_id"] == mod["module_id"]
|
||||
|
||||
# --- Export formation en XML
|
||||
doc1 = sco_formations.formation_export(formation_id, format="xml").get_data(
|
||||
as_text=True
|
||||
)
|
||||
assert isinstance(doc1, str)
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
|
||||
from tools.import_scodoc7_user_db import import_scodoc7_user_db
|
||||
from tools.import_scodoc7_dept import import_scodoc7_dept
|
||||
from tools.migrate_scodoc7_archives import migrate_scodoc7_dept_archive
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/opt/zope213/bin/python
|
||||
#!/opt/scodoc/venv/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: python -*-
|
||||
|
||||
|
@ -68,42 +68,51 @@ anonymize_null = "NULL"
|
|||
ANONYMIZED_FIELDS = {
|
||||
"identite.nom": anonymize_name,
|
||||
"identite.prenom": anonymize_name,
|
||||
"identite.nom_usuel": anonymize_null,
|
||||
"identite.civilite": "'X'",
|
||||
"identite.date_naissance": anonymize_date,
|
||||
"identite.lieu_naissance": anonymize_question_str,
|
||||
"identite.dept_naissance": anonymize_question_str,
|
||||
"identite.nationalite": anonymize_question_str,
|
||||
"identite.foto": anonymize_null,
|
||||
"identite.statut": anonymize_null,
|
||||
"identite.boursier": anonymize_null,
|
||||
"identite.photo_filename": anonymize_null,
|
||||
"identite.code_nip": anonymize_null,
|
||||
"identite.code_ine": anonymize_null,
|
||||
"identite.nom_usuel": anonymize_null,
|
||||
"identite.scodoc7_id": anonymize_null,
|
||||
"adresse.email": "'ano@nyme.fr'",
|
||||
"adresse.emailperso": anonymize_null,
|
||||
"adresse.domicile": anonymize_null,
|
||||
"adresse.codepostaldomicile": anonymize_null,
|
||||
"adresse.villedomicile": anonymize_null,
|
||||
"adresse.paysdomicile": anonymize_null,
|
||||
"adresse.telephone": anonymize_null,
|
||||
"adresse.telephonemobile": anonymize_null,
|
||||
"adresse.fax": anonymize_null,
|
||||
"admissions.nomlycee": anonymize_name,
|
||||
"billet_absence.description": anonymize_null,
|
||||
"etud_annotations.comment": anonymize_name,
|
||||
"entreprises.nom": anonymize_name,
|
||||
"entreprises.adresse": anonymize_null,
|
||||
"entreprises.ville": anonymize_null,
|
||||
"entreprises.codepostal": anonymize_null,
|
||||
"entreprises.pays": anonymize_null,
|
||||
"entreprises.contact_origine": anonymize_null,
|
||||
"entreprises.secteur": anonymize_null,
|
||||
"entreprises.note": anonymize_null,
|
||||
"entreprises.privee": anonymize_null,
|
||||
"entreprises.localisation": anonymize_null,
|
||||
"entreprise_correspondant.nom": anonymize_name,
|
||||
"entreprise_correspondant.prenom": anonymize_name,
|
||||
"entreprise_correspondant.phone1": anonymize_null,
|
||||
"entreprise_correspondant.phone2": anonymize_null,
|
||||
"entreprise_correspondant.mobile": anonymize_null,
|
||||
"entreprise_correspondant.mail1": anonymize_null,
|
||||
"entreprise_correspondant.mail2": anonymize_null,
|
||||
"entreprise_correspondant.note": anonymize_null,
|
||||
"entreprise_correspondant.fax": anonymize_null,
|
||||
"entreprise_contact.description": anonymize_null,
|
||||
"entreprise_contact.enseignant": anonymize_null,
|
||||
# "entreprises.nom": anonymize_name,
|
||||
# "entreprises.adresse": anonymize_null,
|
||||
# "entreprises.ville": anonymize_null,
|
||||
# "entreprises.codepostal": anonymize_null,
|
||||
# "entreprises.pays": anonymize_null,
|
||||
# "entreprises.contact_origine": anonymize_null,
|
||||
# "entreprises.secteur": anonymize_null,
|
||||
# "entreprises.note": anonymize_null,
|
||||
# "entreprises.privee": anonymize_null,
|
||||
# "entreprises.localisation": anonymize_null,
|
||||
# "entreprise_correspondant.nom": anonymize_name,
|
||||
# "entreprise_correspondant.prenom": anonymize_name,
|
||||
# "entreprise_correspondant.phone1": anonymize_null,
|
||||
# "entreprise_correspondant.phone2": anonymize_null,
|
||||
# "entreprise_correspondant.mobile": anonymize_null,
|
||||
# "entreprise_correspondant.mail1": anonymize_null,
|
||||
# "entreprise_correspondant.mail2": anonymize_null,
|
||||
# "entreprise_correspondant.note": anonymize_null,
|
||||
# "entreprise_correspondant.fax": anonymize_null,
|
||||
# "entreprise_contact.description": anonymize_null,
|
||||
# "entreprise_contact.enseignant": anonymize_null,
|
||||
"notes_appreciations.comment": anonymize_name,
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue