Compare commits

...

72 Commits

Author SHA1 Message Date
Emmanuel Viennet 2e88e6f7ce Merge pull request 'Export/Import d'étudiant via fichiers xlsx.' (#98) from jmplace/ScoDoc-Lille:PR_export_import_etudiants into ScoDoc8
Reviewed-on: viennet/ScoDoc#98
2021-08-15 15:05:09 +02:00
Jean-Marie Place 432831140c Export/Import d'étudiant via fichiers xlsx.
Point délicats:
* Le message d'erreur pour une case vide était une exception python.
diagnostic: la création de l'étudiant dans la BDD se faisait avant le controle de la civilité et plantait quand None
correctif: ajout d'une methode _check_civilite (a cote des méthodes de contrôle d unicité de nip et d ine (sco_etud.py)
* Le format de date a changé entre pyExcelerator et openpyxl (réécriture de sco_excel.xldate_as_datetime)
le format xlxs d import précise qu'une date peut être spécifié soit en ISO soit sous forme d'un nombre.
c est testé avec des écriture de fichier xlsx depuis Excel 2019 et LibreOffice 7 (mais sans maitrise sur la forme de date utilisée)
par contre plantage si tentative de lire un fichier ods (fonction excel_bytes_to_list a fixer)
* Le renvoi vers la page de formation_id se faisait mal
correction: calcul de l'url (sco_import_etuds.py:245) et (scolar.py:1710 celle-ci peut être pas necessaire)
2021-08-14 10:12:40 +02:00
Emmanuel Viennet 7372a953fa file suffix 2021-08-13 12:28:38 +02:00
Emmanuel Viennet 72d5904c4c Merge pull request 'adaptation export feuille de préparation jury' (#97) from jmplace/ScoDoc-Lille:prepajury_pr into ScoDoc8
Reviewed-on: viennet/ScoDoc#97
2021-08-13 12:27:02 +02:00
Jean-Marie Place db717826c8 adaptation export feuille de préparation jury 2021-08-13 12:18:22 +02:00
Emmanuel Viennet 40db439904 oups 2021-08-12 14:55:25 +02:00
Emmanuel Viennet fbdcf63c70 Fix: API sco_excel + suffix xslx 2021-08-12 14:49:53 +02:00
Emmanuel Viennet 6287376efb Merge pull request 'fix xlsx extension for exported file' (#95) from jmplace/ScoDoc-Lille:export_import_notes into ScoDoc8
Reviewed-on: viennet/ScoDoc#95
2021-08-12 13:15:44 +02:00
Jean-Marie Place e8e1d2f2bb fix xlsx extension for exported file 2021-08-12 11:11:18 +02:00
Emmanuel Viennet 09d131a85d Merge pull request 'export/import notes ; gentables' (#94) from jmplace/ScoDoc-Lille:repair into ScoDoc8
Reviewed-on: viennet/ScoDoc#94
2021-08-11 11:54:38 +02:00
Jean-Marie Place 48ad4f3877 export/import notes ; gentables 2021-08-11 11:40:28 +02:00
Emmanuel Viennet b2b325dda9 Merge pull request 'selenium' (#92) from jmplace/ScoDoc-Lille:selenium into ScoDoc8
Reviewed-on: viennet/ScoDoc#92
2021-08-10 14:04:06 +02:00
Jean-Marie Place 0d60da2675 reduction PR aux balises 2021-08-10 09:30:08 +02:00
Jean-Marie Place b6036c940b Preparation jeu de test selenium 02 2021-08-09 22:47:41 +02:00
Jean-Marie Place 3d9f362daf exclude .idea files from git
refactoring setinngs variables

in progress: import xml files

ajout creation user "bach" pour certains tests

pseudo-test pour initalisation Selenium

typo

Ajout/suppression de département en tant qu'utilisateur scodoc (non root)

complement scodoc.py pour import xml files

--amend

scodoc is able to perform database operation

move export..xml to tests/scenario dir
2021-08-09 22:23:00 +02:00
Emmanuel Viennet 3d99b8c087 Ajout/suppression de département en tant qu'utilisateur scodoc (non root) 2021-08-09 08:25:33 +02:00
Emmanuel Viennet 76a6270740 typo 2021-08-07 16:34:03 +02:00
Emmanuel Viennet bf0b2c66bd pseudo-test pour initalisation Selenium 2021-08-07 16:33:47 +02:00
Emmanuel Viennet 06f66e6976 ajout creation user "bach" pour certains tests 2021-08-07 16:32:24 +02:00
IDK 383fdb0e53 modified to run as tools/script.sh 2021-08-02 11:34:28 +03:00
IDK 35d5d43eeb removed old rss functions 2021-08-02 10:56:50 +03:00
Emmanuel Viennet 27b8fee9b7 trombino en POST pour dialogue confirmation 2021-08-02 10:16:57 +03:00
Emmanuel Viennet b7e5c12aa1 petites modifs pour intégration 2021-08-02 10:16:14 +03:00
Emmanuel Viennet 0cb9d18344 Contrib JM Place i/o Excel 2021-08-02 09:52:07 +03:00
Emmanuel Viennet 2c4ff46a3a debut restructuration scripts upgrade 2021-08-02 09:41:11 +03:00
Jean-Marie Place 845aeaba5f test openpyxl sur liste appel 2021-08-02 08:26:35 +02:00
Emmanuel Viennet 6cd868b7a3 petit nettoyage 2021-08-01 17:33:09 +03:00
Emmanuel Viennet 80dd25ba02 unit test pass 2021-08-01 17:13:47 +03:00
Emmanuel Viennet 5db32a80ee modified to use pytest 2021-08-01 16:33:08 +03:00
Emmanuel Viennet 704bb2c170 prettier xml exports 2021-08-01 16:17:33 +03:00
Emmanuel Viennet f5dbbaa207 refactoring & tests, cont. 2021-08-01 11:16:16 +03:00
Emmanuel Viennet 56f935b2a8 improved our shell: reload context and admin user 2021-08-01 10:52:28 +03:00
Emmanuel Viennet 37e7667eeb doc et tests unitaires des absences 2021-08-01 00:05:53 +03:00
Emmanuel Viennet 6a42846d2e Vide base departement avant tests 2021-07-31 23:57:54 +03:00
Emmanuel Viennet 540623d3b9 refactoring and tests 2021-07-31 19:01:10 +03:00
Emmanuel Viennet c153c11606 section sur variables d'environnement 2021-07-30 19:52:49 +02:00
Emmanuel Viennet 2306b48b53 fix 2021-07-30 18:47:34 +03:00
Emmanuel Viennet 7012a3ff58 precisions pour les développeurs 2021-07-30 18:46:18 +03:00
Emmanuel Viennet fc59018688 Fix script mode and add safety check 2021-07-30 18:34:47 +03:00
Emmanuel Viennet c0f5292db8 fixed doc markdown 2021-07-30 17:52:48 +03:00
Emmanuel Viennet b0a77fba66 Fix creation/deletion scripts + more unit tests 2021-07-30 10:36:30 +03:00
Emmanuel Viennet 31288efb73 bug fix 2021-07-29 17:58:18 +03:00
Emmanuel Viennet 4b3ac58bc0 bug fix 2021-07-29 17:48:27 +03:00
Emmanuel Viennet 8fedde52e7 refactoring: removing useless args for Flask 2021-07-29 17:31:15 +03:00
Emmanuel Viennet 243a9b6fd9 added cli: flask clear-cache 2021-07-29 11:30:13 +03:00
Emmanuel Viennet 8c02c6ef7e removed useless context arg from evaluations 2021-07-29 11:19:00 +03:00
Emmanuel Viennet eff9ae59bc removed useless context arg from preferences 2021-07-28 18:03:54 +03:00
Emmanuel Viennet 748caf8ada recherche étudiant inter-département 2021-07-28 10:51:18 +03:00
Emmanuel Viennet 9646452457 redirige vers formulaire authentification 2021-07-28 09:42:22 +03:00
Emmanuel Viennet f435885315 Page d'erreur pour ScoValueError 2021-07-28 09:12:57 +03:00
Emmanuel Viennet 18b802130a invalidate local (request) nt cache 2021-07-27 20:36:10 +03:00
Emmanuel Viennet f00a18aeb7 Fix formulaire gestion utilisateur 2021-07-27 17:55:50 +03:00
Emmanuel Viennet b8efe15c0a removed debug logs from scodoc7 decorators 2021-07-27 17:16:58 +03:00
Emmanuel Viennet 40d98eae16 fix migration utilisateurs ScoDoc 7 2021-07-27 17:07:03 +03:00
Emmanuel Viennet 2449d65957 Remplace memcached par Redis 2021-07-27 15:56:24 +03:00
Emmanuel Viennet 3b5b449a8a Remplace memcached par Redis 2021-07-27 15:33:11 +03:00
Emmanuel Viennet da4cea0316 Fix SVG encoding 2021-07-27 14:50:53 +03:00
Emmanuel Viennet 7e3ac9884c create_database.sh run as root 2021-07-27 14:38:26 +03:00
Emmanuel Viennet b17d664652 Fix install scripts 2021-07-27 14:27:21 +03:00
Emmanuel Viennet 79e43adbc3 exception handling in sco_cache set 2021-07-26 18:11:45 +03:00
Emmanuel Viennet 0252bf4df4 Fix: bytes I/O (zips and pdfs) 2021-07-26 18:11:00 +03:00
Emmanuel Viennet c3e3f45370 amélioration code formsemestre_status 2021-07-26 17:52:38 +03:00
Emmanuel Viennet 97446272af Fix: codage chaine interrogation portail 2021-07-26 16:32:55 +03:00
Emmanuel Viennet 2b967ba34e Fix: change photo 2021-07-26 16:23:07 +03:00
Emmanuel Viennet 4aa073beb3 Handle unexpected errors from pylibmc 2021-07-26 16:18:16 +03:00
Emmanuel Viennet 84c72fcb50 Fix install script (check_create_scodoc_user) 2021-07-26 15:57:42 +03:00
Emmanuel Viennet d45f9e25cc Fix JSON exports 2021-07-26 10:50:22 +03:00
Emmanuel Viennet a07571494c Fix PDF (combining accents) and StringIO imports 2021-07-25 23:31:59 +03:00
Emmanuel Viennet 294bc8f205 unit test for pydot 2021-07-25 23:30:45 +03:00
Emmanuel Viennet 424852c312 Fixed links to external sites 2021-07-25 17:53:10 +03:00
Emmanuel Viennet d93b5688ae Fixed graph generation with pydot and added unit test 2021-07-25 17:42:47 +03:00
Emmanuel Viennet 30f88dfd4f nom fichier release 2021-07-25 12:35:16 +03:00
170 changed files with 6036 additions and 6885 deletions

116
README.md
View File

@ -21,19 +21,18 @@ ne soit visible des utilisateurs Web.
**Work in Progress (WIP)**: développement en cours, certaines pages fonctionnent, d'autres pas:
merci de signaler les erreurs.
### État actuel (23 juillet 21)
### État actuel (30 juillet 21)
- serveur de développement fonctionnel (pas testé en mode "production" sous gunicorn+nginx).
- logs à revoir
- logs à revoir (tropverbeux), dans `/opt/scodoc-data/log`
- pas d'import/export excel.
- pas de pages gestion utilisateurs.
**En cours:**
- tests, formulaires utilisateurs, logging.
- nettoyage du code, finalisation tests et intégration.
## Installation (sur Debian 10 / python3.7)
@ -47,20 +46,20 @@ On peut installer à partir de zéro, ou sur une machine ayant déjà un ScoDoc
1. S'assurer que l'installation ScoDoc 7 est à jour
sudo su
cd /opt/scodoc/Products/ScoDoc/config
./upgrade.sh
sudo su
cd /opt/scodoc/Products/ScoDoc/config
./upgrade.sh
2. Arrêter le service ScoDoc 7
systemctl stop scodoc7
systemctl stop scodoc
S'assurer qu'il est bien stoppé (`ps auxw`, ...), sans qui la migration va échouer.
3. Renommer le répertoire de ScoDoc 7:
sudo su
mv /opt/scodoc /opt/scodoc7
sudo su
mv /opt/scodoc /opt/scodoc7
Les données pourront être migrées après installation la nouvelle evrsion, voir plus loin.
@ -69,15 +68,15 @@ Sur un système Linux Debian 10, en tant que `root`:
- Charger la dernière release depuis https://scodoc.org/git/viennet/ScoDoc/releases
- Déplacer ou copier le fichier `scodoc-x.y.z.tgz` dans `/opt` et le décomprimer:
- Déplacer ou copier le fichier `ScoDoc-x.y.z.tgz` dans `/opt` et le décomprimer:
sudo su
cd /opt; tar xf - scodoc-x.y.z.tgz # remplacer x.y.z par votre version
sudo su
cd /opt; tar xf - ScoDoc-x.y.z.tgz # remplacer x.y.z par votre version
- Lancer le script d'installation:
cd /opt/scodoc/tools
./install_debian10.sh
cd /opt/scodoc/tools
./install_debian10.sh
ce script crée un compte utilisateur "scodoc".
@ -93,18 +92,39 @@ via `sqlalchemy`.
Lancer le script:
su postgres
sudo su
cd /opt/scodoc/tools
./create_database.sh
./create_users_database.sh
Ce script crée une base nommée `SCO8USERS`, appartenant à l'utilisateur (role) postgres `scodoc`.
Cet utilisateur est automatiquement créé si nécessaire.
### variables d'environnement
Le serveur utilise des variables d'environnement donnant la configuration de base.
Le plus simple est de les grouper dans un fichier `.env` (dans `/opt/scodoc/.env`)
qui est lu automatiquement au démarrage:
# .env for ScoDoc _development_
FLASK_APP=scodoc.py
FLASK_ENV=development
MAIL_SERVER=votre.serveur.de.mail.net # ou vide si pas de mail
MAIL_PORT=25
SCODOC_ADMIN_MAIL="adresse.admin@toto.fr" # important: le mail de admin
USERS_DATABASE_URI="postgresql://scodoc:le_mot_de_passe@localhost:5432/SCO8USERS"
SECRET_KEY="CGGAJAKlh6789JJK?KNAb=" # une chaine aléatoire
# comment out to use CDN:
BOOTSTRAP_SERVE_LOCAL=1
### Initialisation de la base utilisateur par Flask
En tant qu'utilisateur `scodoc`:
su scodoc # si besoin
cd /opt/scodoc
source venv/bin/activate
flask db init
flask db migrate -m "users and roles tables"
flask db upgrade
@ -126,7 +146,8 @@ ou mieux, importer les utilisateurs de ScoDoc7 avec:
flask user-db-import-scodoc7
(la base `SCOUSERS` de ScoDoc7 n'est pas affectée, ScoDoc8 utilise une base séparée, nommée `SCO8USERS`).
(on peut le faire plus tard avec le script de migration décrit plus bas)
(Note: la base `SCOUSERS` de ScoDoc7 n'est pas affectée, ScoDoc8 utilise une base séparée, nommée `SCO8USERS`).
Pour créer un utilisateur "super admin", c'est à dire admin dans tous les départements:
@ -141,20 +162,31 @@ de votre installation ScoDoc 7 pour passer à ScoDoc 8 (*ne pas utiliser en prod
1. Lancer le script de migration
ScoDoc 8 doit avoir été installé comme expliqué plus haut.
sudo su
cd /opt/scodoc/tools
./migrate_from_scodoc7.sh
sudo su
cd /opt/scodoc/tools
./migrate_from_scodoc7.sh
## Création d'un département
su scodoc # si besoin
cd /opt/scodoc
source venv/bin/activate
flask sco-create-dept DEPT
`DEPT` est le nom du département (un acronyme en majuscule, comme "RT", "GEA", ...).
### Suppression d'un département
su scodoc # si besoin
cd /opt/scodoc
source venv/bin/activate
flask sco-delete-dept DEPT
## Lancement serveur (développement, sur VM Linux)
En tant qu'utilisateur `scodoc` (pour avoir accès aux bases départements de ScoDoc7):
1. Lancer memcached:
memcached
2. Dans un autre terminal, lancer le serveur:
Dans un terminal, lancer le serveur:
export FLASK_APP=scodoc.py
export FLASK_ENV=development
@ -181,15 +213,39 @@ Principaux contenus:
/opt/scodoc-data/photos # Photos des étudiants
/opt/scodoc-data/archives # Archives: PV de jury, maquettes Apogée, fichiers étudiants
## Pour les développeurs
## Tests unitaires
### Installation du code
Procéder comme indiquer au début, mais au lieu de técharger une *release*,
partir d'un clone git et se placer sur la branche *ScoDoc8*:
sudo su
cd /opt
git clone https://scodoc.org/git/viennet/ScoDoc.git
# (ou bien utiliser votre clone gitea so vous l'avez déjà créé !)
mv ScoDoc scodoc # important !
cd /opt/scodoc
git checkout ScoDoc8
### 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
# TODO
Ou avec couverture (`pip install pytest-cov`)
pytest --cov=app --cov-report=term-missing --cov-branch tests/unit/*
## TODO
- page d'erreur ScoValueError
- redirection pour authentification
- import/export Excel

View File

@ -1 +1 @@
8.0.0
8.0.4

View File

@ -1,6 +1,7 @@
# -*- coding: UTF-8 -*
# pylint: disable=invalid-name
from app.scodoc.sco_exceptions import ScoValueError
import os
import sys
@ -11,6 +12,7 @@ from flask import request
from flask import Flask
from flask import current_app
from flask import g
from flask import render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
@ -33,7 +35,16 @@ mail = Mail()
bootstrap = Bootstrap()
moment = Moment()
cache = Cache(config={"CACHE_TYPE": "MemcachedCache"}) # XXX TODO: configuration file
cache = Cache( # XXX TODO: configuration file
config={
# "CACHE_TYPE": "MemcachedCache"
"CACHE_TYPE": "RedisCache"
}
)
def handle_sco_value_error(exc):
return render_template("sco_value_error.html", exc=exc), 404
def create_app(config_class=Config):
@ -50,6 +61,8 @@ def create_app(config_class=Config):
cache.init_app(app)
sco_cache.CACHE = cache
app.register_error_handler(ScoValueError, handle_sco_value_error)
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix="/auth")

View File

@ -66,17 +66,17 @@ class User(UserMixin, db.Model):
assert admin_role
self.add_role(admin_role, None)
db.session.commit()
current_app.logger.info("creating user with roles={}".format(self.roles))
# current_app.logger.info("creating user with roles={}".format(self.roles))
def __repr__(self):
return "<User {u.user_name} id={u.id} dept={u.dept}>".format(u=self)
return f"<User {self.user_name} id={self.id} dept={self.dept}{' (inactive)' if not self.active else ''}>"
def __str__(self):
return self.user_name
def set_password(self, password):
"Set password"
current_app.logger.info("set_password({})".format(self))
current_app.logger.info(f"set_password({self})")
if password:
self.password_hash = generate_password_hash(password)
else:
@ -92,7 +92,7 @@ class User(UserMixin, db.Model):
# Special case: user freshly migrated from ScoDoc7
if scu.check_scodoc7_password(self.password_scodoc7, password):
current_app.logger.warning(
"migrating legacy ScoDoc7 password for {}".format(self)
f"migrating legacy ScoDoc7 password for {self}"
)
self.set_password(password)
self.password_scodoc7 = None
@ -132,15 +132,15 @@ class User(UserMixin, db.Model):
"date_created": self.date_created.isoformat() + "Z"
if self.date_created
else "",
"dept": (self.dept or "").encode("utf-8"), # sco8
"dept": (self.dept or ""), # sco8
"id": self.id,
"active": self.active,
"status_txt": "actif" if self.active else "fermé",
"last_seen": self.last_seen.isoformat() + "Z",
"nom": (self.nom or "").encode("utf-8"), # sco8
"prenom": (self.prenom or "").encode("utf-8"), # sco8
"nom": (self.nom or ""), # sco8
"prenom": (self.prenom or ""), # sco8
"roles_string": self.get_roles_string(), # eg "Ens_RT, Ens_Info"
"user_name": self.user_name.encode("utf-8"), # sco8
"user_name": self.user_name, # sco8
# Les champs calculés:
"nom_fmt": self.get_nom_fmt(),
"prenom_fmt": self.get_prenom_fmt(),
@ -155,9 +155,9 @@ class User(UserMixin, db.Model):
def from_dict(self, data, new_user=False):
"""Set users' attributes from given dict values.
Roles must be encodes as "roles_string", like "Ens_RT, Secr_CJ"
Roles must be encoded as "roles_string", like "Ens_RT, Secr_CJ"
"""
for field in ["nom", "prenom", "dept", "status", "email"]:
for field in ["nom", "prenom", "dept", "active", "email", "date_expiration"]:
if field in data:
setattr(self, field, data[field])
if new_user:

View File

@ -4,6 +4,7 @@ auth.routes.py
"""
from flask import render_template, redirect, url_for, current_app, flash, request
from flask_login.utils import login_required
from werkzeug.urls import url_parse
from flask_login import login_user, logout_user, current_user

View File

@ -64,12 +64,12 @@ class ZRequest(object):
elif request.method == "GET":
self.form = {}
for k in request.args:
current_app.logger.debug("%s\t%s" % (k, request.args.getlist(k)))
# current_app.logger.debug("%s\t%s" % (k, request.args.getlist(k)))
if k.endswith(":list"):
self.form[k[:-5]] = request.args.getlist(k)
else:
self.form[k] = request.args[k]
current_app.logger.info("ZRequest.form=%s" % str(self.form))
# current_app.logger.info("ZRequest.form=%s" % str(self.form))
self.RESPONSE = ZResponse()
def __str__(self):
@ -91,7 +91,7 @@ class ZResponse(object):
self.headers = {}
def redirect(self, url):
current_app.logger.debug("ZResponse redirect to:" + str(url))
# current_app.logger.debug("ZResponse redirect to:" + str(url))
return flask.redirect(url) # http 302
def setHeader(self, header, value):
@ -108,11 +108,12 @@ def permission_required(permission):
# current_app.logger.info(
# "permission_required: %s in %s" % (permission, g.scodoc_dept)
# )
if not current_user.has_permission(permission, g.scodoc_dept):
scodoc_dept = getattr(g, "scodoc_dept", None)
if not current_user.has_permission(permission, scodoc_dept):
abort(403)
return f(*args, **kwargs)
return decorated_function
return login_required(decorated_function)
return decorator
@ -163,9 +164,8 @@ def scodoc7func(context):
req_args = REQUEST.form # args from query string (get) or form (post)
# --- Add positional arguments
pos_arg_values = []
# PY3 à remplacer par inspect.getfullargspec en py3: TODO
argspec = inspect.getargspec(func)
current_app.logger.info("argspec=%s" % str(argspec))
argspec = inspect.getfullargspec(func)
# current_app.logger.info("argspec=%s" % str(argspec))
nb_default_args = len(argspec.defaults) if argspec.defaults else 0
if nb_default_args:
arg_names = argspec.args[:-nb_default_args]
@ -178,8 +178,8 @@ def scodoc7func(context):
pos_arg_values.append(context)
else:
pos_arg_values.append(req_args[arg_name])
current_app.logger.info("pos_arg_values=%s" % pos_arg_values)
current_app.logger.info("req_args=%s" % req_args)
# current_app.logger.info("pos_arg_values=%s" % pos_arg_values)
# current_app.logger.info("req_args=%s" % req_args)
# Add keyword arguments
if nb_default_args:
for arg_name in argspec.args[-nb_default_args:]:
@ -188,10 +188,10 @@ def scodoc7func(context):
elif arg_name in req_args:
# set argument kw optionnel
kwargs[arg_name] = req_args[arg_name]
current_app.logger.info(
"scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s"
% (top_level, pos_arg_values, kwargs)
)
# current_app.logger.info(
# "scodoc7func_decorator: top_level=%s, pos_arg_values=%s, kwargs=%s"
# % (top_level, pos_arg_values, kwargs)
# )
value = func(*pos_arg_values, **kwargs)
if not top_level:

View File

@ -757,6 +757,6 @@ def tf_error_message(msg):
if isinstance(msg, str):
msg = [msg]
return (
'<ul class="tf-msg"><li class="tf-msg">%s</li></ul>'
% '</li><li class="tf-msg">'.join(msg)
'<ul class="tf-msg"><li class="tf-msg error-message">%s</li></ul>'
% '</li><li class="tf-msg tf-msg error-message">'.join(msg)
)

View File

@ -1,7 +1,7 @@
# -*- mode: python -*-
# -*- coding: utf-8 -*-
SCOVERSION = "8.0.0"
SCOVERSION = "8.0.1"
SCONAME = "ScoDoc"
@ -18,7 +18,7 @@ SCONEWS = """
<h4>Année 2020</h4>
<ul>
<li>Corrections d'erreurs, améliorations saisie absences et affichage bulletins</li>
<li>Nouveau site <a href="https://scodoc.org">scodoc.org</a> pour la documentation</li>
<li>Nouveau site <a href="https://scodoc.org" target="_blank" rel="noopener noreferrer">scodoc.org</a> pour la documentation</li>
<li>Enregistrement de semestres extérieurs</li>
<li>Améliorations PV de Jury</li>
<li>Contributions J.-M. Place: aide au diagnostic problèmes export Apogée
@ -128,7 +128,6 @@ SCONEWS = """
<h4>Janvier 2010</h4>
<ul>
<li>Suivez l'actualité du développement sur Twitter: <a href="https://twitter.com/ScoDoc">@ScoDoc</a></li>
<li>Nouveau menu "Groupes" pour faciliter la prise en main</li>
<li>Possibilité de définir des règles ad hoc de calcul des moyennes de modules (formules)</li>
<li>Possibilité d'inclure des images (logos) dans les bulletins PDF</li>

View File

@ -78,6 +78,7 @@ class DEFAULT_TABLE_PREFERENCES(object):
"SCOLAR_FONT": "Helvetica", # used for PDF, overriden by preferences argument
"SCOLAR_FONT_SIZE": 10,
"SCOLAR_FONT_SIZE_FOOT": 6,
"bul_pdf_with_background": False,
}
def __getitem__(self, k):
@ -278,7 +279,7 @@ class GenTable(object):
"""
if format == "html":
return self.html()
elif format == "xls":
elif format == "xls" or format == "xlsx":
return self.excel()
elif format == "text" or format == "csv":
return self.text()
@ -464,22 +465,24 @@ class GenTable(object):
return "\n".join(H)
def excel(self, wb=None):
"Simple Excel representation of the table"
L = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name)
style_bold = sco_excel.Excel_MakeStyle(bold=True)
L.cells += self.xls_before_table
L.set_style(style_bold, li=len(L.cells))
L.append(self.get_titles_list())
L.cells += [[x for x in line] for line in self.get_data_list()]
"""Simple Excel representation of the table"""
ses = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
ses.rows += self.xls_before_table
style_bold = sco_excel.excel_make_style(bold=True)
style_base = sco_excel.excel_make_style()
ses.append_row(ses.make_row(self.get_titles_list(), style_bold))
for line in self.get_data_list():
ses.append_row(ses.make_row(line, style_base))
if self.caption:
L.append([]) # empty line
L.append([self.caption])
ses.append_blank_row() # empty line
ses.append_single_cell_row(self.caption, style_base)
if self.origin:
L.append([]) # empty line
L.append([self.origin])
return L.gen_workbook(wb=wb)
ses.append_blank_row() # empty line
ses.append_single_cell_row(self.origin, style_base)
if wb is None:
return ses.generate_standalone()
else:
ses.generate_embeded()
def text(self):
"raw text representation of the table"
@ -597,7 +600,7 @@ class GenTable(object):
v = str(v)
r[cid] = v
d.append(r)
return json.dumps(d, encoding=scu.SCO_ENCODING)
return json.dumps(d)
def make_page(
self,
@ -627,8 +630,6 @@ class GenTable(object):
H.append(
self.html_header
or html_sco_header.sco_header(
context,
REQUEST,
page_title=page_title,
javascripts=javascripts,
init_qtip=init_qtip,
@ -638,7 +639,7 @@ class GenTable(object):
H.append(html_title)
H.append(self.html())
if with_html_headers:
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
elif format == "pdf":
objects = self.pdf()
@ -649,10 +650,12 @@ class GenTable(object):
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
else:
return doc
elif format == "xls":
elif format == "xls" or format == "xlsx":
xls = self.excel()
if publish:
return sco_excel.sendExcelFile(REQUEST, xls, filename + ".xls")
return sco_excel.send_excel_file(
REQUEST, xls, filename + scu.XLSX_SUFFIX
)
else:
return xls
elif format == "text":
@ -700,7 +703,7 @@ class SeqGenTable(object):
# ----- Exemple d'utilisation minimal.
if __name__ == "__main__":
T = GenTable(
rows=[{"nom": "Toto", "age": 26}, {"nom": "Titi", "age": 21}],
rows=[{"nom": "Hélène", "age": 26}, {"nom": "Titi&çà§", "age": 21}],
columns_ids=("nom", "age"),
)
print("--- HTML:")
@ -709,3 +712,24 @@ if __name__ == "__main__":
print(T.gen(format="xml"))
print("\n--- JSON:")
print(T.gen(format="json"))
# Test pdf:
import io
from reportlab.platypus import KeepInFrame
from app.scodoc import sco_preferences, sco_pdf
preferences = sco_preferences.SemPreferences()
T.preferences = preferences
objects = T.gen(format="pdf")
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
doc = io.BytesIO()
document = sco_pdf.BaseDocTemplate(doc)
document.addPageTemplates(
sco_pdf.ScolarsPageTemplate(
document,
)
)
document.build(objects)
data = doc.getvalue()
open("/tmp/gen_table.pdf", "wb").write(data)
p = T.make_page(None, format="pdf", REQUEST=None)
open("toto.pdf", "wb").write(p)

View File

@ -28,8 +28,10 @@
"""HTML Header/Footer for ScoDoc pages
"""
import cgi
import html
from flask import g
from flask import request
from flask_login import current_user
import app.scodoc.sco_utils as scu
@ -124,9 +126,9 @@ _HTML_BEGIN = """<?xml version="1.0" encoding="%(encoding)s"?>
"""
def scodoc_top_html_header(context, REQUEST, page_title="ScoDoc"):
def scodoc_top_html_header(page_title="ScoDoc: bienvenue"):
H = [
_HTML_BEGIN % {"page_title": "ScoDoc: bienvenue", "encoding": scu.SCO_ENCODING},
_HTML_BEGIN % {"page_title": page_title, "encoding": scu.SCO_ENCODING},
_TOP_LEVEL_CSS,
"""</head><body class="gtrcontent" id="gtrcontent">""",
scu.CUSTOM_HTML_HEADER_CNX,
@ -136,10 +138,7 @@ def scodoc_top_html_header(context, REQUEST, page_title="ScoDoc"):
# Header:
def sco_header(
context,
REQUEST=None,
# optional args
container=None, # objet qui a lancé la demande
page_title="", # page title
no_side_bar=False, # hide sidebar
cssstyles=[], # additionals CSS sheets
@ -158,17 +157,14 @@ def sco_header(
"Main HTML page header for ScoDoc"
from app.scodoc.sco_formsemestre_status import formsemestre_page_title
# context est une instance de ZScolar. container est une instance qui "acquiert" ZScolar
if container:
context = container # je pense que cela suffit pour ce qu'on veut.
context = None # XXX TODO à enlever #context
# Add a HTTP header (can be used by Apache to log requests)
if REQUEST.AUTHENTICATED_USER:
REQUEST.RESPONSE.setHeader("X-ScoDoc-User", str(REQUEST.AUTHENTICATED_USER))
# Get more parameters from REQUEST
if not head_message and "head_message" in REQUEST.form:
head_message = REQUEST.form["head_message"]
# Get head message from http request:
if not head_message:
if request.method == "POST":
head_message = request.form.get("head_message", "")
elif request.method == "GET":
head_message = request.args.get("head_message", "")
params = {
"page_title": page_title or VERSION.SCONAME,
@ -176,7 +172,7 @@ def sco_header(
"ScoURL": scu.ScoURL(),
"encoding": scu.SCO_ENCODING,
"titrebandeau_mkup": "<td>" + titrebandeau + "</td>",
"authuser": str(REQUEST.AUTHENTICATED_USER),
"authuser": current_user.user_name,
}
if bodyOnLoad:
params["bodyOnLoad_mkup"] = """onload="%s" """ % bodyOnLoad
@ -308,11 +304,11 @@ def sco_header(
H.append(scu.CUSTOM_HTML_HEADER)
#
if not no_side_bar:
H.append(html_sidebar.sidebar(context, REQUEST))
H.append(html_sidebar.sidebar())
H.append("""<div class="gtrcontent" id="gtrcontent">""")
#
# Barre menu semestre:
H.append(formsemestre_page_title(context, REQUEST))
H.append(formsemestre_page_title(context))
# Avertissement si mot de passe à changer
if user_check:
@ -327,7 +323,7 @@ def sco_header(
)
#
if head_message:
H.append('<div class="head_message">' + cgi.escape(head_message) + "</div>")
H.append('<div class="head_message">' + html.escape(head_message) + "</div>")
#
# div pour affichage messages temporaires
H.append('<div id="sco_msg" class="head_message"></div>')
@ -335,7 +331,7 @@ def sco_header(
return "".join(H)
def sco_footer(context, REQUEST=None):
def sco_footer():
"""Main HTMl pages footer"""
return (
"""</div><!-- /gtrcontent -->""" + scu.CUSTOM_HTML_FOOTER + """</body></html>"""
@ -355,9 +351,7 @@ def html_sem_header(
"Titre d'une page semestre avec lien vers tableau de bord"
# sem now unused and thus optional...
if with_page_header:
h = sco_header(
context, REQUEST, page_title="%s" % (page_title or title), **args
)
h = sco_header(page_title="%s" % (page_title or title), **args)
else:
h = ""
if with_h2:

View File

@ -28,54 +28,56 @@
"""
Génération de la "sidebar" (marge gauche des pages HTML)
"""
from flask import url_for, g
from flask import url_for
from flask import g
from flask import request
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc import sco_preferences
from app.scodoc.sco_permissions import Permission
def sidebar_common(context, REQUEST=None):
def sidebar_common():
"partie commune à toutes les sidebar"
authuser = REQUEST.AUTHENTICATED_USER
params = {
"ScoURL": scu.ScoURL(),
"UsersURL": scu.UsersURL(),
"NotesURL": scu.NotesURL(),
"AbsencesURL": scu.AbsencesURL(),
"LogoutURL": url_for("auth.logout"),
"authuser": str(authuser),
"authuser": current_user.user_name,
}
H = [
'<a class="scodoc_title" href="about">ScoDoc 8</a>',
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/user_info_page">%(authuser)s</a><br/><a id="deconnectlink" href="%(LogoutURL)s">déconnexion</a></div>'
% params,
sidebar_dept(context, REQUEST),
"""<h2 class="insidebar">Scolarit&eacute;</h2>
<a href="%(ScoURL)s" class="sidebar">Semestres</a> <br/>
<a href="%(NotesURL)s" class="sidebar">Programmes</a> <br/>
<a href="%(AbsencesURL)s" class="sidebar">Absences</a> <br/>
"""
% params,
f"""<a class="scodoc_title" href="about">ScoDoc 8</a>
<div id="authuser"><a id="authuserlink" href="{
url_for("users.user_info_page", scodoc_dept=g.scodoc_dept, user_name=current_user.user_name)
}">{current_user.user_name}</a>
<br/><a id="deconnectlink" href="{url_for("auth.logout")}">déconnexion</a>
</div>
{sidebar_dept()}
<h2 class="insidebar">Scolarité</h2>
<a href="{scu.ScoURL()}" class="sidebar">Semestres</a> <br/>
<a href="{scu.NotesURL()}" class="sidebar">Programmes</a> <br/>
<a href="{scu.AbsencesURL()}" class="sidebar">Absences</a> <br/>
"""
]
if authuser.has_permission(Permission.ScoUsersAdmin) or authuser.has_permission(
Permission.ScoUsersView
):
if current_user.has_permission(
Permission.ScoUsersAdmin
) or current_user.has_permission(Permission.ScoUsersView):
H.append(
"""<a href="%(UsersURL)s" class="sidebar">Utilisateurs</a> <br/>""" % params
f"""<a href="{scu.UsersURL()}" class="sidebar">Utilisateurs</a> <br/>"""
)
if authuser.has_permission(Permission.ScoChangePreferences):
if current_user.has_permission(Permission.ScoChangePreferences):
H.append(
"""<a href="%(ScoURL)s/edit_preferences" class="sidebar">Paramétrage</a> <br/>"""
% params
f"""<a href="{url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept)}" class="sidebar">Paramétrage</a> <br/>"""
)
return "".join(H)
def sidebar(context, REQUEST=None):
def sidebar():
"Main HTML page sidebar"
# rewritten from legacy DTML code
from app.scodoc import sco_abs
@ -86,7 +88,7 @@ def sidebar(context, REQUEST=None):
"SCO_USER_MANUAL": scu.SCO_USER_MANUAL,
}
H = ['<div class="sidebar">', sidebar_common(context, REQUEST)]
H = ['<div class="sidebar">', sidebar_common()]
H.append(
"""<div class="box-chercheetud">Chercher étudiant:<br/>
@ -97,9 +99,14 @@ def sidebar(context, REQUEST=None):
"""
% params
)
# ---- s'il y a un etudiant selectionné:
if "etudid" in REQUEST.form:
etudid = REQUEST.form["etudid"]
# ---- Il y-a-t-il un etudiant selectionné ?
etudid = None
if request.method == "GET":
etudid = request.args.get("etudid", None)
elif request.method == "POST":
etudid = request.form.get("etudid", None)
if etudid:
etud = sco_etud.get_etud_info(filled=1, etudid=etudid)[0]
params.update(etud)
params["fiche_url"] = url_for(
@ -126,7 +133,7 @@ def sidebar(context, REQUEST=None):
)
H.append("<ul>")
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoAbsChange):
if current_user.has_permission(Permission.ScoAbsChange):
H.append(
"""
<li> <a href="%(ScoURL)s/Absences/SignaleAbsenceEtud?etudid=%(etudid)s">Ajouter</a></li>
@ -135,7 +142,7 @@ def sidebar(context, REQUEST=None):
"""
% params
)
if sco_preferences.get_preference(context, "handle_billets_abs"):
if sco_preferences.get_preference("handle_billets_abs"):
H.append(
"""<li> <a href="%(ScoURL)s/Absences/listeBilletsEtud?etudid=%(etudid)s">Billets</a></li>"""
% params
@ -168,27 +175,17 @@ def sidebar(context, REQUEST=None):
return "".join(H)
def sidebar_dept(context, REQUEST=None):
def sidebar_dept():
"""Partie supérieure de la marge de gauche"""
infos = {
"BASE0": REQUEST.BASE0,
"DeptIntranetTitle": sco_preferences.get_preference(
context, "DeptIntranetTitle"
),
"DeptIntranetURL": sco_preferences.get_preference(context, "DeptIntranetURL"),
"DeptName": sco_preferences.get_preference(context, "DeptName"),
"ScoURL": scu.ScoURL(),
}
H = [
"""<h2 class="insidebar">Dépt. %(DeptName)s</h2>
<a href="%(BASE0)s" class="sidebar">Accueil</a> <br/> """
% infos
f"""<h2 class="insidebar">Dépt. {sco_preferences.get_preference("DeptName")}</h2>
<a href="{url_for("scodoc.index")}" class="sidebar">Accueil</a> <br/> """
]
if infos["DeptIntranetURL"]:
dept_intranet_url = sco_preferences.get_preference("DeptIntranetURL")
if dept_intranet_url:
H.append(
'<a href="%(DeptIntranetURL)s" class="sidebar">%(DeptIntranetTitle)s</a> <br/>'
% infos
f"""<a href="{dept_intranet_url}" class="sidebar">{
sco_preferences.get_preference("DeptIntranetTitle")}</a> <br/>"""
)
# Entreprises pas encore supporté en ScoDoc8
# H.append(

View File

@ -2,14 +2,13 @@
# -*- coding: utf-8 -*-
import os
import re
import inspect
import time
import traceback
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from flask import current_app
from flask import g, current_app
"""Simple & stupid file logguer, used only to debug
(logging to SQL is done in scolog)
@ -18,7 +17,7 @@ from flask import current_app
LOG_FILENAME = "notes.log" # empty to disable logging
DEFAULT_LOG_DIR = "/tmp" # clients should call set_log_directory to change this
ALARM_DESTINATION = "emmanuel.viennet@univ-paris13.fr" # XXX a mettre en preference
ALARM_DESTINATION = "emmanuel.viennet@gmail.com" # XXX a mettre en preference
class _logguer(object):
@ -46,7 +45,11 @@ class _logguer(object):
if not self.file:
self._open()
if self.file:
dept = retreive_dept()
try:
dept = getattr(g, "scodoc_dept", "")
except RuntimeError:
# Flask Working outside of application context.
dept = ""
if dept:
dept = " (%s)" % dept
msg = dept + " " + msg
@ -60,42 +63,6 @@ class _logguer(object):
log = _logguer()
def retreive_request(skip=0):
"""Try to retreive a REQUEST variable in caller stack.
This is a hack, used only in log functions.
"""
def search(frame):
if "REQUEST" in frame.f_locals:
return frame.f_locals["REQUEST"]
if frame.f_back:
return search(frame.f_back)
else:
return None
frame = inspect.currentframe()
if frame: # not supported by all pythons
startframe = frame
while skip and startframe.f_back:
startframe = startframe.f_back
return search(startframe)
else:
return None
def retreive_dept():
"""Try to retreive departement (from REQUEST URL)"""
REQUEST = retreive_request()
if not REQUEST:
return ""
try:
url = REQUEST.URL
m = re.match(r"^.*ScoDoc/(\w+).*$", url)
return m.group(1)
except:
return ""
# Alarms by email:
def sendAlarm(context, subj, txt):
from . import sco_utils
@ -105,7 +72,7 @@ def sendAlarm(context, subj, txt):
msg = MIMEMultipart()
subj = Header(subj, sco_utils.SCO_ENCODING)
msg["Subject"] = subj
msg["From"] = sco_preferences.get_preference(context, "email_from_addr")
msg["From"] = sco_preferences.get_preference("email_from_addr")
msg["To"] = ALARM_DESTINATION
msg.epilogue = ""
txt = MIMEText(txt, "plain", sco_utils.SCO_ENCODING)

View File

@ -131,7 +131,6 @@ def comp_etud_sum_coef_modules_ue(context, formsemestre_id, etudid, ue_id):
(nécessaire pour éviter appels récursifs de nt, qui peuvent boucler)
"""
infos = ndb.SimpleDictFetch(
context,
"""SELECT mod.coefficient
FROM notes_modules mod, notes_moduleimpl mi, notes_moduleimpl_inscription ins
WHERE mod.module_id = mi.module_id
@ -188,7 +187,7 @@ class NotesTable(object):
self._uecoef = {} # { ue_id : coef } cache coef manuels ue cap
self._evaluations_etats = None # liste des evaluations avec état
self.use_ue_coefs = sco_preferences.get_preference(
context, "use_ue_coefs", formsemestre_id
"use_ue_coefs", formsemestre_id
)
# Infos sur les etudiants
self.inscrlist = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
@ -1303,7 +1302,7 @@ class NotesTable(object):
"""[ {...evaluation et son etat...} ]"""
if self._evaluations_etats is None:
self._evaluations_etats = sco_evaluations.do_evaluation_list_in_sem(
self.context, self.formsemestre_id
self.formsemestre_id
)
return self._evaluations_etats

View File

@ -33,10 +33,11 @@ def unquote(s):
return s.replace("&amp;", "&")
def open_dept_connection():
"""Open a connection to the current dept db"""
# log("open_dept_connection to " + scu.get_db_cnx_string())
return psycopg2.connect(scu.get_db_cnx_string())
def open_dept_connection(scodoc_dept=None):
"""Open a connection to the current dept db (g.scodoc_dept)
or to the argument scodoc_dept
"""
return psycopg2.connect(scu.get_db_cnx_string(scodoc_dept))
def close_dept_connection():
@ -46,7 +47,16 @@ def close_dept_connection():
g.db_conn.close()
# Essai bien plus simple pour Flask:
def set_sco_dept(scodoc_dept):
"""Set "context" to given dept
open db connection
"""
g.scodoc_dept = scodoc_dept
if hasattr(g, "db_conn"):
close_dept_connection()
g.db_conn = open_dept_connection()
def GetDBConnexion(autocommit=True): # on n'utilise plus autocommit
return g.db_conn
@ -68,7 +78,7 @@ class ScoDocCursor(psycopg2.extensions.cursor):
return {}
def SimpleQuery(context, query, args, cursor=None):
def SimpleQuery(query, args, cursor=None):
if not cursor:
cnx = GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
@ -77,8 +87,8 @@ def SimpleQuery(context, query, args, cursor=None):
return cursor
def SimpleDictFetch(context, query, args, cursor=None):
cursor = SimpleQuery(context, query, args, cursor=cursor)
def SimpleDictFetch(query, args, cursor=None):
cursor = SimpleQuery(query, args, cursor=cursor)
return cursor.dictfetchall()
@ -110,7 +120,7 @@ def DBInsertDict(cnx, table, vals, commit=0, convert_empty_to_nulls=1):
cnx.commit() # get rid of this transaction
raise # and re-raise exception
if commit:
log("DBInsertDict: commit (requested)")
# log("DBInsertDict: commit (requested)")
cnx.commit()
return oid
@ -202,7 +212,6 @@ def DBSelectArgs(
+ limit
+ offset
)
# open('/tmp/select.log','a').write( req % vals + '\n' )
try:
cursor.execute(req, vals)
except:
@ -249,9 +258,6 @@ def DBDelete(cnx, table, colid, val, commit=False):
# --------------------------------------------------------------------
# REQLOG = open('/tmp/req.log', 'w') # DEBUG
# REQN = 0
class EditableTable(object):
"""--- generic class: SQL table with create/edit/list/delete"""
@ -266,7 +272,6 @@ class EditableTable(object):
input_formators={},
aux_tables=[],
convert_null_outputs_to_empty=True,
callback_on_write=None,
allow_set_id=False,
html_quote=True,
fields_creators={}, # { field : [ sql_command_to_create_it ] }
@ -280,16 +285,13 @@ class EditableTable(object):
self.output_formators = output_formators
self.input_formators = input_formators
self.convert_null_outputs_to_empty = convert_null_outputs_to_empty
self.callback_on_write = (
callback_on_write # called after each modification (USELESS and unused)
)
self.allow_set_id = allow_set_id
self.html_quote = html_quote
self.fields_creators = fields_creators
self.filter_nulls = filter_nulls
self.sql_default_values = None
def create(self, cnx, args, has_uniq_values=False):
def create(self, cnx, args):
"create object in table"
vals = dictfilter(args, self.dbfields, self.filter_nulls)
if self.id_name in vals and not self.allow_set_id:
@ -297,7 +299,7 @@ class EditableTable(object):
if self.html_quote:
quote_dict(vals) # quote all HTML markup
# format value
for title in vals.keys():
for title in vals:
if title in self.input_formators:
vals[title] = self.input_formators[title](vals[title])
# insert
@ -309,23 +311,11 @@ class EditableTable(object):
% {"id_name": self.id_name, "table_name": self.table_name, "oid": oid}
)
new_id = cursor.fetchone()[0]
if has_uniq_values: # XXX probably obsolete
# check all tuples (without id_name) are UNIQUE !
res = DBSelectArgs(cnx, self.table_name, vals, what=[self.id_name])
if len(res) != 1:
# BUG !
log("create: BUG table_name=%s args=%s" % (self.table_name, str(args)))
assert len(res) == 1, "len(res) = %d != 1 !" % len(res)
if self.callback_on_write:
self.callback_on_write()
return new_id
def delete(self, cnx, oid, commit=True):
"delete tuple"
DBDelete(cnx, self.table_name, self.id_name, oid, commit=commit)
if self.callback_on_write:
self.callback_on_write()
def list(
self,
@ -339,10 +329,6 @@ class EditableTable(object):
offset="",
):
"returns list of dicts"
# REQLOG.write('%s: %s by %s (%s) %d\n'%(self.table_name,args,sys._getframe(1).f_code.co_name, sys._getframe(2).f_code.co_name, REQN))
# REQLOG.flush()
# global REQN
# REQN = REQN + 1
vals = dictfilter(args, self.dbfields, self.filter_nulls)
if not sortkey:
sortkey = self.sortkey
@ -398,60 +384,10 @@ class EditableTable(object):
where="%s=%%(%s)s" % (self.id_name, self.id_name),
commit=True,
)
if self.callback_on_write:
self.callback_on_write()
def get_sql_default_values(self, cnx):
"return dict with SQL default values for each field"
if self.sql_default_values is None: # not cached
# We insert a new tuple, get the values and delete it
# XXX non, car certaines tables ne peuvent creer de tuples
# a default, a cause des references ou contraintes d'intégrité.
# oid = self.create(cnx, {})
# vals = self.list(cnx, args= {self.id_name : oid})[0]
# self.delete(cnx, oid)
# self.sql_default_values = vals
#
# Méthode spécifique à postgresql (>= 7.4)
cursor = cnx.cursor(cursor_factory=ScoDocCursor)
cursor.execute(
"SELECT column_name, data_type, column_default FROM information_schema.columns WHERE table_name = '%s'"
% self.table_name
)
d = {}
for info in cursor.dictfetchall():
v = info["column_default"]
# strip type information if present (eg 'hello'::text)
if v:
v = v.split("::")[0]
# convert type to Python type
if v:
if info["data_type"] == "text":
log('get: v="%s"' % v)
if v[0] == v[-1] == "'":
v = v[1:-1] # strip quotes
elif v[:2] == "E'" and v[-1] == "'":
v = v[2:-1] # postgresql string with escapes
v = v.replace(
"\\012", "\n"
) # fix (je ne comprend pas bien pourquoi les valeurs sont ici quotées, ce n'est pas le cas dans les tables ordinaires)
v = v.replace("''", "'") # idem
log('--> v="%s"' % v)
elif info["data_type"] == "real":
v = float(v)
elif info["data_type"] == "integer":
v = int(v)
# elif info['data_type'] == 'date':
# pass # XXX todo
else:
log("Warning: unhandled SQL type in get_sql_default_values")
d[info["column_name"]] = v
self.sql_default_values = d
return self.sql_default_values
def dictfilter(d, fields, filter_nulls=True):
# returns a copy of d with only keys listed in "fields" and non null values
"""returns a copy of d with only keys listed in "fields" and non null values"""
r = {}
for f in fields:
if f in d and (d[f] != None or not filter_nulls):
@ -459,7 +395,6 @@ def dictfilter(d, fields, filter_nulls=True):
val = d[f].strip()
except:
val = d[f]
# if val != '': not a good idea: how to suppress a field ?
r[f] = val
return r

View File

@ -73,7 +73,7 @@ def get_code_latex_from_scodoc_preference(
Extrait le template (ou le tag d'annotation au regard du champ fourni) des préférences LaTeX
et s'assure qu'il est renvoyé au format unicode
"""
template_latex = sco_preferences.get_preference(context, champ, formsemestre_id)
template_latex = sco_preferences.get_preference(champ, formsemestre_id)
return template_latex or ""

View File

@ -44,10 +44,7 @@ Created on Fri Sep 9 09:15:05 2016
import os
try:
from io import StringIO ## for Python 3
except ImportError:
from cStringIO import StringIO ## for Python 2
from io import StringIO
from zipfile import ZipFile, BadZipfile
import pprint
@ -197,7 +194,7 @@ class JuryPE(object):
# self.add_file_to_zip(filename, self.xls.excel())
# Fabrique 1 fichier excel résultat avec 1 seule feuille => trop gros
filename = self.NOM_EXPORT_ZIP + "_jurySyntheseDict" + ".xls"
filename = self.NOM_EXPORT_ZIP + "_jurySyntheseDict" + scu.XLSX_SUFFIX
self.xlsV2 = self.table_syntheseJury(mode="multiplesheet")
if self.xlsV2:
self.add_file_to_zip(filename, self.xlsV2.excel())
@ -498,7 +495,7 @@ class JuryPE(object):
lastdate = max(sesdates) # date de fin de l'inscription la plus récente
# if PETable.AFFICHAGE_DEBUG_PE == True : pe_tools.pe_print(" derniere inscription = ", lastDateSem)
semestresDeScoDoc = sco_formsemestre.formsemestre_list(self.context)
semestresDeScoDoc = sco_formsemestre.do_formsemestre_list(self.context)
semestresSuperieurs = [
sem for sem in semestresDeScoDoc if sem["semestre_id"] > sonDernierSidValide
] # Semestre de rang plus élevé que son dernier sem valide
@ -1243,9 +1240,9 @@ def get_cosemestres_diplomants(context, semBase, avec_meme_formation=False):
> dont la formation est la même (optionnel)
> ne prenant en compte que les etudiants sans redoublement
"""
tousLesSems = sco_formsemestre.formsemestre_list(
tousLesSems = sco_formsemestre.do_formsemestre_list(
context
) # tous les semestres memorises dans scodoc
) # tous les semestres memorisés dans scodoc
diplome = get_annee_diplome_semestre(semBase)
if avec_meme_formation: # si une formation est imposee

View File

@ -48,15 +48,13 @@ from app.scodoc import pe_avislatex
def _pe_view_sem_recap_form(context, formsemestre_id, REQUEST=None):
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Avis de poursuite d'études"
),
html_sco_header.sco_header(page_title="Avis de poursuite d'études"),
"""<h2 class="formsemestre">Génération des avis de poursuites d'études</h2>
<p class="help">
Cette fonction génère un ensemble de fichiers permettant d'éditer des avis de poursuites d'études.
<br/>
De nombreux aspects sont paramétrables:
<a href="https://scodoc.org/AvisPoursuiteEtudes">
<a href="https://scodoc.org/AvisPoursuiteEtudes" target="_blank" rel="noopener noreferrer">
voir la documentation</a>.
</p>
<form method="post" action="pe_view_sem_recap" id="pe_view_sem_recap_form" enctype="multipart/form-data">
@ -73,7 +71,7 @@ def _pe_view_sem_recap_form(context, formsemestre_id, REQUEST=None):
formsemestre_id=formsemestre_id
),
]
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def pe_view_sem_recap(
@ -91,7 +89,7 @@ def pe_view_sem_recap(
"""
if REQUEST and REQUEST.REQUEST_METHOD == "GET":
return _pe_view_sem_recap_form(context, formsemestre_id, REQUEST=REQUEST)
prefs = sco_preferences.SemPreferences(context, formsemestre_id=formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
semBase = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -144,7 +142,9 @@ def pe_view_sem_recap(
context, jury.syntheseJury, tag_annotation_pe
)
if sT:
jury.add_file_to_zip(jury.NOM_EXPORT_ZIP + "_annotationsPE.xls", sT.excel())
jury.add_file_to_zip(
jury.NOM_EXPORT_ZIP + "_annotationsPE" + scu.XLSX_SUFFIX, sT.excel()
)
latex_pages = {} # Dictionnaire de la forme nom_fichier => contenu_latex
for etudid in etudids:

View File

@ -1,56 +0,0 @@
alembic==1.6.5
astroid==2.6.2
Babel==2.9.1
blinker==1.4
certifi==2021.5.30
chardet==4.0.0
click==8.0.1
cracklib==2.9.3
dnspython==2.1.0
dominate==2.6.0
email-validator==1.1.3
Flask==2.0.1
Flask-Babel==2.0.0
Flask-Bootstrap==3.3.7.1
Flask-Login==0.5.0
Flask-Mail==0.9.1
Flask-Migrate==3.0.1
Flask-Moment==1.0.1
Flask-SQLAlchemy==2.5.1
Flask-WTF==0.15.1
greenlet==1.1.0
html2text==2020.1.16
icalendar==4.0.7
idna==2.10
importlib-metadata==4.6.1
isort==5.9.2
itsdangerous==2.0.1
Jinja2==3.0.1
lazy-object-proxy==1.6.0
Mako==1.1.4
MarkupSafe==2.0.1
mccabe==0.6.1
Pillow==8.3.1
pkg-resources==0.0.0
psycopg2==2.9.1
PyJWT==2.1.0
pylint==2.9.3
pylint-flask-sqlalchemy==0.2.0
PyRSS2Gen==1.1
python-dateutil==2.8.1
python-dotenv==0.18.0
python-editor==1.0.4
pytz==2021.1
reportlab==3.5.68
requests==2.25.1
six==1.16.0
SQLAlchemy==1.4.20
toml==0.10.2
typed-ast==1.4.3
typing-extensions==3.10.0.0
urllib3==1.26.6
visitor==0.1.3
Werkzeug==2.0.1
wrapt==1.12.1
WTForms==2.3.3
zipp==3.5.0

View File

@ -68,7 +68,7 @@ def _toboolean(x):
def is_work_saturday(context):
"Vrai si le samedi est travaillé"
return int(sco_preferences.get_preference(context, "work_saturday"))
return int(sco_preferences.get_preference("work_saturday"))
def MonthNbDays(month, year):
@ -306,11 +306,14 @@ def list_abs_in_range(etudid, debut, fin, matin=None, moduleimpl_id=None, cursor
"""Liste des absences entre deux dates.
Args:
etudid
debut string iso date ("2020-03-12")
end string iso date ("2020-03-12")
matin None, True, False
moduleimpl_id
etudid:
debut: string iso date ("2020-03-12")
end: string iso date ("2020-03-12")
matin: None, True, False
moduleimpl_id: restreint le comptage aux absences dans ce module
Returns:
List of absences
"""
if matin != None:
matin = _toboolean(matin)
@ -347,9 +350,15 @@ WHERE A.ETUDID = %(etudid)s
return res
def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None):
"""CountAbs
matin= 1 ou 0.
def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
"""compte le nombre d'absences
Args:
etudid: l'étudiant considéré
debut: date, chaîne iso, eg "2021-06-15"
fin: date de fin, incluse
matin: True (compte les matinées), False (les après-midi), None (les deux)
moduleimpl_id: restreint le comptage aux absences dans ce module.
Returns:
An integer.
@ -359,8 +368,19 @@ def count_abs(etudid, debut, fin, matin=None, moduleimpl_id=None):
)
def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None):
"Count just. abs"
def count_abs_just(etudid, debut, fin, matin=None, moduleimpl_id=None) -> int:
"""compte le nombre d'absences justifiées
Args:
etudid: l'étudiant considéré
debut: date, chaîne iso, eg "2021-06-15"
fin: date de fin, incluse
matin: True (compte les matinées), False (les après-midi), None (les deux)
moduleimpl_id: restreint le comptage aux absences dans ce module.
Returns:
An integer.
"""
if matin != None:
matin = _toboolean(matin)
ismatin = " AND A.MATIN = %(matin)s "
@ -569,9 +589,20 @@ ORDER BY A.JOUR
def list_abs_justifs(etudid, datedebut, datefin=None, only_no_abs=False):
"""Liste des justificatifs (sans absence relevée) à partir d'une date,
"""Liste des justificatifs (avec ou sans absence relevée) à partir d'une date,
ou, si datefin spécifié, entre deux dates.
Si only_no_abs: seulement les justificatifs correspondant aux jours sans absences relevées.
Args:
etudid:
datedebut: date de début, iso, eg "2002-03-15"
datefin: date de fin, incluse, eg "2002-03-15"
only_no_abs: si vrai, seulement les justificatifs correspondant
aux jours sans absences relevées.
Returns:
Liste de dict absences
{'etudid': 'EID214', 'jour': datetime.date(2021, 1, 15),
'matin': True, 'description': ''
}
"""
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@ -617,7 +648,6 @@ def add_absence(
vars(),
)
logdb(
REQUEST,
cnx,
"AddAbsence",
etudid=etudid,
@ -642,7 +672,6 @@ def add_justif(context, etudid, jour, matin, REQUEST, description=None):
vars(),
)
logdb(
REQUEST,
cnx,
"AddJustif",
etudid=etudid,
@ -666,9 +695,16 @@ def _add_abslist(context, abslist, REQUEST, moduleimpl_id=None):
add_absence(context, etudid, jour, matin, 0, REQUEST, "", moduleimpl_id)
def annule_absence(context, etudid, jour, matin, moduleimpl_id=None, REQUEST=None):
"""Annule une absence ds base
Si moduleimpl_id, n'annule que pour ce module
def annule_absence(context, etudid, jour, matin, moduleimpl_id=None):
"""Annule une absence dans la base. N'efface pas l'éventuel justificatif.
Args:
etudid:
jour: date, chaîne iso, eg "1999-12-31"
matin:
moduleimpl_id: si spécifié, n'annule que pour ce module.
Returns:
None
"""
# unpublished
matin = _toboolean(matin)
@ -679,7 +715,6 @@ def annule_absence(context, etudid, jour, matin, moduleimpl_id=None, REQUEST=Non
req += " and moduleimpl_id=%(moduleimpl_id)s"
cursor.execute(req, vars())
logdb(
REQUEST,
cnx,
"AnnuleAbsence",
etudid=etudid,
@ -704,7 +739,6 @@ def annule_justif(context, etudid, jour, matin, REQUEST=None):
vars(),
)
logdb(
REQUEST,
cnx,
"AnnuleJustif",
etudid=etudid,
@ -867,7 +901,9 @@ def MonthTableBody(
if pad_width != None:
n = pad_width - len(legend) # pad to 8 cars
if n > 0:
legend = "&nbsp;" * (n / 2) + legend + "&nbsp;" * ((n + 1) / 2)
legend = (
"&nbsp;" * (n // 2) + legend + "&nbsp;" * ((n + 1) // 2)
)
else:
legend = "&nbsp;" # empty cell
cc.append(legend)
@ -974,7 +1010,7 @@ def MonthTableBody(
#
# Cache absences
#
# On cache (via memcached ou autre, voir sco_cache.py) les _nombres_ d'absences
# On cache (via REDIS ou autre, voir sco_cache.py) les _nombres_ d'absences
# (justifiées et non justifiées) de chaque etudiant dans un semestre donné.
# Le cache peut être invalidé soit par étudiant/semestre, soit pour tous
# les étudiant d'un semestre.

View File

@ -73,9 +73,7 @@ def do_abs_notify(context, sem, etudid, date, nbabs, nbabsjust):
formsemestre_id = sem["formsemestre_id"]
else:
formsemestre_id = None
prefs = sco_preferences.SemPreferences(
context, formsemestre_id=sem["formsemestre_id"]
)
prefs = sco_preferences.SemPreferences(formsemestre_id=sem["formsemestre_id"])
destinations = abs_notify_get_destinations(
context, sem, prefs, etudid, date, nbabs, nbabsjust
@ -85,7 +83,7 @@ def do_abs_notify(context, sem, etudid, date, nbabs, nbabsjust):
return # abort
# Vérification fréquence (pour ne pas envoyer de mails trop souvent)
abs_notify_max_freq = sco_preferences.get_preference(context, "abs_notify_max_freq")
abs_notify_max_freq = sco_preferences.get_preference("abs_notify_max_freq")
destinations_filtered = []
for email_addr in destinations:
nbdays_since_last_notif = user_nbdays_since_last_notif(
@ -120,7 +118,6 @@ def abs_notify_send(
msg["To"] = email
sco_emails.sendEmail(context, msg)
ndb.SimpleQuery(
context,
"""insert into absences_notifications (etudid, email, nbabs, nbabsjust, formsemestre_id) values (%(etudid)s, %(email)s, %(nbabs)s, %(nbabsjust)s, %(formsemestre_id)s)""",
vars(),
cursor=cursor,
@ -184,10 +181,10 @@ def abs_notify_is_above_threshold(context, etudid, nbabs, nbabsjust, formsemestr
(nbabs - nbabs_last_notified) > abs_notify_abs_increment
"""
abs_notify_abs_threshold = sco_preferences.get_preference(
context, "abs_notify_abs_threshold", formsemestre_id
"abs_notify_abs_threshold", formsemestre_id
)
abs_notify_abs_increment = sco_preferences.get_preference(
context, "abs_notify_abs_increment", formsemestre_id
"abs_notify_abs_increment", formsemestre_id
)
nbabs_last_notified = etud_nbabs_last_notified(context, etudid, formsemestre_id)
@ -278,7 +275,7 @@ def retreive_current_formsemestre(context, etudid, cur_date):
WHERE sem.formsemestre_id = i.formsemestre_id AND i.etudid=%(etudid)s
AND (%(cur_date)s >= sem.date_debut) AND (%(cur_date)s <= sem.date_fin)"""
r = ndb.SimpleDictFetch(context, req, {"etudid": etudid, "cur_date": cur_date})
r = ndb.SimpleDictFetch(req, {"etudid": etudid, "cur_date": cur_date})
if not r:
return None
# s'il y a plusieurs semestres, prend le premier (rarissime et non significatif):
@ -291,5 +288,5 @@ def mod_with_evals_at_date(context, date_abs, etudid):
req = """SELECT m.* FROM notes_moduleimpl m, notes_evaluation e, notes_moduleimpl_inscription i
WHERE m.moduleimpl_id = e.moduleimpl_id AND e.moduleimpl_id = i.moduleimpl_id
AND i.etudid = %(etudid)s AND e.jour = %(date_abs)s"""
r = ndb.SimpleDictFetch(context, req, {"etudid": etudid, "date_abs": date_abs})
r = ndb.SimpleDictFetch(req, {"etudid": etudid, "date_abs": date_abs})
return r

View File

@ -132,8 +132,6 @@ def doSignaleAbsence(
M = "dans le module %s" % modimpl["module"]["code"]
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Signalement d'une absence pour %(nomprenom)s" % etud,
),
"""<h2>Signalement d'absences</h2>""",
@ -157,7 +155,7 @@ def doSignaleAbsence(
% etud
)
H.append(sco_find_etud.form_search_etud(context, REQUEST))
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -169,7 +167,7 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
disabled = False
if not etud["cursem"]:
require_module = sco_preferences.get_preference(
context, "abs_require_module"
"abs_require_module"
) # on utilise la pref globale car pas de sem courant
if require_module:
menu_module = """<div class="ue_warning">Pas inscrit dans un semestre courant,
@ -180,7 +178,7 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
else:
formsemestre_id = etud["cursem"]["formsemestre_id"]
require_module = sco_preferences.get_preference(
context, "abs_require_module", formsemestre_id
"abs_require_module", formsemestre_id
)
nt = sco_cache.NotesTableCache.get(formsemestre_id)
ues = nt.get_ues(etudid=etudid)
@ -221,8 +219,6 @@ def SignaleAbsenceEtud(context, REQUEST=None): # etudid implied
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Signalement d'une absence pour %(nomprenom)s" % etud,
),
"""<table><tr><td>
@ -278,7 +274,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
"menu_module": menu_module,
"disabled": "disabled" if disabled else "",
},
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -340,8 +336,6 @@ def doJustifAbsence(
#
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Justification d'une absence pour %(nomprenom)s" % etud,
),
"""<h2>Justification d'absences</h2>""",
@ -367,7 +361,7 @@ def doJustifAbsence(
% etud
)
H.append(sco_find_etud.form_search_etud(context, REQUEST))
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -378,8 +372,6 @@ def JustifAbsenceEtud(context, REQUEST=None): # etudid implied
etudid = etud["etudid"]
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Justification d'une absence pour %(nomprenom)s" % etud,
),
"""<table><tr><td>
@ -424,7 +416,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
</form> """
% etud,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -441,17 +433,15 @@ def doAnnuleAbsence(
demijournee = int(demijournee)
for jour in dates:
if demijournee == 2:
sco_abs.annule_absence(context, etudid, jour, False, REQUEST=REQUEST)
sco_abs.annule_absence(context, etudid, jour, True, REQUEST=REQUEST)
sco_abs.annule_absence(context, etudid, jour, False)
sco_abs.annule_absence(context, etudid, jour, True)
nbadded += 2
else:
sco_abs.annule_absence(context, etudid, jour, demijournee, REQUEST=REQUEST)
sco_abs.annule_absence(context, etudid, jour, demijournee)
nbadded += 1
#
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Annulation d'une absence pour %(nomprenom)s" % etud,
),
"""<h2>Annulation d'absences pour %(nomprenom)s</h2>""" % etud,
@ -477,7 +467,7 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
% etud
)
H.append(sco_find_etud.form_search_etud(context, REQUEST))
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -489,8 +479,6 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Annulation d'une absence pour %(nomprenom)s" % etud,
),
"""<table><tr><td>
@ -565,7 +553,7 @@ def AnnuleAbsenceEtud(context, REQUEST=None): # etudid implied
</form>
</td></tr></table>"""
% etud,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -591,8 +579,6 @@ def doAnnuleJustif(
#
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Annulation d'une justification pour %(nomprenom)s" % etud,
),
"""<h2>Annulation de justifications pour %(nomprenom)s</h2>""" % etud,
@ -618,13 +604,20 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
% etud
)
H.append(sco_find_etud.form_search_etud(context, REQUEST))
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None, REQUEST=None):
"""Supprime les absences aux dates indiquées
mais ne supprime pas les justificatifs.
def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None):
"""Supprime les absences non justifiées aux dates indiquées
Ne supprime pas les justificatifs éventuels.
Args:
etudid: l'étudiant
dates: liste de dates iso, eg [ "2000-01-15", "2000-01-16" ]
moduleimpl_id: si spécifié, n'affecte que les absences de ce module
Returns:
None
"""
# log('AnnuleAbsencesDatesNoJust: moduleimpl_id=%s' % moduleimpl_id)
if not dates:
@ -640,7 +633,7 @@ def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None, REQUES
matin = 0
else:
raise ValueError("invalid ampm !")
sco_abs.annule_absence(context, etudid, jour, matin, moduleimpl_id, REQUEST)
sco_abs.annule_absence(context, etudid, jour, matin, moduleimpl_id)
return
cnx = ndb.GetDBConnexion()
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
@ -666,7 +659,6 @@ def AnnuleAbsencesDatesNoJust(context, etudid, dates, moduleimpl_id=None, REQUES
else:
date1 = None
logdb(
REQUEST,
cnx,
"AnnuleAbsencesDatesNoJust",
etudid=etudid,
@ -679,7 +671,7 @@ def EtatAbsences(context, REQUEST=None):
"""Etat des absences: choix du groupe"""
# crude portage from 1999 DTML
H = [
html_sco_header.sco_header(context, REQUEST, page_title="Etat des absences"),
html_sco_header.sco_header(page_title="Etat des absences"),
"""<h2>Etat des absences pour un groupe</h2>
<form action="EtatAbsencesGr" method="GET">""",
formChoixSemestreGroupe(context),
@ -695,8 +687,8 @@ def EtatAbsences(context, REQUEST=None):
</td></tr></table>
</form>"""
% (scu.AnneeScolaire(REQUEST), datetime.datetime.now().strftime("%d/%m/%Y")),
html_sco_header.sco_footer(context, REQUEST),
% (scu.AnneeScolaire(), datetime.datetime.now().strftime("%d/%m/%Y")),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -729,12 +721,13 @@ def formChoixSemestreGroupe(context, all=False):
return "\n".join(H)
def CalAbs(context, REQUEST=None): # etud implied
def CalAbs(context, 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=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
anneescolaire = int(scu.AnneeScolaire(REQUEST))
anneescolaire = int(scu.AnneeScolaire(sco_year))
datedebut = str(anneescolaire) + "-08-31"
datefin = str(anneescolaire + 1) + "-07-31"
nbabs = sco_abs.count_abs(etudid=etudid, debut=datedebut, fin=datefin)
@ -760,8 +753,6 @@ def CalAbs(context, REQUEST=None): # etud implied
#
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Calendrier des absences de %(nomprenom)s" % etud,
cssstyles=["css/calabs.css"],
),
@ -799,25 +790,32 @@ def CalAbs(context, REQUEST=None): # etud implied
H.append("selected")
H.append(""">%s</option>""" % y)
H.append("""</select></form>""")
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def ListeAbsEtud(
context,
etudid,
with_evals=True, # indique les evaluations aux dates d'absences
with_evals=True,
format="html",
absjust_only=0, # si vrai, renvoie table absences justifiées
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).
En format json, xml, xls ou pdf: l'un ou l'autre des table, suivant absjust_only.
En format 'text': texte avec liste d'absences (pour mails).
Args:
etudid:
with_evals: indique les evaluations aux dates d'absences
absjust_only: si vrai, renvoie table absences justifiées
sco_year: année scolaire à utiliser. Si non spécifier, utilie l'année en cours. e.g. "2005"
"""
absjust_only = int(absjust_only) # si vrai, table absjust seule (export xls ou pdf)
datedebut = "%s-08-31" % scu.AnneeScolaire(REQUEST)
datedebut = "%s-08-31" % scu.AnneeScolaire(sco_year=sco_year)
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
@ -839,9 +837,7 @@ def ListeAbsEtud(
base_url=base_url_nj,
filename="abs_" + scu.make_filename(etud["nomprenom"]),
caption="Absences non justifiées de %(nomprenom)s" % etud,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
tab_absjust = GenTable(
titles=titles,
@ -852,9 +848,7 @@ def ListeAbsEtud(
base_url=base_url_j,
filename="absjust_" + scu.make_filename(etud["nomprenom"]),
caption="Absences justifiées de %(nomprenom)s" % etud,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
# Formats non HTML et demande d'une seule table:
@ -868,9 +862,7 @@ def ListeAbsEtud(
# Mise en forme HTML:
H = []
H.append(
html_sco_header.sco_header(
context, REQUEST, page_title="Absences de %s" % etud["nomprenom"]
)
html_sco_header.sco_header(page_title="Absences de %s" % etud["nomprenom"])
)
H.append(
"""<h2>Absences de %s (à partir du %s)</h2>"""
@ -888,7 +880,7 @@ def ListeAbsEtud(
H.append(tab_absjust.html())
else:
H.append("""<h3>Pas d'absences justifiées</h3>""")
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
elif format == "text":
T = []

View File

@ -64,9 +64,7 @@ _help_txt = """
def apo_compare_csv_form(context, REQUEST=None):
"""Form: submit 2 CSV files to compare them."""
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Comparaison de fichiers Apogée"
),
html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
"""<h2>Comparaison de fichiers Apogée</h2>
<form id="apo_csv_add" action="apo_compare_csv" method="post" enctype="multipart/form-data">
""",
@ -85,7 +83,7 @@ def apo_compare_csv_form(context, REQUEST=None):
<input type="submit" value="Comparer ces fichiers"/>
</div>
</form>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -96,16 +94,14 @@ def apo_compare_csv(context, A_file, B_file, autodetect=True, REQUEST=None):
B = _load_apo_data(B_file, autodetect=autodetect)
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Comparaison de fichiers Apogée"
),
html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
"<h2>Comparaison de fichiers Apogée</h2>",
_help_txt,
'<div class="apo_compare_csv">',
_apo_compare_csv(context, A, B, REQUEST=None),
"</div>",
"""<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -266,9 +262,7 @@ def apo_table_compare_etud_results(context, A, B, REQUEST=None):
columns_ids=("nip", "nom", "prenom", "elt_code", "type_res", "val_A", "val_B"),
html_class="table_leftalign",
html_with_td_classes=True,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return T

View File

@ -1266,8 +1266,8 @@ def export_csv_to_apogee(
nf += 1
log_filename = "scodoc-" + basename + ".log.txt"
nar_filename = basename + "-nar.xls"
cr_filename = basename + "-decisions.xls"
nar_filename = basename + "-nar" + scu.XLSX_SUFFIX
cr_filename = basename + "-decisions" + scu.XLSX_SUFFIX
logf = StringIO()
logf.write("export_to_apogee du %s\n\n" % time.ctime())

View File

@ -52,6 +52,8 @@ import re
import shutil
import glob
import flask
import app.scodoc.sco_utils as scu
from config import Config
from app.scodoc.notes_log import log
@ -235,7 +237,13 @@ class BaseArchiver(object):
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
return data
elif ext == ".xls":
return sco_excel.sendExcelFile(REQUEST, data, filename)
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":
@ -283,7 +291,7 @@ def do_formsemestre_archive(
if not group_ids:
# tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(context, formsemestre_id)]
group_ids = [sco_groups.get_default_group(formsemestre_id)]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
)
@ -295,7 +303,7 @@ def do_formsemestre_archive(
context, REQUEST, formsemestre_id, format="xls"
)
if data:
PVArchive.store(archive_id, "Tableau_moyennes.xls", data)
PVArchive.store(archive_id, "Tableau_moyennes" + scu.XLSX_SUFFIX, data)
# Tableau recap notes en HTML (pour tous les etudiants, n'utilise pas les groupes)
data, _, _ = make_formsemestre_recapcomplet(
context, REQUEST, formsemestre_id, format="html", disable_etudlink=True
@ -304,8 +312,6 @@ def do_formsemestre_archive(
data = "\n".join(
[
html_sco_header.sco_header(
context,
REQUEST,
page_title="Moyennes archivées le %s" % date,
head_message="Moyennes archivées le %s" % date,
no_side_bar=True,
@ -313,7 +319,7 @@ def do_formsemestre_archive(
'<h2 class="fontorange">Valeurs archivées le %s</h2>' % date,
'<style type="text/css">table.notes_recapcomplet tr { color: rgb(185,70,0); }</style>',
data,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
)
PVArchive.store(archive_id, "Tableau_moyennes.html", data)
@ -329,7 +335,7 @@ def do_formsemestre_archive(
context, formsemestre_id, format="xls", REQUEST=REQUEST, publish=False
)
if data:
PVArchive.store(archive_id, "Decisions_Jury.xls", data)
PVArchive.store(archive_id, "Decisions_Jury" + scu.XLSX_SUFFIX, data)
# Classeur bulletins (PDF)
data, _ = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
context, formsemestre_id, REQUEST, version=bulVersion
@ -372,7 +378,7 @@ def formsemestre_archive(context, REQUEST, formsemestre_id, group_ids=[]):
"""Make and store new archive for this formsemestre.
(all students or only selected groups)
"""
if not sco_permissions_check.can_edit_pv(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_edit_pv(formsemestre_id):
raise AccessDenied(
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
)
@ -380,7 +386,7 @@ def formsemestre_archive(context, REQUEST, formsemestre_id, group_ids=[]):
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
if not group_ids:
# tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(context, formsemestre_id)]
group_ids = [sco_groups.get_default_group(formsemestre_id)]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
)
@ -406,7 +412,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
F = [
"""<p><em>Note: les documents sont aussi affectés par les réglages sur la page "<a href="edit_preferences">Paramétrage</a>" (accessible à l'administrateur du département).</em>
</p>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
descr = [
@ -490,7 +496,7 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
msg = "Nouvelle%20archive%20créée"
# submitted or cancelled:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_list_archives?formsemestre_id=%s&head_message=%s"
% (formsemestre_id, msg)
)
@ -539,7 +545,7 @@ def formsemestre_list_archives(context, REQUEST, formsemestre_id):
H.append("</ul></li>")
H.append("</ul>")
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def formsemestre_get_archived_file(
@ -555,7 +561,7 @@ def formsemestre_delete_archive(
context, REQUEST, formsemestre_id, archive_name, dialog_confirmed=False
):
"""Delete an archive"""
if not sco_permissions_check.can_edit_pv(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_edit_pv(formsemestre_id):
raise AccessDenied(
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
)
@ -568,12 +574,10 @@ def formsemestre_delete_archive(
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression de l'archive du %s ?</h2>
<p>La suppression sera définitive.</p>"""
% PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y %H:%M"),
dest_url="",
REQUEST=REQUEST,
cancel_url=dest_url,
parameters={
"formsemestre_id": formsemestre_id,
@ -582,4 +586,4 @@ def formsemestre_delete_archive(
)
PVArchive.delete_archive(archive_id)
return REQUEST.RESPONSE.redirect(dest_url + "&head_message=Archive%20supprimée")
return flask.redirect(dest_url + "&head_message=Archive%20supprimée")

View File

@ -29,6 +29,7 @@
Il s'agit de fichiers quelconques, généralement utilisés pour conserver
les dossiers d'admission et autres pièces utiles.
"""
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -133,8 +134,6 @@ def etud_upload_file_form(context, REQUEST, etudid):
etud = sco_etud.get_etud_info(filled=1, REQUEST=REQUEST)[0]
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Chargement d'un document associé à %(nomprenom)s" % etud,
),
"""<h2>Chargement d'un document associé à %(nomprenom)s</h2>
@ -164,9 +163,9 @@ def etud_upload_file_form(context, REQUEST, etudid):
cancelbutton="Annuler",
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
else:
@ -176,7 +175,7 @@ def etud_upload_file_form(context, REQUEST, etudid):
_store_etud_file_to_new_archive(
context, REQUEST, etudid, data, filename, description=descr
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
@ -203,7 +202,6 @@ def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed
archive_id = EtudsArchive.get_id_from_name(context, etudid, archive_name)
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression des fichiers ?</h2>
<p>Fichier associé le %s à l'étudiant %s</p>
<p>La suppression sera définitive.</p>"""
@ -212,7 +210,6 @@ def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed
etud["nomprenom"],
),
dest_url="",
REQUEST=REQUEST,
cancel_url=url_for(
"scolar.ficheEtud",
scodoc_dept=g.scodoc_dept,
@ -223,7 +220,7 @@ def etud_delete_archive(context, REQUEST, etudid, archive_name, dialog_confirmed
)
EtudsArchive.delete_archive(archive_id)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for(
"scolar.ficheEtud",
scodoc_dept=g.scodoc_dept,
@ -259,14 +256,16 @@ def etudarchive_generate_excel_sample(context, group_id=None, REQUEST=None):
extra_cols=["fichier_a_charger"],
REQUEST=REQUEST,
)
return sco_excel.sendExcelFile(REQUEST, data, "ImportFichiersEtudiants.xls")
return sco_excel.send_excel_file(
REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX
)
def etudarchive_import_files_form(context, group_id, REQUEST=None):
"""Formulaire pour importation fichiers d'un groupe"""
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Import de fichiers associés aux étudiants"
page_title="Import de fichiers associés aux étudiants"
),
"""<h2 class="formsemestre">Téléchargement de fichier associés aux étudiants</h2>
<p>Les fichiers associés (dossiers d'admission, certificats, ...), de types quelconques (pdf, doc, images)
@ -289,7 +288,7 @@ def etudarchive_import_files_form(context, group_id, REQUEST=None):
"""
% group_id,
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
tf = TrivialFormulator(
REQUEST.URL0,
REQUEST.form,
@ -313,9 +312,13 @@ def etudarchive_import_files_form(context, group_id, REQUEST=None):
return "\n".join(H) + tf[1] + "</li></ol>" + F
elif tf[0] == -1:
# retrouve le semestre à partir du groupe:
g = sco_groups.get_group(context, group_id)
return REQUEST.RESPONSE.redirect(
"formsemestre_status?formsemestre_id=" + g["formsemestre_id"]
group = sco_groups.get_group(context, group_id)
return flask.redirect(
url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=group["formsemestre_id"],
)
)
else:
return etudarchive_import_files(
@ -342,4 +345,4 @@ def etudarchive_import_files(
r = sco_trombino.zip_excel_import_files(
context, xlsfile, zipfile, REQUEST, callback, filename_title, page_title
)
return r + html_sco_header.sco_footer(context, REQUEST)
return r + html_sco_header.sco_footer()

View File

@ -38,7 +38,10 @@ from email.header import Header
from reportlab.lib.colors import Color
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
from flask import g, url_for
from flask import g
from flask import url_for
from flask_login import current_user
from app.scodoc import sco_emails
import app.scodoc.sco_utils as scu
@ -99,8 +102,8 @@ def make_context_dict(context, sem, etud):
C.update(etud)
# copie preferences
# XXX devrait acceder directement à un dict de preferences, à revoir
for name in sco_preferences.get_base_preferences(context).prefs_name:
C[name] = sco_preferences.get_preference(context, name, sem["formsemestre_id"])
for name in sco_preferences.get_base_preferences().prefs_name:
C[name] = sco_preferences.get_preference(name, sem["formsemestre_id"])
# ajoute groupes et group_0, group_1, ...
sco_groups.etud_add_group_infos(context, etud, sem)
@ -133,7 +136,7 @@ def formsemestre_bulletinetud_dict(
if not version in scu.BULLETINS_VERSIONS:
raise ValueError("invalid version code !")
prefs = sco_preferences.SemPreferences(context, formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > toutes notes
I = scu.DictDefault(defaultvalue="")
@ -417,10 +420,10 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
Result: liste de modules de l'UE avec les infos dans chacun (seulement ceux où l'étudiant est inscrit).
"""
bul_show_mod_rangs = sco_preferences.get_preference(
context, "bul_show_mod_rangs", formsemestre_id
"bul_show_mod_rangs", formsemestre_id
)
bul_show_abs_modules = sco_preferences.get_preference(
context, "bul_show_abs_modules", formsemestre_id
"bul_show_abs_modules", formsemestre_id
)
if bul_show_abs_modules:
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -481,9 +484,7 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
'<a class="bull_link" href="moduleimpl_status?moduleimpl_id=%s" title="%s">'
% (modimpl["moduleimpl_id"], mod["mod_descr_txt"])
)
if sco_preferences.get_preference(
context, "bul_show_codemodules", formsemestre_id
):
if sco_preferences.get_preference("bul_show_codemodules", formsemestre_id):
mod["code"] = modimpl["module"]["code"]
mod["code_html"] = link_mod + mod["code"] + "</a>"
else:
@ -502,9 +503,7 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
'<a class="bull_link" href="moduleimpl_status?moduleimpl_id=%s" title="%s">'
% (modimpl["moduleimpl_id"], mod_descr)
)
if sco_preferences.get_preference(
context, "bul_show_codemodules", formsemestre_id
):
if sco_preferences.get_preference("bul_show_codemodules", formsemestre_id):
mod["code_txt"] = modimpl["module"]["code"]
mod["code_html"] = link_mod + mod["code_txt"] + "</a>"
else:
@ -565,12 +564,10 @@ def _ue_mod_bulletin(context, etudid, formsemestre_id, ue_id, modimpls, nt, vers
# Evaluations incomplètes ou futures:
mod["evaluations_incompletes"] = []
if sco_preferences.get_preference(
context, "bul_show_all_evals", formsemestre_id
):
if sco_preferences.get_preference("bul_show_all_evals", formsemestre_id):
complete_eval_ids = set([e["evaluation_id"] for e in evals])
all_evals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": modimpl["moduleimpl_id"]}
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
)
all_evals.reverse() # plus ancienne d'abord
for e in all_evals:
@ -784,7 +781,7 @@ def formsemestre_bulletinetud(
etud = sco_etud.get_etud_info(filled=1, REQUEST=REQUEST)[0]
etudid = etud["etudid"]
except:
return scu.log_unknown_etud(context, REQUEST, format=format)
return scu.log_unknown_etud(REQUEST, format=format)
bulletin = do_formsemestre_bulletinetud(
context,
@ -838,21 +835,18 @@ def formsemestre_bulletinetud(
H.append('<div id="radar_bulletin"></div>')
# --- Pied de page
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "".join(H)
def can_send_bulletin_by_mail(context, formsemestre_id, REQUEST):
def can_send_bulletin_by_mail(context, formsemestre_id):
"""True if current user is allowed to send a bulletin by mail"""
authuser = REQUEST.AUTHENTICATED_USER
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
return (
sco_preferences.get_preference(
context, "bul_mail_allowed_for_all", formsemestre_id
)
or authuser.has_permission(Permission.ScoImplement)
or str(authuser) in sem["responsables"]
sco_preferences.get_preference("bul_mail_allowed_for_all", formsemestre_id)
or current_user.has_permission(Permission.ScoImplement)
or current_user.user_name in sem["responsables"]
)
@ -929,7 +923,7 @@ def do_formsemestre_bulletinetud(
elif format == "pdfmail":
# format pdfmail: envoie le pdf par mail a l'etud, et affiche le html
# check permission
if not can_send_bulletin_by_mail(context, formsemestre_id, REQUEST):
if not can_send_bulletin_by_mail(context, formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if nohtml:
@ -978,18 +972,12 @@ def mail_bulletin(context, formsemestre_id, I, pdfdata, filename, recipient_addr
If bul_mail_list_abs pref is true, put list of absences in mail body (text).
"""
etud = I["etud"]
webmaster = sco_preferences.get_preference(
context, "bul_mail_contact_addr", formsemestre_id
)
webmaster = sco_preferences.get_preference("bul_mail_contact_addr", formsemestre_id)
dept = scu.unescape_html(
sco_preferences.get_preference(context, "DeptName", formsemestre_id)
)
copy_addr = sco_preferences.get_preference(
context, "email_copy_bulletins", formsemestre_id
)
intro_mail = sco_preferences.get_preference(
context, "bul_intro_mail", formsemestre_id
sco_preferences.get_preference("DeptName", formsemestre_id)
)
copy_addr = sco_preferences.get_preference("email_copy_bulletins", formsemestre_id)
intro_mail = sco_preferences.get_preference("bul_intro_mail", formsemestre_id)
if intro_mail:
hea = intro_mail % {
@ -1000,7 +988,7 @@ def mail_bulletin(context, formsemestre_id, I, pdfdata, filename, recipient_addr
else:
hea = ""
if sco_preferences.get_preference(context, "bul_mail_list_abs"):
if sco_preferences.get_preference("bul_mail_list_abs"):
hea += "\n\n" + sco_abs_views.ListeAbsEtud(
context, etud["etudid"], with_evals=False, format="text"
)
@ -1009,9 +997,7 @@ def mail_bulletin(context, formsemestre_id, I, pdfdata, filename, recipient_addr
subj = Header("Relevé de notes de %s" % etud["nomprenom"], scu.SCO_ENCODING)
recipients = [recipient_addr]
msg["Subject"] = subj
msg["From"] = sco_preferences.get_preference(
context, "email_from_addr", formsemestre_id
)
msg["From"] = sco_preferences.get_preference("email_from_addr", formsemestre_id)
msg["To"] = " ,".join(recipients)
if copy_addr:
msg["Bcc"] = copy_addr.strip()
@ -1045,9 +1031,7 @@ def _formsemestre_bulletinetud_header_html(
uid = str(authuser)
H = [
html_sco_header.sco_header(
context,
page_title="Bulletin de %(nomprenom)s" % etud,
REQUEST=REQUEST,
javascripts=[
"js/bulletin.js",
"libjs/d3.v3.min.js",
@ -1128,7 +1112,7 @@ def _formsemestre_bulletinetud_header_html(
},
"enabled": etud["email"]
and can_send_bulletin_by_mail(
context, formsemestre_id, REQUEST
context, formsemestre_id
), # possible slt si on a un mail...
},
{
@ -1143,7 +1127,7 @@ def _formsemestre_bulletinetud_header_html(
},
"enabled": etud["emailperso"]
and can_send_bulletin_by_mail(
context, formsemestre_id, REQUEST
context, formsemestre_id
), # possible slt si on a un mail...
},
{
@ -1184,9 +1168,7 @@ def _formsemestre_bulletinetud_header_html(
"formsemestre_id": formsemestre_id,
"etudid": etudid,
},
"enabled": sco_permissions_check.can_validate_sem(
context, REQUEST, formsemestre_id
),
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
},
{
"title": "Enregistrer note d'une UE externe",
@ -1195,9 +1177,7 @@ def _formsemestre_bulletinetud_header_html(
"formsemestre_id": formsemestre_id,
"etudid": etudid,
},
"enabled": sco_permissions_check.can_validate_sem(
context, REQUEST, formsemestre_id
),
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
},
{
"title": "Entrer décisions jury",
@ -1206,9 +1186,7 @@ def _formsemestre_bulletinetud_header_html(
"formsemestre_id": formsemestre_id,
"etudid": etudid,
},
"enabled": sco_permissions_check.can_validate_sem(
context, REQUEST, formsemestre_id
),
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
},
{
"title": "Editer PV jury",

View File

@ -89,9 +89,7 @@ def bulletin_get_class_name_displayed(context, formsemestre_id):
"""Le nom du générateur utilisé, en clair"""
from app.scodoc import sco_preferences
bul_class_name = sco_preferences.get_preference(
context, "bul_class_name", formsemestre_id
)
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
try:
gen_class = bulletin_get_class(bul_class_name)
return gen_class.description
@ -127,7 +125,7 @@ class BulletinGenerator(object):
self.server_name = server_name
# Store preferences for convenience:
formsemestre_id = self.infos["formsemestre_id"]
self.preferences = sco_preferences.SemPreferences(context, formsemestre_id)
self.preferences = sco_preferences.SemPreferences(formsemestre_id)
self.diagnostic = None # error message if any problem
# Common PDF styles:
# - Pour tous les champs du bulletin sauf les cellules de table:
@ -231,9 +229,7 @@ class BulletinGenerator(object):
margins=self.margins,
server_name=self.server_name,
filigranne=self.filigranne,
preferences=sco_preferences.SemPreferences(
self.context, formsemestre_id
),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
)
document.build(objects)
@ -285,9 +281,7 @@ def make_formsemestre_bulletinetud(
raise ValueError("invalid version code !")
formsemestre_id = infos["formsemestre_id"]
bul_class_name = sco_preferences.get_preference(
context, "bul_class_name", formsemestre_id
)
bul_class_name = sco_preferences.get_preference("bul_class_name", formsemestre_id)
try:
gen_class = bulletin_get_class(bul_class_name)
except:

View File

@ -70,7 +70,7 @@ def make_json_formsemestre_bulletinetud(
if REQUEST:
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
return json.dumps(d, cls=scu.ScoDocJSONEncoder, encoding=scu.SCO_ENCODING)
return json.dumps(d, cls=scu.ScoDocJSONEncoder)
# (fonction séparée: n'utilise pas formsemestre_bulletinetud_dict()
@ -158,8 +158,7 @@ def formsemestre_bulletinetud_published_dict(
mg = scu.fmt_note(nt.get_etud_moy_gen(etudid))
if (
nt.get_moduleimpls_attente()
or sco_preferences.get_preference(context, "bul_show_rangs", formsemestre_id)
== 0
or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
):
# n'affiche pas le rang sur le bulletin s'il y a des
# notes en attente dans ce semestre
@ -254,9 +253,7 @@ def formsemestre_bulletinetud_published_dict(
m["note"][k] = scu.fmt_note(m["note"][k])
u["module"].append(m)
if sco_preferences.get_preference(
context, "bul_show_mod_rangs", formsemestre_id
):
if sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id):
m["rang"] = dict(
value=nt.mod_rangs[modimpl["moduleimpl_id"]][0][etudid]
)
@ -293,10 +290,10 @@ def formsemestre_bulletinetud_published_dict(
# Evaluations incomplètes ou futures:
complete_eval_ids = set([e["evaluation_id"] for e in evals])
if sco_preferences.get_preference(
context, "bul_show_all_evals", formsemestre_id
"bul_show_all_evals", formsemestre_id
):
all_evals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": modimpl["moduleimpl_id"]}
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
)
all_evals.reverse() # plus ancienne d'abord
for e in all_evals:
@ -338,13 +335,13 @@ def formsemestre_bulletinetud_published_dict(
)
# --- Absences
if sco_preferences.get_preference(context, "bul_show_abs", formsemestre_id):
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
d["absences"] = dict(nbabs=nbabs, nbabsjust=nbabsjust)
# --- Decision Jury
if (
sco_preferences.get_preference(context, "bul_show_decision", formsemestre_id)
sco_preferences.get_preference("bul_show_decision", formsemestre_id)
or xml_with_decisions
):
infos, dpv = sco_bulletins.etud_descr_situation_semestre(
@ -353,7 +350,7 @@ def formsemestre_bulletinetud_published_dict(
formsemestre_id,
format="xml",
show_uevalid=sco_preferences.get_preference(
context, "bul_show_uevalid", formsemestre_id
"bul_show_uevalid", formsemestre_id
),
)
d["situation"] = scu.quote_xml_attr(infos["situation"])
@ -377,7 +374,7 @@ def formsemestre_bulletinetud_published_dict(
d["decision_ue"] = []
if decision[
"decisions_ue"
]: # and sco_preferences.get_preference(context, 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys():
ue = sco_edit_ue.do_ue_list(context, {"ue_id": ue_id})[0]
d["decision_ue"].append(

View File

@ -88,7 +88,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
context = self.context
bul_show_abs_modules = sco_preferences.get_preference(
context, "bul_show_abs_modules", formsemestre_id
"bul_show_abs_modules", formsemestre_id
)
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -103,7 +103,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
H = ['<table class="notes_bulletin" style="background-color: %s;">' % bgcolor]
if sco_preferences.get_preference(context, "bul_show_minmax", formsemestre_id):
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
minmax = (
'<span class="bul_minmax" title="[min, max] promo">[%s, %s]</span>'
% (I["moy_min"], I["moy_max"])
@ -129,7 +129,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
continue # saute les modules où on n'est pas inscrit
H.append('<tr class="notes_bulletin_row_mod%s">' % rowstyle)
if sco_preferences.get_preference(
context, "bul_show_minmax_mod", formsemestre_id
"bul_show_minmax_mod", formsemestre_id
):
rang_minmax = '%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>' % (
mod["mod_rang_txt"],
@ -178,7 +178,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
plusminus = minuslink #
if ue["ue_status"]["is_capitalized"]:
if sco_preferences.get_preference(
context, "bul_show_ue_cap_details", formsemestre_id
"bul_show_ue_cap_details", formsemestre_id
):
plusminus = minuslink
hide = ""
@ -209,9 +209,7 @@ class BulletinGeneratorLegacy(sco_bulletins_generator.BulletinGenerator):
)
H.append('<tr class="notes_bulletin_row_ue">')
if sco_preferences.get_preference(
context, "bul_show_minmax", formsemestre_id
):
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
moy_txt = (
'%s <span class="bul_minmax" title="[min, max] UE">[%s, %s]</span>'
% (
@ -448,10 +446,10 @@ def _bulletin_pdf_table_legacy(context, I, version="long"):
P = [] # elems pour gen. pdf
formsemestre_id = I["formsemestre_id"]
bul_show_abs_modules = sco_preferences.get_preference(
context, "bul_show_abs_modules", formsemestre_id
"bul_show_abs_modules", formsemestre_id
)
if sco_preferences.get_preference(context, "bul_show_minmax", formsemestre_id):
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
minmax = ' <font size="8">[%s, %s]</font>' % (I["moy_min"], I["moy_max"])
else:
minmax = ""
@ -473,9 +471,7 @@ def _bulletin_pdf_table_legacy(context, I, version="long"):
if mod["mod_moy_txt"] == "NI":
continue # saute les modules où on n'est pas inscrit
S.modline(ue_type=ue_type)
if sco_preferences.get_preference(
context, "bul_show_minmax_mod", formsemestre_id
):
if sco_preferences.get_preference("bul_show_minmax_mod", formsemestre_id):
rang_minmax = '%s <font size="8">[%s, %s]</font>' % (
mod["mod_rang_txt"],
scu.fmt_note(mod["stats"]["min"]),
@ -516,12 +512,12 @@ def _bulletin_pdf_table_legacy(context, I, version="long"):
ue_descr = "(en cours, non prise en compte)"
S.ueline()
if sco_preferences.get_preference(
context, "bul_show_ue_cap_details", formsemestre_id
"bul_show_ue_cap_details", formsemestre_id
):
list_modules(ue["modules_capitalized"])
ue_type = "cur"
if sco_preferences.get_preference(context, "bul_show_minmax", formsemestre_id):
if sco_preferences.get_preference("bul_show_minmax", formsemestre_id):
moy_txt = '%s <font size="8">[%s, %s]</font>' % (
ue["cur_moy_ue_txt"],
ue["min"],

View File

@ -86,10 +86,10 @@ def pdfassemblebulletins(
return ""
# Paramètres de mise en page
margins = (
sco_preferences.get_preference(context, "left_margin", formsemestre_id),
sco_preferences.get_preference(context, "top_margin", formsemestre_id),
sco_preferences.get_preference(context, "right_margin", formsemestre_id),
sco_preferences.get_preference(context, "bottom_margin", formsemestre_id),
sco_preferences.get_preference("left_margin", formsemestre_id),
sco_preferences.get_preference("top_margin", formsemestre_id),
sco_preferences.get_preference("right_margin", formsemestre_id),
sco_preferences.get_preference("bottom_margin", formsemestre_id),
)
report = StringIO.StringIO() # in-memory document, no disk file
@ -105,7 +105,7 @@ def pdfassemblebulletins(
margins=margins,
pagesbookmarks=pagesbookmarks,
filigranne=filigranne,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
)
document.build(objects)
@ -194,9 +194,7 @@ def get_formsemestre_bulletins_pdf(
bookmarks[i] = scu.suppress_accents(nt.get_sexnom(etudid))
i = i + 1
#
infos = {
"DeptName": sco_preferences.get_preference(context, "DeptName", formsemestre_id)
}
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}
if REQUEST:
server_name = REQUEST.BASE0
else:
@ -248,7 +246,7 @@ def get_etud_bulletins_pdf(context, etudid, REQUEST, version="selectedevals"):
filigrannes[i] = filigranne
bookmarks[i] = sem["session_id"] # eg RT-DUT-FI-S1-2015
i = i + 1
infos = {"DeptName": sco_preferences.get_preference(context, "DeptName")}
infos = {"DeptName": sco_preferences.get_preference("DeptName")}
if REQUEST:
server_name = REQUEST.BASE0
else:

View File

@ -97,7 +97,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
columns_ids=colkeys,
pdf_table_style=pdf_style,
pdf_col_widths=[colWidths[k] for k in colkeys],
preferences=sco_preferences.SemPreferences(self.context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
html_class="notes_bulletin",
html_class_ignore_default=True,
html_with_td_classes=True,
@ -278,7 +278,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
context = self.context
P = [] # elems pour générer table avec gen_table (liste de dicts)
formsemestre_id = I["formsemestre_id"]
prefs = sco_preferences.SemPreferences(context, formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
# Colonnes à afficher:
with_col_abs = prefs["bul_show_abs_modules"]
@ -667,9 +667,7 @@ class BulletinGeneratorStandard(sco_bulletins_generator.BulletinGenerator):
else:
t["_module_colspan"] = 2
if prefs["bul_show_minmax_eval"] or prefs["bul_show_moypromo"]:
etat = sco_evaluations.do_evaluation_etat(
self.context, e["evaluation_id"]
)
etat = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
if prefs["bul_show_minmax_eval"]:
t["min"] = scu.fmt_note(etat["mini"])
t["max"] = scu.fmt_note(etat["maxi"])

View File

@ -72,7 +72,7 @@ class BulletinGeneratorUCAC(sco_bulletins_standard.BulletinGeneratorStandard):
I = self.infos
context = self.context
formsemestre_id = I["formsemestre_id"]
prefs = sco_preferences.SemPreferences(context, formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
P = [] # elems pour générer table avec gen_table (liste de dicts)
@ -192,7 +192,7 @@ class BulletinGeneratorUCAC(sco_bulletins_standard.BulletinGeneratorStandard):
# --- UE capitalisée:
if ue["ue_status"]["is_capitalized"]:
if sco_preferences.get_preference(
context, "bul_show_ue_cap_details", formsemestre_id
"bul_show_ue_cap_details", formsemestre_id
):
nb_modules = len(ue["modules_capitalized"])
hidden = False

View File

@ -151,8 +151,7 @@ def make_xml_formsemestre_bulletinetud(
mg = scu.fmt_note(nt.get_etud_moy_gen(etudid))
if (
nt.get_moduleimpls_attente()
or sco_preferences.get_preference(context, "bul_show_rangs", formsemestre_id)
== 0
or sco_preferences.get_preference("bul_show_rangs", formsemestre_id) == 0
):
# n'affiche pas le rang sur le bulletin s'il y a des
# notes en attente dans ce semestre
@ -255,9 +254,7 @@ def make_xml_formsemestre_bulletinetud(
moy=scu.fmt_note(modstat["moy"]),
)
)
if sco_preferences.get_preference(
context, "bul_show_mod_rangs", formsemestre_id
):
if sco_preferences.get_preference("bul_show_mod_rangs", formsemestre_id):
x_mod.append(
Element(
"rang",
@ -298,10 +295,10 @@ def make_xml_formsemestre_bulletinetud(
# Evaluations incomplètes ou futures:
complete_eval_ids = set([e["evaluation_id"] for e in evals])
if sco_preferences.get_preference(
context, "bul_show_all_evals", formsemestre_id
"bul_show_all_evals", formsemestre_id
):
all_evals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": modimpl["moduleimpl_id"]}
args={"moduleimpl_id": modimpl["moduleimpl_id"]}
)
all_evals.reverse() # plus ancienne d'abord
for e in all_evals:
@ -349,12 +346,12 @@ def make_xml_formsemestre_bulletinetud(
)
# --- Absences
if sco_preferences.get_preference(context, "bul_show_abs", formsemestre_id):
if sco_preferences.get_preference("bul_show_abs", formsemestre_id):
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
doc.append(Element("absences", nbabs=nbabs, nbabsjust=nbabsjust))
# --- Decision Jury
if (
sco_preferences.get_preference(context, "bul_show_decision", formsemestre_id)
sco_preferences.get_preference("bul_show_decision", formsemestre_id)
or xml_with_decisions
):
infos, dpv = sco_bulletins.etud_descr_situation_semestre(
@ -363,7 +360,7 @@ def make_xml_formsemestre_bulletinetud(
formsemestre_id,
format="xml",
show_uevalid=sco_preferences.get_preference(
context, "bul_show_uevalid", formsemestre_id
"bul_show_uevalid", formsemestre_id
),
)
x_situation = Element("situation")
@ -395,7 +392,7 @@ def make_xml_formsemestre_bulletinetud(
if decision[
"decisions_ue"
]: # and sco_preferences.get_preference(context, 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
]: # and sco_preferences.get_preference( 'bul_show_uevalid', formsemestre_id): always publish (car utile pour export Apogee)
for ue_id in decision["decisions_ue"].keys():
ue = sco_edit_ue.do_ue_list(context, {"ue_id": ue_id})[0]
doc.append(

View File

@ -27,7 +27,7 @@
"""Gestion des caches
-écrite pour ScoDoc8, utilise flask_caching et memcached
-écrite pour ScoDoc8, utilise flask_caching et REDIS
ScoDoc est maintenant multiprocessus / mono-thread, avec un cache en mémoire partagé.
"""
@ -55,6 +55,7 @@
#
import time
import traceback
from flask import g
@ -80,16 +81,25 @@ class ScoDocCache:
@classmethod
def get(cls, oid):
"""Returns cached evaluation, or None"""
return CACHE.get(cls._get_key(oid))
try:
return CACHE.get(cls._get_key(oid))
except:
log("XXX CACHE Warning: error in get")
log(traceback.format_exc())
return None
@classmethod
def set(cls, oid, value):
"""Store value"""
key = cls._get_key(oid)
# log(f"CACHE key={key}, timeout={cls.timeout}")
status = CACHE.set(key, value, timeout=cls.timeout)
if not status:
log("Error: cache set failed !")
# log(f"CACHE key={key}, type={type(value)}, timeout={cls.timeout}")
try:
status = CACHE.set(key, value, timeout=cls.timeout)
if not status:
log("Error: cache set failed !")
except:
log("XXX CACHE Warning: error in set !!!")
return status
@classmethod
@ -122,8 +132,7 @@ class EvaluationCache(ScoDocCache):
WHERE s.formsemestre_id = %(formsemestre_id)s and s.formsemestre_id=m.formsemestre_id and e.moduleimpl_id=m.moduleimpl_id;
"""
evaluation_ids = [
x[0]
for x in ndb.SimpleQuery(None, req, {"formsemestre_id": formsemestre_id})
x[0] for x in ndb.SimpleQuery(req, {"formsemestre_id": formsemestre_id})
]
cls.delete_many(evaluation_ids)
@ -132,9 +141,7 @@ class EvaluationCache(ScoDocCache):
"delete all evaluations from cache"
evaluation_ids = [
x[0]
for x in ndb.SimpleQuery(
None, "SELECT evaluation_id FROM notes_evaluation", ""
)
for x in ndb.SimpleQuery("SELECT evaluation_id FROM notes_evaluation", "")
]
cls.delete_many(evaluation_ids)
@ -196,19 +203,30 @@ class NotesTableCache(ScoDocCache):
@classmethod
def get(cls, formsemestre_id, compute=True):
"""Returns NotesTable for this formsemestre
Search in local cache (g.nt_cache) or global app cache (eg REDIS)
If not in cache and compute is True, build it and cache it.
"""
# try local cache (same request)
if not hasattr(g, "nt_cache"):
g.nt_cache = {}
else:
if formsemestre_id in g.nt_cache:
return g.nt_cache[formsemestre_id]
# try REDIS
key = cls._get_key(formsemestre_id)
nt = CACHE.get(key)
if nt or not compute:
return nt
# Recompute asked table:
from app.scodoc import notes_table
t0 = time.time()
nt = notes_table.NotesTable(None, formsemestre_id)
context = None # XXX TO REMOVE #context
nt = notes_table.NotesTable(context, formsemestre_id)
dt = time.time() - t0
log("caching formsemestre_id=%s (%gs)" % (formsemestre_id, dt))
_ = cls.set(formsemestre_id, nt)
g.nt_cache[formsemestre_id] = nt
return nt
@ -227,7 +245,7 @@ def invalidate_formsemestre( # was inval_cache( context, formsemestre_id=None,
formsemestre_ids = [
x[0]
for x in ndb.SimpleQuery(
None, "SELECT formsemestre_id FROM notes_formsemestre", ""
"SELECT formsemestre_id FROM notes_formsemestre", ""
)
]
else:
@ -240,11 +258,15 @@ def invalidate_formsemestre( # was inval_cache( context, formsemestre_id=None,
# Delete cached notes and evaluations
NotesTableCache.delete_many(formsemestre_ids)
if formsemestre_id:
for formsemestre_id in formsemestre_ids:
EvaluationCache.invalidate_sem(formsemestre_id)
for fid in formsemestre_ids:
EvaluationCache.invalidate_sem(fid)
if hasattr(g, "nt_cache") and fid in g.nt_cache:
del g.nt_cache[fid]
else:
# optimization when we invalidate all evaluations:
EvaluationCache.invalidate_all_sems()
if hasattr(g, "nt_cache"):
del g.nt_cache
SemInscriptionsCache.delete_many(formsemestre_ids)
SemBulletinsPDFCache.invalidate_sems(formsemestre_ids)

View File

@ -228,7 +228,7 @@ def do_moduleimpl_moyennes(context, nt, mod):
for e in evals:
e["nb_inscrits"] = e["etat"]["nb_inscrits"]
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
context, e["evaluation_id"]
e["evaluation_id"]
) # toutes, y compris demissions
# restreint aux étudiants encore inscrits à ce module
notes = [

View File

@ -58,7 +58,7 @@ def formsemestre_table_estim_cost(
(dans ce cas, retoucher le tableau excel exporté).
"""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
sco_formsemestre_status.fill_formsemestre(context, sem, REQUEST=REQUEST)
sco_formsemestre_status.fill_formsemestre(sem)
Mlist = sco_moduleimpl.do_moduleimpl_withmodule_list(
context, formsemestre_id=formsemestre_id
)
@ -121,7 +121,7 @@ def formsemestre_table_estim_cost(
),
rows=T,
html_sortable=True,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
html_class="table_leftalign table_listegroupe",
xls_before_table=[
["%(titre)s %(num_sem)s %(modalitestr)s" % sem],

View File

@ -82,7 +82,7 @@ def get_etudids_with_debouche(context, start_year):
start_date = str(start_year) + "-01-01"
# Recupere tous les etudid avec un debouché renseigné et une inscription dans un semestre
# posterieur à la date de depart:
# r = ndb.SimpleDictFetch(context,
# r = ndb.SimpleDictFetch(
# """SELECT DISTINCT i.etudid
# FROM notes_formsemestre_inscription i, admissions adm, notes_formsemestre s
# WHERE adm.debouche is not NULL
@ -92,7 +92,6 @@ def get_etudids_with_debouche(context, start_year):
# {'start_date' : start_date })
r = ndb.SimpleDictFetch(
context,
"""SELECT DISTINCT i.etudid
FROM notes_formsemestre_inscription i, notes_formsemestre s, itemsuivi it
WHERE i.etudid = it.etudid
@ -189,9 +188,7 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
# html_col_width='4em',
html_sortable=True,
html_class="table_leftalign table_listegroupe",
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab
@ -199,11 +196,11 @@ def table_debouche_etudids(context, etudids, keep_numeric=True):
def report_debouche_ask_date(context, REQUEST=None):
"""Formulaire demande date départ"""
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ """<form method="GET">
Date de départ de la recherche: <input type="text" name="start_year" value="" size=10/>
</form>"""
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
@ -213,22 +210,6 @@ def report_debouche_ask_date(context, REQUEST=None):
#
# ----------------------------------------------------------------------------
# OBSOLETE (this field has been copied to itemsuivi)
# def debouche_set(context, object, value, REQUEST=None):
# """Set debouche (field in admission table, may be deprecated ?)
# """
# if not sco_permissions_check.can_edit_suivi(context, REQUEST):
# raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
# adm_id = object
# debouche = value.strip('-_ \t')
# cnx = ndb.GetDBConnexion()
# adms = sco_etud.admission_list(cnx, {'etudid' : etudid})
# if not adms:
# raise ValueError('no admission info for %s !' % etudid)
# adm = adms[0]
# adm['debouche'] = debouche
# admission_edit(cnx, adm)
_itemsuiviEditor = ndb.EditableTable(
"itemsuivi",
@ -269,13 +250,13 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
def itemsuivi_suppress(context, itemsuivi_id, REQUEST=None):
"""Suppression d'un item"""
if not sco_permissions_check.can_edit_suivi(context, REQUEST):
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
cnx = ndb.GetDBConnexion()
item = itemsuivi_get(cnx, itemsuivi_id, ignore_errors=True)
if item:
_itemsuivi_delete(cnx, itemsuivi_id)
logdb(REQUEST, cnx, method="itemsuivi_suppress", etudid=item["etudid"])
logdb(cnx, method="itemsuivi_suppress", etudid=item["etudid"])
log("suppressed itemsuivi %s" % (itemsuivi_id,))
@ -283,13 +264,13 @@ def itemsuivi_create(
context, etudid, item_date=None, situation="", REQUEST=None, format=None
):
"""Creation d'un item"""
if not sco_permissions_check.can_edit_suivi(context, REQUEST):
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
cnx = ndb.GetDBConnexion()
itemsuivi_id = _itemsuivi_create(
cnx, args={"etudid": etudid, "item_date": item_date, "situation": situation}
)
logdb(REQUEST, cnx, method="itemsuivi_create", etudid=etudid)
logdb(cnx, method="itemsuivi_create", etudid=etudid)
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
item = itemsuivi_get(cnx, itemsuivi_id)
if format == "json":
@ -301,7 +282,7 @@ def itemsuivi_set_date(context, itemsuivi_id, item_date, REQUEST=None):
"""set item date
item_date is a string dd/mm/yyyy
"""
if not sco_permissions_check.can_edit_suivi(context, REQUEST):
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
# log('itemsuivi_set_date %s : %s' % (itemsuivi_id, item_date))
cnx = ndb.GetDBConnexion()
@ -312,7 +293,7 @@ def itemsuivi_set_date(context, itemsuivi_id, item_date, REQUEST=None):
def itemsuivi_set_situation(context, object, value, REQUEST=None):
"""set situation"""
if not sco_permissions_check.can_edit_suivi(context, REQUEST):
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
itemsuivi_id = object
situation = value.strip("-_ \t")
@ -338,7 +319,6 @@ def itemsuivi_list_etud(context, etudid, format=None, REQUEST=None):
def itemsuivi_tag_list(context, itemsuivi_id):
"""les noms de tags associés à cet item"""
r = ndb.SimpleDictFetch(
context,
"""SELECT t.title
FROM itemsuivi_tags_assoc a, itemsuivi_tags t
WHERE a.tag_id = t.tag_id
@ -356,7 +336,6 @@ def itemsuivi_tag_search(context, term, REQUEST=None):
data = []
else:
r = ndb.SimpleDictFetch(
context,
"SELECT title FROM itemsuivi_tags WHERE title LIKE %(term)s",
{"term": term + "%"},
)
@ -370,7 +349,7 @@ def itemsuivi_tag_set(context, itemsuivi_id="", taglist=[], REQUEST=None):
a string with tag names separated by commas ("un;deux")
or a list of strings (["un", "deux"])
"""
if not sco_permissions_check.can_edit_suivi(context, REQUEST):
if not sco_permissions_check.can_edit_suivi():
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if not taglist:
taglist = []

View File

@ -29,6 +29,7 @@
"""
from flask import g
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc.gen_tables import GenTable
@ -49,9 +50,7 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
H = []
# News:
# 2020-12-30: abandonne l'icon rss
# rssicon = scu.icontag("rssscodoc_img", title="Flux RSS", border="0")
H.append(sco_news.scolar_news_summary_html(context)) # , rssicon=rssicon))
H.append(sco_news.scolar_news_summary_html(context))
# Avertissement de mise à jour:
H.append(sco_up_to_date.html_up_to_date_box(context))
@ -112,7 +111,7 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
# aucun semestre courant: affiche aide
H.append(
"""<h2 class="listesems">Aucune session en cours !</h2>
<p>Pour ajouter une session, aller dans <a href="Notes">Programmes</a>,
<p>Pour ajouter une session, aller dans <a href="Notes" id="link-programmes">Programmes</a>,
choisissez une formation, puis suivez le lien "<em>UE, modules, semestres</em>".
</p><p>
, en bas de page, suivez le lien
@ -125,7 +124,7 @@ def index_html(context, REQUEST=None, showcodes=0, showsemtable=0):
"""<hr/>
<h2>Semestres de %s</h2>
"""
% sco_preferences.get_preference(context, "DeptName")
% sco_preferences.get_preference("DeptName")
)
H.append(_sem_table_gt(context, sems).html())
H.append("</table>")
@ -144,8 +143,7 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8" spellchec
% scu.NotesURL()
)
#
authuser = REQUEST.AUTHENTICATED_USER
if authuser.has_permission(Permission.ScoEtudInscrit):
if current_user.has_permission(Permission.ScoEtudInscrit):
H.append(
"""<hr>
<h3>Gestion des étudiants</h3>
@ -158,7 +156,7 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8" spellchec
"""
)
#
if authuser.has_permission(Permission.ScoEditApo):
if current_user.has_permission(Permission.ScoEditApo):
H.append(
"""<hr>
<h3>Exports Apogée</h3>
@ -178,11 +176,7 @@ Chercher étape courante: <input name="etape_apo" type="text" size="8" spellchec
"""
)
#
return (
html_sco_header.sco_header(context, REQUEST)
+ "\n".join(H)
+ html_sco_header.sco_footer(context, REQUEST)
)
return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
def _sem_table(context, sems):
@ -250,9 +244,7 @@ def _sem_table_gt(context, sems, showcodes=False):
html_sortable=True,
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
# caption='Maquettes enregistrées',
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab

View File

@ -66,11 +66,9 @@ SCO_DUMP_LOCK = "/tmp/scodump.lock"
def sco_dump_and_send_db(context, REQUEST=None):
"""Dump base de données du département courant et l'envoie anonymisée pour debug"""
H = [
html_sco_header.sco_header(context, REQUEST, page_title="Assistance technique")
]
H = [html_sco_header.sco_header(page_title="Assistance technique")]
# get currect (dept) DB name:
cursor = ndb.SimpleQuery(context, "SELECT current_database()", {})
cursor = ndb.SimpleQuery("SELECT current_database()", {})
db_name = cursor.fetchone()[0]
ano_db_name = "ANO" + db_name
# Lock
@ -125,7 +123,7 @@ def sco_dump_and_send_db(context, REQUEST=None):
fcntl.flock(x, fcntl.LOCK_UN)
log("sco_dump_and_send_db: done.")
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def _duplicate_db(db_name, ano_db_name):
@ -190,7 +188,7 @@ def _send_db(context, REQUEST, ano_db_name):
scu.SCO_DUMP_UP_URL,
files=files,
data={
"dept_name": sco_preferences.get_preference(context, "DeptName"),
"dept_name": sco_preferences.get_preference("DeptName"),
"serial": _get_scodoc_serial(context),
"sco_user": str(REQUEST.AUTHENTICATED_USER),
"sent_by": sco_users.user_info(str(REQUEST.AUTHENTICATED_USER))[

View File

@ -28,6 +28,8 @@
"""Ajout/Modification/Supression formations
(portage from DTML)
"""
import flask
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
@ -52,9 +54,7 @@ def formation_delete(context, formation_id=None, dialog_confirmed=False, REQUEST
F = F[0]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Suppression d'une formation"
),
html_sco_header.sco_header(page_title="Suppression d'une formation"),
"""<h2>Suppression de la formation %(titre)s (%(acronyme)s)</h2>""" % F,
]
@ -75,30 +75,28 @@ def formation_delete(context, formation_id=None, dialog_confirmed=False, REQUEST
else:
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression de la formation %(titre)s (%(acronyme)s) ?</h2>
<p><b>Attention:</b> la suppression d'une formation est <b>irréversible</b> et implique la supression de toutes les UE, matières et modules de la formation !
</p>
"""
% F,
REQUEST=REQUEST,
OK="Supprimer cette formation",
cancel_url=scu.NotesURL(),
parameters={"formation_id": formation_id},
)
else:
do_formation_delete(context, F["formation_id"], REQUEST)
do_formation_delete(context, F["formation_id"])
H.append(
"""<p>OK, formation supprimée.</p>
<p><a class="stdlink" href="%s">continuer</a></p>"""
% scu.NotesURL()
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def do_formation_delete(context, oid, REQUEST):
def do_formation_delete(context, oid):
"""delete a formation (and all its UE, matieres, modules)
XXX delete all ues, will break if there are validations ! USE WITH CARE !
"""
@ -109,17 +107,16 @@ def do_formation_delete(context, oid, REQUEST):
# delete all UE in this formation
ues = sco_edit_ue.do_ue_list(context, {"formation_id": oid})
for ue in ues:
sco_edit_ue.do_ue_delete(context, ue["ue_id"], REQUEST=REQUEST, force=True)
sco_edit_ue.do_ue_delete(context, ue["ue_id"], force=True)
sco_formations._formationEditor.delete(cnx, oid)
# news
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=oid,
text="Suppression de la formation %(acronyme)s" % F,
max_frequency=3,
)
@ -132,9 +129,7 @@ def formation_edit(context, formation_id=None, create=False, REQUEST=None):
"""Edit or create a formation"""
if create:
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Création d'une formation"
),
html_sco_header.sco_header(page_title="Création d'une formation"),
"""<h2>Création d'une formation</h2>
<p class="help">Une "formation" décrit une filière, comme un DUT ou une Licence. La formation se subdivise en unités pédagogiques (UE, matières, modules). Elle peut se diviser en plusieurs semestres (ou sessions), qui seront mis en place séparément.
@ -156,9 +151,7 @@ def formation_edit(context, formation_id=None, create=False, REQUEST=None):
is_locked = sco_formations.formation_has_locked_sems(context, formation_id)
submitlabel = "Modifier les valeurs"
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Modification d'une formation"
),
html_sco_header.sco_header(page_title="Modification d'une formation"),
"""<h2>Modification de la formation %(acronyme)s</h2>""" % initvalues,
]
if is_locked:
@ -228,9 +221,9 @@ def formation_edit(context, formation_id=None, create=False, REQUEST=None):
submitlabel=submitlabel,
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(scu.NotesURL())
return flask.redirect(scu.NotesURL())
else:
# check unicity : constraint UNIQUE(acronyme,titre,version)
if create:
@ -251,17 +244,17 @@ def formation_edit(context, formation_id=None, create=False, REQUEST=None):
"Valeurs incorrectes: il existe déjà une formation avec même titre, acronyme et version."
)
+ tf[1]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
#
if create:
formation_id = do_formation_create(context, tf[2], REQUEST)
formation_id = do_formation_create(context, tf[2])
else:
do_formation_edit(context, tf[2])
return REQUEST.RESPONSE.redirect("ue_list?formation_id=%s" % formation_id)
return flask.redirect("ue_list?formation_id=%s" % formation_id)
def do_formation_create(context, args, REQUEST):
def do_formation_create(context, args):
"create a formation"
cnx = ndb.GetDBConnexion()
# check unique acronyme/titre/version
@ -279,10 +272,9 @@ def do_formation_create(context, args, REQUEST):
r = sco_formations._formationEditor.create(cnx, args)
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
text="Création de la formation %(titre)s (%(acronyme)s)" % args,
max_frequency=3,
)
return r
@ -348,7 +340,7 @@ def module_move(context, module_id, after=0, REQUEST=None, redirect=1):
# redirect to ue_list page:
if redirect:
return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + formation_id)
return flask.redirect("ue_list?formation_id=" + formation_id)
def ue_move(context, ue_id, after=0, REQUEST=None, redirect=1):
@ -379,4 +371,4 @@ def ue_move(context, ue_id, after=0, REQUEST=None, redirect=1):
sco_edit_ue._ueEditor.edit(cnx, neigh)
# redirect to ue_list page
if redirect:
return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + o["formation_id"])
return flask.redirect("ue_list?formation_id=" + o["formation_id"])

View File

@ -28,6 +28,8 @@
"""Ajout/Modification/Supression matieres
(portage from DTML)
"""
import flask
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
@ -68,7 +70,7 @@ def do_matiere_edit(context, *args, **kw):
sco_edit_formation.invalidate_sems_in_formation(formation_id)
def do_matiere_create(context, args, REQUEST):
def do_matiere_create(context, args):
"create a matiere"
from app.scodoc import sco_edit_ue
from app.scodoc import sco_formations
@ -85,11 +87,10 @@ def do_matiere_create(context, args, REQUEST):
context, args={"formation_id": ue["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=ue["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
return r
@ -100,9 +101,7 @@ def matiere_create(context, ue_id=None, REQUEST=None):
UE = sco_edit_ue.do_ue_list(context, args={"ue_id": ue_id})[0]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Création d'une matière"
),
html_sco_header.sco_header(page_title="Création d'une matière"),
"""<h2>Création d'une matière dans l'UE %(titre)s (%(acronyme)s)</h2>""" % UE,
"""<p class="help">Les matières sont des groupes de modules dans une UE
d'une formation donnée. Les matières servent surtout pour la
@ -140,9 +139,9 @@ associé.
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + UE["formation_id"]
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# check unicity
mats = do_matiere_list(context, args={"ue_id": ue_id, "titre": tf[2]["titre"]})
@ -151,13 +150,13 @@ associé.
"\n".join(H)
+ tf_error_message("Titre de matière déjà existant dans cette UE")
+ tf[1]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
_ = do_matiere_create(context, tf[2], REQUEST)
return REQUEST.RESPONSE.redirect(dest_url)
_ = do_matiere_create(context, tf[2])
return flask.redirect(dest_url)
def do_matiere_delete(context, oid, REQUEST):
def do_matiere_delete(context, oid):
"delete matiere and attached modules"
from app.scodoc import sco_formations
from app.scodoc import sco_edit_ue
@ -178,7 +177,7 @@ def do_matiere_delete(context, oid, REQUEST):
# delete all modules in this matiere
mods = sco_edit_module.do_module_list(context, {"matiere_id": oid})
for mod in mods:
sco_edit_module.do_module_delete(context, mod["module_id"], REQUEST)
sco_edit_module.do_module_delete(context, mod["module_id"])
_matiereEditor.delete(cnx, oid)
# news
@ -186,11 +185,10 @@ def do_matiere_delete(context, oid, REQUEST):
context, args={"formation_id": ue["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=ue["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
@ -201,9 +199,7 @@ def matiere_delete(context, matiere_id=None, REQUEST=None):
M = do_matiere_list(context, args={"matiere_id": matiere_id})[0]
UE = sco_edit_ue.do_ue_list(context, args={"ue_id": M["ue_id"]})[0]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Suppression d'une matière"
),
html_sco_header.sco_header(page_title="Suppression d'une matière"),
"<h2>Suppression de la matière %(titre)s" % M,
" dans l'UE (%(acronyme)s))</h2>" % UE,
]
@ -217,12 +213,12 @@ def matiere_delete(context, matiere_id=None, REQUEST=None):
cancelbutton="Annuler",
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
do_matiere_delete(context, matiere_id, REQUEST)
return REQUEST.RESPONSE.redirect(dest_url)
do_matiere_delete(context, matiere_id)
return flask.redirect(dest_url)
def matiere_edit(context, matiere_id=None, REQUEST=None):
@ -246,9 +242,7 @@ def matiere_edit(context, matiere_id=None, REQUEST=None):
ue_names = ["%(acronyme)s (%(titre)s)" % u for u in ues]
ue_ids = [u["ue_id"] for u in ues]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Modification d'une matière"
),
html_sco_header.sco_header(page_title="Modification d'une matière"),
"""<h2>Modification de la matière %(titre)s""" % F,
"""(formation %(acronyme)s, version %(version)s)</h2>""" % Fo,
]
@ -292,11 +286,9 @@ associé.
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + U["formation_id"]
if tf[0] == 0:
return (
"\n".join(H) + tf[1] + help + html_sco_header.sco_footer(context, REQUEST)
)
return "\n".join(H) + tf[1] + help + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# check unicity
mats = do_matiere_list(
@ -307,21 +299,20 @@ associé.
"\n".join(H)
+ tf_error_message("Titre de matière déjà existant dans cette UE")
+ tf[1]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
# changement d'UE ?
if tf[2]["ue_id"] != F["ue_id"]:
log("attaching mat %s to new UE %s" % (matiere_id, tf[2]["ue_id"]))
ndb.SimpleQuery(
context,
"UPDATE notes_modules SET ue_id = %(ue_id)s WHERE matiere_id=%(matiere_id)s",
{"ue_id": tf[2]["ue_id"], "matiere_id": matiere_id},
)
do_matiere_edit(context, tf[2])
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
def matiere_is_locked(context, matiere_id):
@ -329,7 +320,6 @@ def matiere_is_locked(context, matiere_id):
(contains modules used in a locked formsemestre)
"""
r = ndb.SimpleDictFetch(
context,
"""SELECT ma.* from notes_matieres ma, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ma.matiere_id = mod.matiere_id AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id
AND ma.matiere_id = %(matiere_id)s AND sem.etat = 0

View File

@ -28,6 +28,9 @@
"""Ajout/Modification/Suppression modules
(portage from DTML)
"""
import flask
from flask import url_for, g
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
@ -96,7 +99,7 @@ def do_module_list(context, *args, **kw):
return _moduleEditor.list(cnx, *args, **kw)
def do_module_create(context, args, REQUEST):
def do_module_create(context, args):
"create a module"
# create
from app.scodoc import sco_formations
@ -109,11 +112,10 @@ def do_module_create(context, args, REQUEST):
context, args={"formation_id": args["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=args["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
return r
@ -133,7 +135,7 @@ def module_create(context, matiere_id=None, REQUEST=None):
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
semestres_indices = list(range(1, parcours.NB_SEM + 1))
H = [
html_sco_header.sco_header(context, REQUEST, page_title="Création d'un module"),
html_sco_header.sco_header(page_title="Création d'un module"),
"""<h2>Création d'un module dans la matière %(titre)s""" % M,
""" (UE %(acronyme)s)</h2>""" % UE,
_MODULE_HELP,
@ -237,15 +239,19 @@ def module_create(context, matiere_id=None, REQUEST=None):
submitlabel="Créer ce module",
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
else:
do_module_create(context, tf[2], REQUEST)
return REQUEST.RESPONSE.redirect(
scu.NotesURL() + "/ue_list?formation_id=" + UE["formation_id"]
do_module_create(context, tf[2])
return flask.redirect(
url_for(
"notes.ue_list",
scodoc_dept=g.scodoc_dept,
formation_id=UE["formation_id"],
)
)
def do_module_delete(context, oid, REQUEST):
def do_module_delete(context, oid):
"delete module"
from app.scodoc import sco_formations
@ -257,12 +263,10 @@ def do_module_delete(context, oid, REQUEST):
mods = sco_moduleimpl.do_moduleimpl_list(context, module_id=oid)
if mods:
err_page = scu.confirm_dialog(
context,
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"]},
REQUEST=REQUEST,
)
raise ScoGenError(err_page)
# delete
@ -274,11 +278,10 @@ def do_module_delete(context, oid, REQUEST):
context, args={"formation_id": mod["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=mod["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
@ -291,9 +294,7 @@ def module_delete(context, module_id=None, REQUEST=None):
raise ScoValueError("Module inexistant !")
Mod = Mods[0]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Suppression d'un module"
),
html_sco_header.sco_header(page_title="Suppression d'un module"),
"""<h2>Suppression du module %(titre)s (%(code)s)</h2>""" % Mod,
]
@ -307,12 +308,12 @@ def module_delete(context, module_id=None, REQUEST=None):
cancelbutton="Annuler",
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
do_module_delete(context, module_id, REQUEST)
return REQUEST.RESPONSE.redirect(dest_url)
do_module_delete(context, module_id)
return flask.redirect(dest_url)
def do_module_edit(context, val):
@ -359,7 +360,6 @@ def module_edit(context, module_id=None, REQUEST=None):
)[0]
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
M = ndb.SimpleDictFetch(
context,
"SELECT ue.acronyme, mat.* FROM notes_matieres mat, notes_ue ue WHERE mat.ue_id = ue.ue_id AND ue.formation_id = %(formation_id)s ORDER BY ue.numero, mat.numero",
{"formation_id": Mod["formation_id"]},
)
@ -373,8 +373,6 @@ def module_edit(context, module_id=None, REQUEST=None):
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Modification du module %(titre)s" % Mod,
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
javascripts=[
@ -505,16 +503,16 @@ def module_edit(context, module_id=None, REQUEST=None):
)
if tf[0] == 0:
return "\n".join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# l'UE peut changer
tf[2]["ue_id"], tf[2]["matiere_id"] = tf[2]["ue_matiere_id"].split("!")
# Check unicité code module dans la formation
do_module_edit(context, tf[2])
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
# Edition en ligne du code Apogee
@ -544,9 +542,7 @@ def module_list(context, formation_id, REQUEST=None):
raise ScoValueError("invalid formation !")
F = sco_formations.formation_list(context, args={"formation_id": formation_id})[0]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Liste des modules de %(titre)s" % F
),
html_sco_header.sco_header(page_title="Liste des modules de %(titre)s" % F),
"""<h2>Listes des modules dans la formation %(titre)s (%(acronyme)s)</h2>"""
% F,
'<ul class="notes_module_list">',
@ -562,7 +558,7 @@ def module_list(context, formation_id, REQUEST=None):
)
H.append("</li>")
H.append("</ul>")
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -571,7 +567,6 @@ def module_is_locked(context, module_id):
(used in a locked formsemestre)
"""
r = ndb.SimpleDictFetch(
context,
"""SELECT mi.* from notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id
AND mi.module_id = %(module_id)s AND sem.etat = 0
@ -606,7 +601,7 @@ def formation_add_malus_modules(context, formation_id, titre=None, REQUEST=None)
ue_add_malus_module(context, ue["ue_id"], titre=titre, REQUEST=REQUEST)
if REQUEST:
return REQUEST.RESPONSE.redirect("ue_list?formation_id=" + formation_id)
return flask.redirect("ue_list?formation_id=" + formation_id)
def ue_add_malus_module(context, ue_id, titre=None, code=None, REQUEST=None):
@ -635,7 +630,7 @@ def ue_add_malus_module(context, ue_id, titre=None, code=None, REQUEST=None):
Matlist = sco_edit_matiere.do_matiere_list(context, args={"ue_id": ue_id})
numero = max([mat["numero"] for mat in Matlist]) + 10
matiere_id = sco_edit_matiere.do_matiere_create(
context, {"ue_id": ue_id, "titre": "Malus", "numero": numero}, REQUEST
context, {"ue_id": ue_id, "titre": "Malus", "numero": numero}
)
module_id = do_module_create(
@ -650,7 +645,6 @@ def ue_add_malus_module(context, ue_id, titre=None, code=None, REQUEST=None):
"semestre_id": semestre_id,
"module_type": scu.MODULE_MALUS,
},
REQUEST,
)
return module_id

View File

@ -28,6 +28,10 @@
"""Ajout/Modification/Suppression UE
"""
import flask
from flask import g, url_for
from flask_login import current_user
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
from app.scodoc.notes_log import log
@ -83,7 +87,7 @@ def do_ue_list(context, *args, **kw):
return _ueEditor.list(cnx, *args, **kw)
def do_ue_create(context, args, REQUEST):
def do_ue_create(context, args):
"create an ue"
from app.scodoc import sco_formations
@ -102,11 +106,10 @@ def do_ue_create(context, args, REQUEST):
context, args={"formation_id": args["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=args["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
return r
@ -132,11 +135,9 @@ def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=F
)
if validations and not delete_validations and not force:
return scu.confirm_dialog(
context,
"<p>%d étudiants ont validé l'UE %s (%s)</p><p>Si vous supprimez cette UE, ces validations vont être supprimées !</p>"
% (len(validations), ue["acronyme"], ue["titre"]),
dest_url="",
REQUEST=REQUEST,
target_variable="delete_validations",
cancel_url="ue_list?formation_id=%s" % ue["formation_id"],
parameters={"ue_id": ue_id, "dialog_confirmed": 1},
@ -144,7 +145,6 @@ def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=F
if delete_validations:
log("deleting all validations of UE %s" % ue_id)
ndb.SimpleQuery(
context,
"DELETE FROM scolar_formsemestre_validation WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id},
)
@ -152,16 +152,13 @@ def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=F
# delete all matiere in this UE
mats = sco_edit_matiere.do_matiere_list(context, {"ue_id": ue_id})
for mat in mats:
sco_edit_matiere.do_matiere_delete(context, mat["matiere_id"], REQUEST)
sco_edit_matiere.do_matiere_delete(context, mat["matiere_id"])
# delete uecoef and events
ndb.SimpleQuery(
context,
"DELETE FROM notes_formsemestre_uecoef WHERE ue_id=%(ue_id)s",
{"ue_id": ue_id},
)
ndb.SimpleQuery(
context, "DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id}
)
ndb.SimpleQuery("DELETE FROM scolar_events WHERE ue_id=%(ue_id)s", {"ue_id": ue_id})
cnx = ndb.GetDBConnexion()
_ueEditor.delete(cnx, ue_id)
# > UE delete + supr. validations associées etudiants (cas compliqué, mais rarement utilisé: acceptable de tout invalider ?):
@ -171,16 +168,19 @@ def do_ue_delete(context, ue_id, delete_validations=False, REQUEST=None, force=F
context, args={"formation_id": ue["formation_id"]}
)[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=ue["formation_id"],
text="Modification de la formation %(acronyme)s" % F,
max_frequency=3,
)
#
if not force:
return REQUEST.RESPONSE.redirect(
scu.NotesURL() + "/ue_list?formation_id=" + str(ue["formation_id"])
return flask.redirect(
url_for(
"notes.ue_list",
scodoc_dept=g.scodoc_dept,
formation_id=ue["formation_id"],
)
)
else:
return None
@ -219,9 +219,7 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
parcours = sco_codes_parcours.get_parcours_from_code(Fo["type_parcours"])
H = [
html_sco_header.sco_header(
context, REQUEST, page_title=title, javascripts=["js/edit_ue.js"]
),
html_sco_header.sco_header(page_title=title, javascripts=["js/edit_ue.js"]),
"<h2>" + title,
" (formation %(acronyme)s, version %(version)s)</h2>" % Fo,
"""
@ -334,7 +332,7 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
if tf[0] == 0:
X = """<div id="ue_list_code"></div>
"""
return "\n".join(H) + tf[1] + X + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + tf[1] + X + html_sco_header.sco_footer()
else:
if create:
if not tf[2]["ue_code"]:
@ -347,12 +345,11 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
context, formation_id, int(tf[2]["semestre_id"] or 0)
)
ue_id = do_ue_create(context, tf[2], REQUEST)
ue_id = do_ue_create(context, tf[2])
if parcours.UE_IS_MODULE or tf[2]["create_matiere"]:
matiere_id = sco_edit_matiere.do_matiere_create(
context,
{"ue_id": ue_id, "titre": tf[2]["titre"], "numero": 1},
REQUEST,
)
if parcours.UE_IS_MODULE:
# dans ce mode, crée un (unique) module dans l'UE:
@ -367,12 +364,13 @@ def ue_edit(context, ue_id=None, create=False, formation_id=None, REQUEST=None):
"formation_id": formation_id,
"semestre_id": tf[2]["semestre_id"],
},
REQUEST,
)
else:
do_ue_edit(context, tf[2])
return REQUEST.RESPONSE.redirect(
scu.NotesURL() + "/ue_list?formation_id=" + formation_id
return flask.redirect(
url_for(
"notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=formation_id
)
)
@ -419,10 +417,8 @@ def ue_delete(
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"<h2>Suppression de l'UE %(titre)s (%(acronyme)s))</h2>" % ue,
dest_url="",
REQUEST=REQUEST,
parameters={"ue_id": ue_id},
cancel_url="ue_list?formation_id=%s" % ue["formation_id"],
)
@ -439,8 +435,6 @@ def ue_list(context, formation_id=None, msg="", REQUEST=None):
from app.scodoc import sco_formations
from app.scodoc import sco_formsemestre_validation
authuser = REQUEST.AUTHENTICATED_USER
F = sco_formations.formation_list(context, args={"formation_id": formation_id})
if not F:
raise ScoValueError("invalid formation_id")
@ -454,7 +448,7 @@ def ue_list(context, formation_id=None, msg="", REQUEST=None):
ue_list.sort(key=lambda u: (u["semestre_id"], u["numero"]))
has_duplicate_ue_codes = len(set([ue["ue_code"] for ue in ue_list])) != len(ue_list)
perm_change = authuser.has_permission(Permission.ScoChangeFormation)
perm_change = current_user.has_permission(Permission.ScoChangeFormation)
# editable = (not locked) and perm_change
# On autorise maintanant la modification des formations qui ont des semestres verrouillés,
# sauf si cela affect les notes passées (verrouillées):
@ -462,14 +456,14 @@ def ue_list(context, formation_id=None, msg="", REQUEST=None):
# - pas de changement des codes d'UE utilisés dans des semestres verrouillés
editable = perm_change
tag_editable = (
authuser.has_permission(Permission.ScoEditFormationTags) or perm_change
current_user.has_permission(Permission.ScoEditFormationTags) or perm_change
)
if locked:
lockicon = scu.icontag("lock32_img", title="verrouillé")
else:
lockicon = ""
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags(context, REQUEST)
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags()
delete_icon = scu.icontag(
"delete_small_img", title="Supprimer (module inutilisé)", alt="supprimer"
)
@ -478,8 +472,6 @@ def ue_list(context, formation_id=None, msg="", REQUEST=None):
)
H = [
html_sco_header.sco_header(
context,
REQUEST,
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
javascripts=[
"libjs/jinplace-1.2.1.min.js",
@ -814,7 +806,7 @@ Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module
H.append("</li>")
H.append("</ul>")
if authuser.has_permission(Permission.ScoImplement):
if current_user.has_permission(Permission.ScoImplement):
H.append(
"""<ul>
<li><a class="stdlink" href="formsemestre_createwithmodules?formation_id=%(formation_id)s&semestre_id=1">Mettre en place un nouveau semestre de formation %(acronyme)s</a>
@ -828,7 +820,7 @@ Si vous souhaitez modifier cette formation (par exemple pour y ajouter un module
warn, _ = sco_formsemestre_validation.check_formation_ues(context, formation_id)
H.append(warn)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "".join(H)
@ -949,7 +941,6 @@ def ue_is_locked(context, ue_id):
(contains modules used in a locked formsemestre)
"""
r = ndb.SimpleDictFetch(
context,
"""SELECT ue.* FROM notes_ue ue, notes_modules mod, notes_formsemestre sem, notes_moduleimpl mi
WHERE ue.ue_id = mod.ue_id
AND mi.module_id = mod.module_id AND mi.formsemestre_id = sem.formsemestre_id
@ -1042,9 +1033,7 @@ def formation_table_recap(context, formation_id, format="html", REQUEST=None):
page_title=title,
html_title="<h2>" + title + "</h2>",
pdf_title=title,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab.make_page(context, format=format, REQUEST=REQUEST)

View File

@ -55,7 +55,7 @@ def formsemestre_get_ics_url(context, sem):
https://example.fr/agenda/{sem[etapes][0]}
"""
ics_url_tmpl = sco_preferences.get_preference(
context, "edt_sem_ics_url", sem["formsemestre_id"]
"edt_sem_ics_url", sem["formsemestre_id"]
)
if not ics_url_tmpl:
return None
@ -102,7 +102,7 @@ def get_edt_transcodage_groups(context, formsemestre_id):
edt2sco = {}
sco2edt = {}
msg = "" # message erreur, '' si ok
txt = sco_preferences.get_preference(context, "edt_groups2scodoc", formsemestre_id)
txt = sco_preferences.get_preference("edt_groups2scodoc", formsemestre_id)
if not txt:
return edt2sco, sco2edt, msg
@ -166,8 +166,6 @@ def experimental_calendar(context, group_id=None, formsemestre_id=None, REQUEST=
return "\n".join(
[
html_sco_header.sco_header(
context,
REQUEST,
javascripts=[
"libjs/purl.js",
"libjs/moment.min.js",
@ -198,7 +196,7 @@ def experimental_calendar(context, group_id=None, formsemestre_id=None, REQUEST=
"""</form><div id="loading">loading...</div>
<div id="calendar"></div>
""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
"""<script>
$(document).ready(function() {

View File

@ -387,7 +387,7 @@ apo_csv_store(context, csv_data, annee_scolaire, sem_id)
groups_infos = sco_groups_view.DisplayedGroupsInfos(context, [sco_groups.get_default_group(context, formsemestre_id)], formsemestre_id=formsemestre_id, REQUEST=REQUEST)
groups_infos = sco_groups_view.DisplayedGroupsInfos(context, [sco_groups.get_default_group(formsemestre_id)], formsemestre_id=formsemestre_id)
nt = sco_cache.NotesTableCache.get( formsemestre_id)

View File

@ -28,12 +28,10 @@
"""ScoDoc : formulaires gestion maquettes Apogee / export resultats
"""
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
from io import StringIO
from zipfile import ZipFile
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -72,7 +70,7 @@ def apo_semset_maq_status(
if not semset_id:
raise ValueError("invalid null semset_id")
semset = sco_semset.SemSet(context, semset_id=semset_id)
semset.fill_formsemestres(REQUEST)
semset.fill_formsemestres()
# autorise export meme si etudiants Apo manquants:
allow_missing_apo = int(allow_missing_apo)
# autorise export meme s'il manque des décisions de jury:
@ -85,9 +83,7 @@ def apo_semset_maq_status(
block_export_res_modules = int(block_export_res_modules)
block_export_res_sdj = int(block_export_res_sdj)
prefs = sco_preferences.SemPreferences(
context,
)
prefs = sco_preferences.SemPreferences()
tab_archives = table_apo_csv_list(context, semset, REQUEST=REQUEST)
@ -110,8 +106,6 @@ def apo_semset_maq_status(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Export Apogée",
javascripts=["js/apo_semset_maq_status.js"],
),
@ -435,7 +429,7 @@ def apo_semset_maq_status(
"""
% (APO_INPUT_ENCODING,)
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -489,9 +483,7 @@ def table_apo_csv_list(context, semset, REQUEST=None):
html_sortable=True,
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
# caption='Maquettes enregistrées',
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab
@ -577,8 +569,6 @@ def _view_etuds_page(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=title,
init_qtip=True,
javascripts=["js/etud_info.js"],
@ -600,9 +590,7 @@ def _view_etuds_page(
html_sortable=True,
html_class="table_leftalign",
filename="students_apo",
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
if format != "html":
return tab.make_page(context, format=format, REQUEST=REQUEST)
@ -614,7 +602,7 @@ def _view_etuds_page(
% semset_id
)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def view_apo_csv_store(
@ -653,7 +641,7 @@ def view_apo_csv_store(
context, data, semset["annee_scolaire"], semset["sem_id"]
)
return REQUEST.RESPONSE.redirect("apo_semset_maq_status?semset_id=" + semset_id)
return flask.redirect("apo_semset_maq_status?semset_id=" + semset_id)
def view_apo_csv_download_and_store(context, etape_apo="", semset_id="", REQUEST=None):
@ -683,12 +671,10 @@ def view_apo_csv_delete(
dest_url = "apo_semset_maq_status?semset_id=" + semset_id
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Confirmer la suppression du fichier étape <tt>%s</tt>?</h2>
<p>La suppression sera définitive.</p>"""
% (etape_apo,),
dest_url="",
REQUEST=REQUEST,
cancel_url=dest_url,
parameters={"semset_id": semset_id, "etape_apo": etape_apo},
)
@ -697,7 +683,7 @@ def view_apo_csv_delete(
context, etape_apo, semset["annee_scolaire"], semset["sem_id"]
)
sco_etape_apogee.apo_csv_delete(context, info["archive_id"])
return REQUEST.RESPONSE.redirect(dest_url + "&head_message=Archive%20supprimée")
return flask.redirect(dest_url + "&head_message=Archive%20supprimée")
def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=None):
@ -728,8 +714,6 @@ def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=Non
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Maquette Apogée enregistrée pour %s" % etape_apo,
init_qtip=True,
javascripts=["js/etud_info.js"],
@ -752,11 +736,7 @@ def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=Non
# Liste des étudiants (sans les résultats pour le moment): TODO
etuds = apo_data.etuds
if not etuds:
return (
"\n".join(H)
+ "<p>Aucun étudiant</p>"
+ html_sco_header.sco_footer(context, REQUEST)
)
return "\n".join(H) + "<p>Aucun étudiant</p>" + html_sco_header.sco_footer()
# Ajout infos sur ScoDoc vs Apogee
for e in etuds:
@ -788,9 +768,7 @@ def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=Non
base_url="%s?etape_apo=%s&semset_id=%s" % (REQUEST.URL0, etape_apo, semset_id),
filename="students_" + etape_apo,
caption="Etudiants Apogée en " + etape_apo,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
if format != "html":
@ -803,7 +781,7 @@ def view_apo_csv(context, etape_apo="", semset_id="", format="html", REQUEST=Non
"""<div><a class="stdlink" href="apo_semset_maq_status?semset_id=%s">Retour</a>
</div>"""
% semset_id,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -826,9 +804,7 @@ def apo_csv_export_results(
# nota: on peut éventuellement exporter même si tout n'est pas ok
# mais le lien via le tableau de bord n'est pas actif
# Les fichiers résultats ne sont pas stockés: pas besoin de permission particulière
prefs = sco_preferences.SemPreferences(
context,
)
prefs = sco_preferences.SemPreferences()
export_res_etape = prefs["export_res_etape"] and not int(block_export_res_etape)
export_res_sem = prefs["export_res_sem"] and not int(block_export_res_sem)
export_res_ues = prefs["export_res_ues"] and not int(block_export_res_ues)
@ -869,7 +845,7 @@ def apo_csv_export_results(
)
basename = (
sco_preferences.get_preference(context, "DeptName")
sco_preferences.get_preference("DeptName")
+ str(annee_scolaire)
+ "-%s-" % periode
+ "-".join(etapes_apo)

View File

@ -351,7 +351,6 @@ def _check_duplicate_code(cnx, args, code_name, context, edit=True, REQUEST=None
parameters = {}
if context:
err_page = scu.confirm_dialog(
context,
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])
@ -360,7 +359,6 @@ def _check_duplicate_code(cnx, args, code_name, context, edit=True, REQUEST=None
OK=OK,
dest_url=dest_url,
parameters=parameters,
REQUEST=REQUEST,
)
else:
err_page = """<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name
@ -368,6 +366,11 @@ def _check_duplicate_code(cnx, args, code_name, context, edit=True, REQUEST=None
raise ScoGenError(err_page)
def _check_civilite(args):
civilite = args.get("civilite", "X") or "X"
args["civilite"] = input_civilite(civilite) # TODO: A faire valider
def identite_edit(cnx, args, context=None, REQUEST=None):
"""Modifie l'identite d'un étudiant.
Si context et notification et difference, envoie message notification.
@ -377,9 +380,7 @@ def identite_edit(cnx, args, context=None, REQUEST=None):
notify_to = None
if context:
try:
notify_to = sco_preferences.get_preference(
context, "notify_etud_changes_to"
)
notify_to = sco_preferences.get_preference("notify_etud_changes_to")
except:
pass
@ -407,6 +408,7 @@ def identite_create(cnx, args, context=None, REQUEST=None):
"check unique etudid, then create"
_check_duplicate_code(cnx, args, "code_nip", context, edit=False, REQUEST=REQUEST)
_check_duplicate_code(cnx, args, "code_ine", context, edit=False, REQUEST=REQUEST)
_check_civilite(args)
if "etudid" in args:
etudid = args["etudid"]
@ -446,7 +448,7 @@ def notify_etud_change(context, email_addr, etud, before, after, subject):
msg = MIMEMultipart()
subj = Header("[ScoDoc] " + subject, SCO_ENCODING)
msg["Subject"] = subj
msg["From"] = sco_preferences.get_preference(context, "email_from_addr")
msg["From"] = sco_preferences.get_preference("email_from_addr")
msg["To"] = email_addr
mime_txt = MIMEText(txt, "plain", SCO_ENCODING)
msg.attach(mime_txt)
@ -492,9 +494,7 @@ def adresse_edit(cnx, args, context=None):
notify_to = None
if context:
try:
notify_to = sco_preferences.get_preference(
context, "notify_etud_changes_to"
)
notify_to = sco_preferences.get_preference("notify_etud_changes_to")
except:
pass
if notify_to:
@ -696,7 +696,6 @@ def create_etud(context, cnx, args={}, REQUEST=None):
)
# log
logdb(
REQUEST,
cnx,
method="etudident_edit_form",
etudid=etudid,
@ -706,8 +705,6 @@ def create_etud(context, cnx, args={}, REQUEST=None):
fill_etuds_info([etud])
etud["url"] = url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_INSCR,
object=None, # pas d'object pour ne montrer qu'un etudiant
text='Nouvel étudiant <a href="%(url)s">%(nomprenom)s</a>' % etud,
@ -743,7 +740,7 @@ scolar_events_edit = _scolar_eventsEditor.edit
def scolar_events_create(cnx, args):
# several "events" may share the same values
_scolar_eventsEditor.create(cnx, args, has_uniq_values=False)
_scolar_eventsEditor.create(cnx, args)
# --------
@ -764,7 +761,6 @@ _etud_annotationsEditor = ndb.EditableTable(
output_formators={"comment": safehtml.html_to_safe_html, "date": ndb.DateISOtoDMY},
)
etud_annotations_create = _etud_annotationsEditor.create
etud_annotations_delete = _etud_annotationsEditor.delete
etud_annotations_list = _etud_annotationsEditor.list
@ -1058,4 +1054,4 @@ def descr_situation_etud(context, etudid, ne=""):
else:
date_dem = events[0]["event_date"]
situation += " le " + str(date_dem)
return situation
return situation

View File

@ -33,6 +33,11 @@ import pprint
import time
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import flask
from flask import url_for
from flask import g
from flask_login import current_user
from app.scodoc.notes_log import log, logCallStack
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -124,7 +129,7 @@ _evaluationEditor = ndb.EditableTable(
)
def do_evaluation_list(context, args, sortkey=None):
def do_evaluation_list(args, sortkey=None):
"""List evaluations, sorted by numero (or most recent date first).
Ajoute les champs:
@ -172,19 +177,19 @@ def do_evaluation_list(context, args, sortkey=None):
return evals
def do_evaluation_list_in_formsemestre(context, formsemestre_id):
def do_evaluation_list_in_formsemestre(formsemestre_id):
"list evaluations in this formsemestre"
context = None # #context
mods = sco_moduleimpl.do_moduleimpl_list(context, formsemestre_id=formsemestre_id)
evals = []
for mod in mods:
evals += do_evaluation_list(
context, args={"moduleimpl_id": mod["moduleimpl_id"]}
)
evals += do_evaluation_list(args={"moduleimpl_id": mod["moduleimpl_id"]})
return evals
def _check_evaluation_args(context, args):
def _check_evaluation_args(args):
"Check coefficient, dates and duration, raises exception if invalid"
context = None # #context
moduleimpl_id = args["moduleimpl_id"]
# check bareme
note_max = args.get("note_max", None)
@ -236,7 +241,6 @@ def _check_evaluation_args(context, args):
def do_evaluation_create(
context,
moduleimpl_id=None,
jour=None,
heure_debut=None,
@ -249,29 +253,24 @@ def do_evaluation_create(
evaluation_type=None,
numero=None,
REQUEST=None,
**kw # ceci pour absorber les arguments excedentaires de tf #sco8
**kw, # ceci pour absorber les arguments excedentaires de tf #sco8
):
"""Create an evaluation"""
if not sco_permissions_check.can_edit_evaluation(
context, REQUEST, moduleimpl_id=moduleimpl_id
):
context = None # #context
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
raise AccessDenied(
"Modification évaluation impossible pour %s"
% scu.get_current_user_name(REQUEST)
"Modification évaluation impossible pour %s" % current_user.get_nomplogin()
)
args = locals()
log("do_evaluation_create: args=" + str(args))
_check_evaluation_args(context, args)
_check_evaluation_args(args)
# Check numeros
module_evaluation_renumber(
context, moduleimpl_id, REQUEST=REQUEST, only_if_unumbered=True
)
module_evaluation_renumber(moduleimpl_id, only_if_unumbered=True)
if not "numero" in args or args["numero"] is None:
n = None
# determine le numero avec la date
# Liste des eval existantes triees par date, la plus ancienne en tete
ModEvals = do_evaluation_list(
context,
args={"moduleimpl_id": moduleimpl_id},
sortkey="jour asc, heure_debut asc",
)
@ -289,9 +288,7 @@ def do_evaluation_create(
next_eval = e
break
if next_eval:
n = module_evaluation_insert_before(
context, ModEvals, next_eval, REQUEST
)
n = module_evaluation_insert_before(ModEvals, next_eval)
else:
n = None # a placer en fin
if n is None: # pas de date ou en fin:
@ -313,8 +310,6 @@ def do_evaluation_create(
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=moduleimpl_id,
text='Création d\'une évaluation dans <a href="%(url)s">%(titre)s</a>' % mod,
@ -324,22 +319,20 @@ def do_evaluation_create(
return r
def do_evaluation_edit(context, REQUEST, args):
def do_evaluation_edit(args):
"edit an evaluation"
context = None # #context
evaluation_id = args["evaluation_id"]
the_evals = do_evaluation_list(context, {"evaluation_id": evaluation_id})
the_evals = do_evaluation_list({"evaluation_id": evaluation_id})
if not the_evals:
raise ValueError("evaluation inexistante !")
moduleimpl_id = the_evals[0]["moduleimpl_id"]
if not sco_permissions_check.can_edit_evaluation(
context, REQUEST, moduleimpl_id=moduleimpl_id
):
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
raise AccessDenied(
"Modification évaluation impossible pour %s"
% scu.get_current_user_name(REQUEST)
"Modification évaluation impossible pour %s" % current_user.get_nomplogin()
)
args["moduleimpl_id"] = moduleimpl_id
_check_evaluation_args(context, args)
_check_evaluation_args(args)
cnx = ndb.GetDBConnexion()
_evaluationEditor.edit(cnx, args)
@ -348,20 +341,18 @@ def do_evaluation_edit(context, REQUEST, args):
sco_cache.invalidate_formsemestre(formsemestre_id=M["formsemestre_id"])
def do_evaluation_delete(context, REQUEST, evaluation_id):
def do_evaluation_delete(evaluation_id):
"delete evaluation"
the_evals = do_evaluation_list(context, {"evaluation_id": evaluation_id})
context = None # #context
the_evals = do_evaluation_list({"evaluation_id": evaluation_id})
if not the_evals:
raise ValueError("evaluation inexistante !")
moduleimpl_id = the_evals[0]["moduleimpl_id"]
if not sco_permissions_check.can_edit_evaluation(
context, REQUEST, moduleimpl_id=moduleimpl_id
):
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
raise AccessDenied(
"Modification évaluation impossible pour %s"
% scu.get_current_user_name(REQUEST)
"Modification évaluation impossible pour %s" % current_user.get_nomplogin()
)
NotesDB = do_evaluation_get_all_notes(context, evaluation_id) # { etudid : value }
NotesDB = do_evaluation_get_all_notes(evaluation_id) # { etudid : value }
notes = [x["value"] for x in NotesDB.values()]
if notes:
raise ScoValueError(
@ -381,8 +372,6 @@ def do_evaluation_delete(context, REQUEST, evaluation_id):
scu.NotesURL() + "/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
)
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=moduleimpl_id,
text='Suppression d\'une évaluation dans <a href="%(url)s">%(titre)s</a>' % mod,
@ -393,9 +382,7 @@ def do_evaluation_delete(context, REQUEST, evaluation_id):
_DEE_TOT = 0
def do_evaluation_etat(
context, evaluation_id, partition_id=None, select_first_partition=False
):
def do_evaluation_etat(evaluation_id, partition_id=None, select_first_partition=False):
"""donne infos sur l'etat du evaluation
{ nb_inscrits, nb_notes, nb_abs, nb_neutre, nb_att,
moyenne, mediane, mini, maxi,
@ -404,12 +391,11 @@ def do_evaluation_etat(
à ce module ont des notes)
evalattente est vrai s'il ne manque que des notes en attente
"""
context = None # #context
nb_inscrits = len(
sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True
)
sco_groups.do_evaluation_listeetuds_groups(evaluation_id, getallstudents=True)
)
NotesDB = do_evaluation_get_all_notes(context, evaluation_id) # { etudid : value }
NotesDB = do_evaluation_get_all_notes(evaluation_id) # { etudid : value }
notes = [x["value"] for x in NotesDB.values()]
nb_abs = len([x for x in notes if x is None])
nb_neutre = len([x for x in notes if x == scu.NOTES_NEUTRALISE])
@ -432,7 +418,7 @@ def do_evaluation_etat(
else:
last_modif = None
# ---- Liste des groupes complets et incomplets
E = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0]
E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
is_malus = Mod["module_type"] == scu.MODULE_MALUS # True si module de malus
@ -568,7 +554,7 @@ def do_evaluation_etat(
}
def do_evaluation_list_in_sem(context, formsemestre_id, with_etat=True):
def do_evaluation_list_in_sem(formsemestre_id, with_etat=True):
"""Liste les evaluations de tous les modules de ce semestre.
Donne pour chaque eval son état (voir do_evaluation_etat)
{ evaluation_id,nb_inscrits, nb_notes, nb_abs, nb_neutre, moy, median, last_modif ... }
@ -625,19 +611,18 @@ def do_evaluation_list_in_sem(context, formsemestre_id, with_etat=True):
for r in res:
r["jour"] = r["jour"] or datetime.date(1900, 1, 1) # pour les comparaisons
if with_etat:
r["etat"] = do_evaluation_etat(context, r["evaluation_id"])
r["etat"] = do_evaluation_etat(r["evaluation_id"])
return res
# ancien _notes_getall
def do_evaluation_get_all_notes(
context, evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
evaluation_id, table="notes_notes", filter_suppressed=True, by_uid=None
):
"""Toutes les notes pour une evaluation: { etudid : { 'value' : value, 'date' : date ... }}
Attention: inclut aussi les notes des étudiants qui ne sont plus inscrits au module.
"""
# log('do_evaluation_get_all_notes( e=%s fs=%s )' % (evaluation_id, filter_suppressed))
do_cache = (
filter_suppressed and table == "notes_notes" and (by_uid is None)
) # pas de cache pour (rares) appels via undo_notes ou specifiant un enseignant
@ -665,7 +650,9 @@ def do_evaluation_get_all_notes(
for x in res:
d[x["etudid"]] = x
if do_cache:
sco_cache.EvaluationCache.set(evaluation_id, d)
status = sco_cache.EvaluationCache.set(evaluation_id, d)
if not status:
log(f"Warning: EvaluationCache.set: {evaluation_id}\t{status}")
return d
@ -703,7 +690,7 @@ def _eval_etat(evals):
}
def do_evaluation_etat_in_sem(context, formsemestre_id, REQUEST=None):
def do_evaluation_etat_in_sem(formsemestre_id):
"""-> nb_eval_completes, nb_evals_en_cours, nb_evals_vides,
date derniere modif, attente"""
nt = sco_cache.NotesTableCache.get(
@ -716,7 +703,7 @@ def do_evaluation_etat_in_sem(context, formsemestre_id, REQUEST=None):
return etat
def do_evaluation_etat_in_mod(context, nt, moduleimpl_id):
def do_evaluation_etat_in_mod(nt, moduleimpl_id):
""""""
evals = nt.get_mod_evaluation_etat_list(moduleimpl_id)
etat = _eval_etat(evals)
@ -726,8 +713,9 @@ def do_evaluation_etat_in_mod(context, nt, moduleimpl_id):
return etat
def formsemestre_evaluations_cal(context, formsemestre_id, REQUEST=None):
def formsemestre_evaluations_cal(formsemestre_id, REQUEST=None):
"""Page avec calendrier de toutes les evaluations de ce semestre"""
context = None # #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > liste evaluations
@ -810,16 +798,16 @@ def formsemestre_evaluations_cal(context, formsemestre_id, REQUEST=None):
"""<p><a href="formsemestre_evaluations_delai_correction?formsemestre_id=%s" class="stdlink">voir les délais de correction</a></p>
"""
% (formsemestre_id,),
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
def evaluation_date_first_completion(context, evaluation_id):
def evaluation_date_first_completion(evaluation_id):
"""Première date à laquelle l'évaluation a été complète
ou None si actuellement incomplète
"""
etat = do_evaluation_etat(context, evaluation_id)
etat = do_evaluation_etat(evaluation_id)
if not etat["evalcomplete"]:
return None
@ -827,7 +815,7 @@ def evaluation_date_first_completion(context, evaluation_id):
# Il faut considerer les inscriptions au semestre
# (pour avoir l'etat et le groupe) et aussi les inscriptions
# au module (pour gerer les modules optionnels correctement)
# E = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0]
# E = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
# M = sco_moduleimpl.do_moduleimpl_list(context,moduleimpl_id=E["moduleimpl_id"])[0]
# formsemestre_id = M["formsemestre_id"]
# insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_listinscrits(context, formsemestre_id)
@ -837,13 +825,11 @@ def evaluation_date_first_completion(context, evaluation_id):
# ins = [i for i in insem if i["etudid"] in insmodset]
notes = list(
do_evaluation_get_all_notes(
context, evaluation_id, filter_suppressed=False
).values()
do_evaluation_get_all_notes(evaluation_id, filter_suppressed=False).values()
)
notes_log = list(
do_evaluation_get_all_notes(
context, evaluation_id, filter_suppressed=False, table="notes_notes_log"
evaluation_id, filter_suppressed=False, table="notes_notes_log"
).values()
)
date_premiere_note = {} # etudid : date
@ -861,13 +847,14 @@ def evaluation_date_first_completion(context, evaluation_id):
def formsemestre_evaluations_delai_correction(
context, formsemestre_id, format="html", REQUEST=None
formsemestre_id, format="html", REQUEST=None
):
"""Experimental: un tableau indiquant pour chaque évaluation
le nombre de jours avant la publication des notes.
N'indique pas les évaluations de ratrapage ni celles des modules de bonus/malus.
"""
context = None # #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > liste evaluations
@ -884,9 +871,7 @@ def formsemestre_evaluations_delai_correction(
Mod["module_type"] == scu.MODULE_MALUS
):
continue
e["date_first_complete"] = evaluation_date_first_completion(
context, e["evaluation_id"]
)
e["date_first_complete"] = evaluation_date_first_completion(e["evaluation_id"])
if e["date_first_complete"]:
e["delai_correction"] = (e["date_first_complete"].date() - e["jour"]).days
else:
@ -930,7 +915,7 @@ def formsemestre_evaluations_delai_correction(
html_sortable=True,
html_title="<h2>Correction des évaluations du semestre</h2>",
caption="Correction des évaluations du semestre",
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]),
@ -938,7 +923,7 @@ def formsemestre_evaluations_delai_correction(
return tab.make_page(context, format=format, REQUEST=REQUEST)
def module_evaluation_insert_before(context, ModEvals, next_eval, REQUEST):
def module_evaluation_insert_before(ModEvals, next_eval):
"""Renumber evals such that an evaluation with can be inserted before next_eval
Returns numero suitable for the inserted evaluation
"""
@ -946,9 +931,9 @@ def module_evaluation_insert_before(context, ModEvals, next_eval, REQUEST):
n = next_eval["numero"]
if not n:
log("renumbering old evals")
module_evaluation_renumber(context, next_eval["moduleimpl_id"], REQUEST)
module_evaluation_renumber(next_eval["moduleimpl_id"])
next_eval = do_evaluation_list(
context, args={"evaluation_id": next_eval["evaluation_id"]}
args={"evaluation_id": next_eval["evaluation_id"]}
)[0]
n = next_eval["numero"]
else:
@ -959,35 +944,30 @@ def module_evaluation_insert_before(context, ModEvals, next_eval, REQUEST):
if e["numero"] >= n:
e["numero"] += 1
# log('incrementing %s to %s' % (e['evaluation_id'], e['numero']))
do_evaluation_edit(context, REQUEST, e)
do_evaluation_edit(e)
return n
def module_evaluation_move(context, evaluation_id, after=0, REQUEST=None, redirect=1):
def module_evaluation_move(evaluation_id, after=0, redirect=1):
"""Move before/after previous one (decrement/increment numero)
(published)
"""
e = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0]
e = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
redirect = int(redirect)
# access: can change eval ?
if not sco_permissions_check.can_edit_evaluation(
context, REQUEST, moduleimpl_id=e["moduleimpl_id"]
):
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=e["moduleimpl_id"]):
raise AccessDenied(
"Modification évaluation impossible pour %s"
% scu.get_current_user_name(REQUEST)
"Modification évaluation impossible pour %s" % current_user.get_nomplogin()
)
module_evaluation_renumber(
context, e["moduleimpl_id"], REQUEST=REQUEST, only_if_unumbered=True
)
e = do_evaluation_list(context, args={"evaluation_id": evaluation_id})[0]
module_evaluation_renumber(e["moduleimpl_id"], only_if_unumbered=True)
e = do_evaluation_list(args={"evaluation_id": evaluation_id})[0]
after = int(after) # 0: deplace avant, 1 deplace apres
if after not in (0, 1):
raise ValueError('invalid value for "after"')
ModEvals = do_evaluation_list(context, {"moduleimpl_id": e["moduleimpl_id"]})
ModEvals = do_evaluation_list({"moduleimpl_id": e["moduleimpl_id"]})
# log('ModEvals=%s' % [ x['evaluation_id'] for x in ModEvals] )
if len(ModEvals) > 1:
idx = [p["evaluation_id"] for p in ModEvals].index(evaluation_id)
@ -999,18 +979,20 @@ def module_evaluation_move(context, evaluation_id, after=0, REQUEST=None, redire
if neigh: #
# swap numero with neighbor
e["numero"], neigh["numero"] = neigh["numero"], e["numero"]
do_evaluation_edit(context, REQUEST, e)
do_evaluation_edit(context, REQUEST, neigh)
do_evaluation_edit(e)
do_evaluation_edit(neigh)
# redirect to moduleimpl page:
if redirect:
return REQUEST.RESPONSE.redirect(
"moduleimpl_status?moduleimpl_id=" + e["moduleimpl_id"]
return flask.redirect(
url_for(
"notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept,
moduleimpl_id=e["moduleimpl_id"],
)
)
def module_evaluation_renumber(
context, moduleimpl_id, REQUEST=None, only_if_unumbered=False, redirect=0
):
def module_evaluation_renumber(moduleimpl_id, only_if_unumbered=False, redirect=0):
"""Renumber evaluations in this module, according to their date. (numero=0: oldest one)
Needed because previous versions of ScoDoc did not have eval numeros
Note: existing numeros are ignored
@ -1020,7 +1002,6 @@ def module_evaluation_renumber(
# List sorted according to date/heure, ignoring numeros:
# (note that we place evaluations with NULL date at the end)
ModEvals = do_evaluation_list(
context,
args={"moduleimpl_id": moduleimpl_id},
sortkey="jour asc, heure_debut asc",
)
@ -1034,24 +1015,29 @@ def module_evaluation_renumber(
i = 1
for e in ModEvals:
e["numero"] = i
do_evaluation_edit(context, REQUEST, e)
do_evaluation_edit(e)
i += 1
# If requested, redirect to moduleimpl page:
if redirect:
return REQUEST.RESPONSE.redirect(
"moduleimpl_status?moduleimpl_id=" + moduleimpl_id
return flask.redirect(
url_for(
"notes.moduleimpl_status",
scodoc_dept=g.scodoc_dept,
moduleimpl_id=moduleimpl_id,
)
)
# -------------- VIEWS
def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=None):
def evaluation_describe(evaluation_id="", edit_in_place=True, REQUEST=None):
"""HTML description of evaluation, for page headers
edit_in_place: allow in-place editing when permitted (not implemented)
"""
context = None # #context
from app.scodoc import sco_saisie_notes
E = do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = do_evaluation_list({"evaluation_id": evaluation_id})[0]
moduleimpl_id = E["moduleimpl_id"]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0]
Mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
@ -1060,7 +1046,7 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
resp = u["prenomnom"]
nomcomplet = u["nomcomplet"]
can_edit = sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, moduleimpl_id, allow_ens=False
REQUEST.AUTHENTICATED_USER, moduleimpl_id, allow_ens=False
)
link = (
@ -1097,7 +1083,7 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
% (jour, E["heure_debut"], E["heure_fin"])
)
if E["jour"]:
group_id = sco_groups.get_default_group(context, formsemestre_id)
group_id = sco_groups.get_default_group(formsemestre_id)
H.append(
'<span class="noprint"><a href="%s/Absences/EtatAbsencesDate?group_ids=%s&date=%s">(absences ce jour)</a></span>'
% (
@ -1122,7 +1108,6 @@ def evaluation_describe(context, evaluation_id="", edit_in_place=True, REQUEST=N
def evaluation_create_form(
context,
moduleimpl_id=None,
evaluation_id=None,
REQUEST=None,
@ -1131,8 +1116,9 @@ def evaluation_create_form(
page_title="Evaluation",
):
"formulaire creation/edition des evaluations (pas des notes)"
context = None # #context
if evaluation_id != None:
the_eval = do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
the_eval = do_evaluation_list({"evaluation_id": evaluation_id})[0]
moduleimpl_id = the_eval["moduleimpl_id"]
#
M = sco_moduleimpl.do_moduleimpl_withmodule_list(
@ -1142,18 +1128,16 @@ def evaluation_create_form(
formsemestre_id = M["formsemestre_id"]
min_note_max = scu.NOTES_PRECISION # le plus petit bareme possible
if not readonly:
if not sco_permissions_check.can_edit_evaluation(
context, REQUEST, moduleimpl_id=moduleimpl_id
):
if not sco_permissions_check.can_edit_evaluation(moduleimpl_id=moduleimpl_id):
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "<h2>Opération non autorisée</h2><p>"
+ "Modification évaluation impossible pour %s"
% scu.get_current_user_name(REQUEST)
% current_user.get_nomplogin()
+ "</p>"
+ '<p><a href="moduleimpl_status?moduleimpl_id=%s">Revenir</a></p>'
% (moduleimpl_id,)
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
if readonly:
edit = True # montre les donnees existantes
@ -1187,7 +1171,7 @@ def evaluation_create_form(
action = "Modification d'une é"
link = ""
# Note maximale actuelle dans cette eval ?
etat = do_evaluation_etat(context, evaluation_id)
etat = do_evaluation_etat(evaluation_id)
if etat["maxi_num"] is not None:
min_note_max = max(scu.NOTES_PRECISION, etat["maxi_num"])
else:
@ -1242,7 +1226,7 @@ def evaluation_create_form(
if not readonly:
H = ["<h3>%svaluation en %s</h3>" % (action, mod_descr)]
else:
return evaluation_describe(context, evaluation_id, REQUEST=REQUEST)
return evaluation_describe(evaluation_id, REQUEST=REQUEST)
heures = ["%02dh%02d" % (h, m) for h in range(8, 19) for m in (0, 30)]
#
@ -1376,17 +1360,10 @@ def evaluation_create_form(
dest_url = "moduleimpl_status?moduleimpl_id=%s" % M["moduleimpl_id"]
if tf[0] == 0:
head = html_sco_header.sco_header(context, REQUEST, page_title=page_title)
return (
head
+ "\n".join(H)
+ "\n"
+ tf[1]
+ help
+ html_sco_header.sco_footer(context, REQUEST)
)
head = html_sco_header.sco_header(page_title=page_title)
return head + "\n".join(H) + "\n" + tf[1] + help + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# form submission
if tf[2]["visibulletinlist"]:
@ -1395,8 +1372,8 @@ def evaluation_create_form(
tf[2]["visibulletin"] = 0
if not edit:
# creation d'une evaluation
evaluation_id = do_evaluation_create(context, REQUEST=REQUEST, **tf[2])
return REQUEST.RESPONSE.redirect(dest_url)
evaluation_id = do_evaluation_create(REQUEST=REQUEST, **tf[2])
return flask.redirect(dest_url)
else:
do_evaluation_edit(context, REQUEST, tf[2])
return REQUEST.RESPONSE.redirect(dest_url)
do_evaluation_edit(tf[2])
return flask.redirect(dest_url)

File diff suppressed because it is too large Load Diff

View File

@ -102,9 +102,7 @@ def _build_results_table(context, start_date=None, end_date=None, types_parcours
origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
html_class="table_leftalign",
html_sortable=True,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab, semlist
@ -207,7 +205,6 @@ def _build_results_list(context, dpv_by_sem, etuds_infos):
def get_set_formsemestre_id_dates(context, start_date, end_date):
"""Ensemble des formsemestre_id entre ces dates"""
s = ndb.SimpleDictFetch(
context,
"SELECT formsemestre_id FROM notes_formsemestre WHERE date_debut >= %(start_date)s AND date_fin <= %(end_date)s",
{"start_date": start_date, "end_date": end_date},
)
@ -281,8 +278,6 @@ def scodoc_table_results(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Export résultats",
init_qtip=True,
javascripts=html_sco_header.BOOTSTRAP_MULTISELECT_JS
@ -309,7 +304,7 @@ def scodoc_table_results(
""",
"\n".join(info_sems),
"""</div>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)

View File

@ -27,7 +27,9 @@
"""Recherche d'étudiants
"""
import flask
from flask import url_for, g
from flask_login import current_user
from scodoc_manager import sco_mgr
import app.scodoc.sco_utils as scu
@ -85,11 +87,9 @@ def form_search_etud(
if add_headers:
return (
html_sco_header.sco_header(
context, REQUEST, page_title="Choix d'un étudiant"
)
html_sco_header.sco_header(page_title="Choix d'un étudiant")
+ "\n".join(H)
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
else:
return "\n".join(H)
@ -98,8 +98,8 @@ def form_search_etud(
def search_etud_in_dept(context, expnom="", REQUEST=None):
"""Page recherche d'un etudiant.
Affiche la fiche de l'étudiant, ou, si la recherche donne plusieurs résultats, la liste des étudianst
correspondants.
Affiche la fiche de l'étudiant, ou, si la recherche donne plusieurs résultats,
la liste des étudiants correspondants.
Appelée par boite de recherche barre latérale gauche.
Args:
@ -109,15 +109,15 @@ def search_etud_in_dept(context, expnom="", REQUEST=None):
etuds = sco_etud.get_etud_info(filled=1, etudid=expnom, REQUEST=REQUEST)
if len(etuds) != 1:
if scu.is_valid_code_nip(expnom):
etuds = search_etuds_infos(context, code_nip=expnom, REQUEST=REQUEST)
etuds = search_etuds_infos(code_nip=expnom)
else:
etuds = search_etuds_infos(context, expnom=expnom, REQUEST=REQUEST)
etuds = search_etuds_infos(expnom=expnom)
else:
etuds = [] # si expnom est trop court, n'affiche rien
if len(etuds) == 1:
# va directement a la fiche
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for(
"scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etuds[0]["etudid"]
)
@ -125,12 +125,10 @@ def search_etud_in_dept(context, expnom="", REQUEST=None):
H = [
html_sco_header.sco_header(
context,
page_title="Recherche d'un étudiant",
no_side_bar=True,
init_qtip=True,
javascripts=["js/etud_info.js"],
REQUEST=REQUEST,
),
"""<h2>%d résultats pour "%s": choisissez un étudiant:</h2>"""
% (len(etuds), expnom),
@ -165,9 +163,7 @@ def search_etud_in_dept(context, expnom="", REQUEST=None):
rows=etuds,
html_sortable=True,
html_class="table_leftalign",
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
H.append(tab.html())
if len(etuds) > 20: # si la page est grande
@ -184,11 +180,11 @@ def search_etud_in_dept(context, expnom="", REQUEST=None):
H.append(
"""<p class="help">La recherche porte sur tout ou partie du NOM ou du NIP de l'étudiant</p>"""
)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
# Was chercheEtudsInfo()
def search_etuds_infos(context, expnom=None, code_nip=None, REQUEST=None):
def search_etuds_infos(expnom=None, code_nip=None):
"""recherche les étudiants correspondants à expnom ou au code_nip
et ramene liste de mappings utilisables en DTML.
"""
@ -223,7 +219,6 @@ def search_etud_by_name(context, term, REQUEST=None):
else:
if may_be_nip:
r = ndb.SimpleDictFetch(
context,
"SELECT nom, prenom, code_nip FROM identite WHERE code_nip LIKE %(beginning)s ORDER BY nom",
{"beginning": term + "%"},
)
@ -237,7 +232,6 @@ def search_etud_by_name(context, term, REQUEST=None):
]
else:
r = ndb.SimpleDictFetch(
context,
"SELECT etudid, nom, prenom FROM identite WHERE nom LIKE %(beginning)s ORDER BY nom",
{"beginning": term + "%"},
)
@ -256,50 +250,31 @@ def search_etud_by_name(context, term, REQUEST=None):
# ---------- Recherche sur plusieurs département
def form_search_etud_in_accessible_depts(context, REQUEST):
"""Form recherche etudiants pour page accueil ScoDoc"""
authuser = REQUEST.AUTHENTICATED_USER
# present form only to authenticated users
if not authuser.has_role("Authenticated"):
return ""
return """<form action="table_etud_in_accessible_depts" method="POST">
<b>Chercher étudiant:</b>
<input type="text" name="expnom" width="12" spellcheck="false" value="">
<input type="submit" value="Chercher">
<br/>(entrer une partie du nom ou le code NIP, cherche dans tous les départements autorisés)
def search_etud_in_accessible_depts(expnom=None, code_nip=None):
"""
def search_etud_in_accessible_depts(context, expnom=None, code_nip=None, REQUEST=None):
"""
context est le ZScoDoc
result is a list of (sorted) etuds, one list per dept.
"""
result = []
accessible_depts = []
depts = sco_mgr.get_dept_ids()
for dept in depts:
# log('%s searching %s' % (str(REQUEST.AUTHENTICATED_USER),dept))
if REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoView, dept=dept):
if current_user.has_permission(Permission.ScoView, dept=dept):
if expnom or code_nip:
accessible_depts.append(dept.Scolarite.DeptId())
etuds = search_etuds_infos(
dept.Scolarite, expnom=expnom, code_nip=code_nip, REQUEST=REQUEST
)
accessible_depts.append(dept)
ndb.set_sco_dept(dept)
etuds = search_etuds_infos(expnom=expnom, code_nip=code_nip)
else:
etuds = []
result.append(etuds)
return result, accessible_depts
def table_etud_in_accessible_depts(context, expnom=None, REQUEST=None):
def table_etud_in_accessible_depts(expnom=None):
"""
Page avec table étudiants trouvés, dans tous les departements.
Attention: nous sommes ici au niveau de ScoDoc, pas dans un département
"""
result, accessible_depts = search_etud_in_accessible_depts(
context, expnom=expnom, REQUEST=REQUEST
)
result, accessible_depts = search_etud_in_accessible_depts(expnom=expnom)
H = [
"""<div class="table_etud_in_accessible_depts">""",
"""<h3>Recherche multi-département de "<tt>%s</tt>"</h3>""" % expnom,
@ -330,16 +305,17 @@ def table_etud_in_accessible_depts(context, expnom=None, REQUEST=None):
else:
ss = ""
H.append(
"""<p>(recherche menée dans le%s département%s: %s)</p><p>
<a href=".." class="stdlink">Retour à l'accueil</a></p>"""
% (ss, ss, ", ".join(accessible_depts))
f"""<p>(recherche menée dans le{ss} département{ss}:
{", ".join(accessible_depts)})
</p>
<p>
<a href="{url_for("scodoc.index")}" class="stdlink">Retour à l'accueil</a>
</p>
</div>
"""
)
H.append("</div>")
return (
html_sco_header.scodoc_top_html_header(
context, REQUEST, page_title="Choix d'un étudiant"
)
html_sco_header.scodoc_top_html_header(page_title="Choix d'un étudiant")
+ "\n".join(H)
+ html_sco_header.standard_html_footer()
)
@ -352,9 +328,7 @@ def search_inscr_etud_by_nip(context, code_nip, REQUEST=None, format="json"):
Renvoie une liste des inscriptions de l'étudiants dans tout ScoDoc:
code_nip, nom, prenom, civilite_str, dept, formsemestre_id, date_debut_sem, date_fin_sem
"""
result, _ = search_etud_in_accessible_depts(
context, code_nip=code_nip, REQUEST=REQUEST
)
result, _ = search_etud_in_accessible_depts(code_nip=code_nip)
T = []
for etuds in result:

View File

@ -30,6 +30,8 @@
from operator import itemgetter
import xml.dom.minidom
import flask
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -134,13 +136,22 @@ def formation_export(
)
def formation_import_xml(context, REQUEST, doc, import_tags=True):
def formation_import_xml(context, doc, import_tags=True):
"""Create a formation from XML representation
(format dumped by formation_export( format='xml' ))
XML may contain object (UE, modules) ids: this function returns two
dicts mapping these ids to the created ids.
Args:
doc: str, xml data
import_tags: if false, does not import tags on modules.
Returns:
formation_id, modules_old2new, ues_old2new
"""
from app.scodoc import sco_edit_formation
log("formation_import_xml: doc=%s" % doc)
# log("formation_import_xml: doc=%s" % doc)
try:
dom = xml.dom.minidom.parseString(doc)
except:
@ -175,7 +186,7 @@ def formation_import_xml(context, REQUEST, doc, import_tags=True):
# create formation
# F_unquoted = F.copy()
# unescape_html_dict(F_unquoted)
formation_id = sco_edit_formation.do_formation_create(context, F, REQUEST)
formation_id = sco_edit_formation.do_formation_create(context, F)
log("formation %s created" % formation_id)
ues_old2new = {} # xml ue_id : new ue_id
modules_old2new = {} # xml module_id : new module_id
@ -189,14 +200,14 @@ def formation_import_xml(context, REQUEST, doc, import_tags=True):
del ue_info[1]["ue_id"]
else:
xml_ue_id = None
ue_id = sco_edit_ue.do_ue_create(context, ue_info[1], REQUEST)
ue_id = sco_edit_ue.do_ue_create(context, ue_info[1])
if xml_ue_id:
ues_old2new[xml_ue_id] = ue_id
# -- create matieres
for mat_info in ue_info[2]:
assert mat_info[0] == "matiere"
mat_info[1]["ue_id"] = ue_id
mat_id = sco_edit_matiere.do_matiere_create(context, mat_info[1], REQUEST)
mat_id = sco_edit_matiere.do_matiere_create(context, mat_info[1])
# -- create modules
for mod_info in mat_info[2]:
assert mod_info[0] == "module"
@ -208,7 +219,7 @@ def formation_import_xml(context, REQUEST, doc, import_tags=True):
mod_info[1]["formation_id"] = formation_id
mod_info[1]["matiere_id"] = mat_id
mod_info[1]["ue_id"] = ue_id
mod_id = sco_edit_module.do_module_create(context, mod_info[1], REQUEST)
mod_id = sco_edit_module.do_module_create(context, mod_info[1])
if xml_module_id:
modules_old2new[xml_module_id] = mod_id
if import_tags:
@ -248,6 +259,7 @@ def formation_list_table(context, formation_id=None, args={}, REQUEST=None):
f["parcours_name"] = ""
f["_titre_target"] = "ue_list?formation_id=%(formation_id)s" % f
f["_titre_link_class"] = "stdlink"
f["_titre_id"] = "titre-%s" % f["acronyme"].lower().replace(" ", "-")
# Ajoute les semestres associés à chaque formation:
f["sems"] = sco_formsemestre.do_formsemestre_list(
context, args={"formation_id": f["formation_id"]}
@ -255,13 +267,14 @@ def formation_list_table(context, formation_id=None, args={}, REQUEST=None):
f["sems_list_txt"] = ", ".join([s["session_id"] for s in f["sems"]])
f["_sems_list_txt_html"] = ", ".join(
[
'<a class="discretelink" href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(session_id)s<a>'
% s
'<a class="discretelink" href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%('
"session_id)s<a> " % s
for s in f["sems"]
]
+ [
'<a class="stdlink" href="formsemestre_createwithmodules?formation_id=%(formation_id)s&semestre_id=1">ajouter</a>'
% f
'<a class="stdlink" id="add-semestre-%s" '
'href="formsemestre_createwithmodules?formation_id=%s&semestre_id=1">ajouter</a> '
% (f["acronyme"].lower().replace(" ", "-"), f["formation_id"])
]
)
if f["sems"]:
@ -277,16 +290,17 @@ def formation_list_table(context, formation_id=None, args={}, REQUEST=None):
else:
but_locked = '<span class="but_placeholder"></span>'
if editable and not locked:
but_suppr = (
'<a class="stdlink" href="formation_delete?formation_id=%s">%s</a>'
% (f["formation_id"], suppricon)
but_suppr = '<a class="stdlink" href="formation_delete?formation_id=%s" id="delete-formation-%s">%s</a>' % (
f["formation_id"],
f["acronyme"].lower().replace(" ", "-"),
suppricon,
)
else:
but_suppr = '<span class="but_placeholder"></span>'
if editable:
but_edit = (
'<a class="stdlink" href="formation_edit?formation_id=%s">%s</a>'
% (f["formation_id"], editicon)
'<a class="stdlink" href="formation_edit?formation_id=%s" id="edit-formation-%s">%s</a>'
% (f["formation_id"], f["acronyme"].lower().replace(" ", "-"), editicon)
)
else:
but_edit = '<span class="but_placeholder"></span>'
@ -334,28 +348,24 @@ def formation_list_table(context, formation_id=None, args={}, REQUEST=None):
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
page_title=title,
pdf_title=title,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
def formation_create_new_version(context, formation_id, redirect=True, REQUEST=None):
"duplicate formation, with new version number"
xml = formation_export(context, formation_id, export_ids=True, format="xml")
new_id, modules_old2new, ues_old2new = formation_import_xml(context, REQUEST, xml)
new_id, modules_old2new, ues_old2new = formation_import_xml(context, xml)
# news
F = formation_list(context, args={"formation_id": new_id})[0]
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_FORM,
object=new_id,
text="Nouvelle version de la formation %(acronyme)s" % F,
)
if redirect:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"ue_list?formation_id=" + new_id + "&msg=Nouvelle version !"
)
else:
return new_id, modules_old2new, ues_old2new
return new_id, modules_old2new, ues_old2new

View File

@ -219,8 +219,9 @@ def etapes_apo_str(etapes):
return ", ".join([str(x) for x in etapes])
def do_formsemestre_create(context, args, REQUEST, silent=False):
def do_formsemestre_create(args, silent=False):
"create a formsemestre"
context = None # XXX #context
from app.scodoc import sco_groups
from app.scodoc import sco_news
@ -235,11 +236,12 @@ def do_formsemestre_create(context, args, REQUEST, silent=False):
# create default partition
partition_id = sco_groups.partition_create(
context, formsemestre_id, default=True, redirect=0, REQUEST=REQUEST
)
_group_id = sco_groups.createGroup(
context, partition_id, default=True, REQUEST=REQUEST
context,
formsemestre_id,
default=True,
redirect=0,
)
_group_id = sco_groups.createGroup(context, partition_id, default=True)
# news
if "titre" not in args:
@ -248,8 +250,6 @@ def do_formsemestre_create(context, args, REQUEST, silent=False):
args["url"] = "Notes/formsemestre_status?formsemestre_id=%(formsemestre_id)s" % args
if not silent:
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_SEM,
text='Création du semestre <a href="%(url)s">%(titre)s</a>' % args,
url=args["url"],
@ -277,7 +277,6 @@ def read_formsemestre_responsables(context, formsemestre_id):
:returns: liste de chaines
"""
r = ndb.SimpleDictFetch(
context,
"SELECT responsable_id FROM notes_formsemestre_responsables WHERE formsemestre_id = %(formsemestre_id)s",
{"formsemestre_id": formsemestre_id},
)
@ -340,7 +339,6 @@ def read_formsemestre_etapes(context, formsemestre_id):
:returns: liste d'instance de ApoEtapeVDI
"""
r = ndb.SimpleDictFetch(
context,
"SELECT etape_apo FROM notes_formsemestre_etapes WHERE formsemestre_id = %(formsemestre_id)s",
{"formsemestre_id": formsemestre_id},
)
@ -410,7 +408,7 @@ def sem_in_semestre_scolaire(context, sem, year=False, saison=0, REQUEST=None):
)
"""
if not year:
year = scu.AnneeScolaire(REQUEST)
year = scu.AnneeScolaire()
# est-on dans la même année universitaire ?
if sem["mois_debut_ord"] > 7:
if sem["annee_debut"] != str(year):
@ -434,7 +432,7 @@ def sem_in_annee_scolaire(context, sem, year=False, REQUEST=None):
Si annee non specifiée, année scolaire courante
"""
if not year:
year = scu.AnneeScolaire(REQUEST)
year = scu.AnneeScolaire()
return ((sem["annee_debut"] == str(year)) and (sem["mois_debut_ord"] > 7)) or (
(sem["annee_debut"] == str(year + 1)) and (sem["mois_debut_ord"] <= 7)
)
@ -518,13 +516,9 @@ def table_formsemestres(
"etapes_apo_str": "Apo.",
}
if sems:
preferences = sco_preferences.SemPreferences(
context, sems[0]["formsemestre_id"]
)
preferences = sco_preferences.SemPreferences(sems[0]["formsemestre_id"])
else:
preferences = sco_preferences.SemPreferences(
context,
)
preferences = sco_preferences.SemPreferences()
tab = GenTable(
columns_ids=columns_ids,
rows=sems,
@ -580,7 +574,7 @@ def view_formsemestre_by_etape(context, etape_apo=None, format="html", REQUEST=N
tab = table_formsemestres(
context,
list_formsemestre_by_etape(
context, etape_apo=etape_apo, annee_scolaire=scu.AnneeScolaire(REQUEST)
context, etape_apo=etape_apo, annee_scolaire=scu.AnneeScolaire()
),
html_title=html_title,
html_next_section="""<form action="view_formsemestre_by_etape">

View File

@ -27,7 +27,7 @@
"""Menu "custom" (défini par l'utilisateur) dans les semestres
"""
import flask
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -128,11 +128,9 @@ def formsemestre_custommenu_edit(context, formsemestre_id, REQUEST=None):
name="tf",
)
if tf[0] == 0:
return (
"\n".join(H) + "\n" + tf[1] + html_sco_header.sco_footer(context, REQUEST)
)
return "\n".join(H) + "\n" + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# form submission
cnx = ndb.GetDBConnexion()
@ -159,4 +157,4 @@ def formsemestre_custommenu_edit(context, formsemestre_id, REQUEST=None):
"url": tf[2]["url_" + custommenu_id],
},
)
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)

View File

@ -27,6 +27,7 @@
"""Form choix modules / responsables et creation formsemestre
"""
import flask
from flask import url_for, g
from app.auth.models import User
@ -68,8 +69,6 @@ def formsemestre_createwithmodules(context, REQUEST=None):
"""Page création d'un semestre"""
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Création d'un semestre",
javascripts=["libjs/AutoSuggest.js"],
cssstyles=["css/autosuggest_inquisitor.css"],
@ -82,7 +81,7 @@ def formsemestre_createwithmodules(context, REQUEST=None):
H.append(r)
else:
return r # response redirect
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def formsemestre_editwithmodules(context, REQUEST, formsemestre_id):
@ -119,7 +118,7 @@ def formsemestre_editwithmodules(context, REQUEST, formsemestre_id):
<p class="help">Les modules ont toujours un responsable. Par défaut, c'est le directeur des études.</p>"""
)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def can_edit_sem(context, REQUEST, formsemestre_id="", sem=None):
@ -412,7 +411,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
"title": "Element(s) Apogée:",
"explanation": "du semestre (ex: VRTW1). Séparés par des virgules.",
"allow_null": not sco_preferences.get_preference(
context, "always_require_apo_sem_codes"
"always_require_apo_sem_codes"
),
},
)
@ -425,7 +424,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
"title": "Element(s) Apogée:",
"explanation": "de l'année (ex: VRT1A). Séparés par des virgules.",
"allow_null": not sco_preferences.get_preference(
context, "always_require_apo_sem_codes"
"always_require_apo_sem_codes"
),
},
)
@ -552,9 +551,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
else:
disabled = ""
fcg = '<select name="%s" %s>' % (select_name, disabled)
default_group_id = sco_groups.get_default_group(
context, formsemestre_id
)
default_group_id = sco_groups.get_default_group(formsemestre_id)
fcg += '<option value="%s" %s>Tous</option>' % (
default_group_id,
opt_selected(default_group_id),
@ -676,9 +673,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
# check dates
if ndb.DateDMYtoISO(tf[2]["date_debut"]) > ndb.DateDMYtoISO(tf[2]["date_fin"]):
msg = '<ul class="tf-msg"><li class="tf-msg">Dates de début et fin incompatibles !</li></ul>'
if sco_preferences.get_preference(
context, "always_require_apo_sem_codes"
) and not any(
if sco_preferences.get_preference("always_require_apo_sem_codes") and not any(
[tf[2]["etape_apo" + str(n)] for n in range(0, scu.EDIT_NB_ETAPES + 1)]
):
msg = '<ul class="tf-msg"><li class="tf-msg">Code étape Apogée manquant</li></ul>'
@ -738,9 +733,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
)
if not edit:
# creation du semestre
formsemestre_id = sco_formsemestre.do_formsemestre_create(
context, tf[2], REQUEST
)
formsemestre_id = sco_formsemestre.do_formsemestre_create(tf[2])
# creation des modules
for module_id in tf[2]["tf-checked"]:
modargs = {
@ -749,7 +742,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
"responsable_id": tf[2][module_id],
}
_ = sco_moduleimpl.do_moduleimpl_create(context, modargs)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Nouveau%%20semestre%%20créé"
% formsemestre_id
)
@ -853,7 +846,7 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
)
return msg_html
else:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Semestre modifié"
% formsemestre_id
)
@ -873,9 +866,7 @@ def formsemestre_delete_moduleimpls(context, formsemestre_id, module_ids_to_del)
)[0]["moduleimpl_id"]
mod = sco_edit_module.do_module_list(context, {"module_id": module_id})[0]
# Evaluations dans ce module ?
evals = sco_evaluations.do_evaluation_list(
context, {"moduleimpl_id": moduleimpl_id}
)
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if evals:
msg += [
'<b>impossible de supprimer %s (%s) car il y a %d évaluations définies (<a href="moduleimpl_status?moduleimpl_id=%s" class="stdlink">supprimer les d\'abord</a>)</b>'
@ -997,9 +988,9 @@ def formsemestre_clone(context, formsemestre_id, REQUEST=None):
if ndb.DateDMYtoISO(tf[2]["date_debut"]) > ndb.DateDMYtoISO(tf[2]["date_fin"]):
msg = '<ul class="tf-msg"><li class="tf-msg">Dates de début et fin incompatibles !</li></ul>'
if tf[0] == 0 or msg:
return "".join(H) + msg + tf[1] + html_sco_header.sco_footer(context, REQUEST)
return "".join(H) + msg + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1: # cancel
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
)
else:
@ -1013,7 +1004,7 @@ def formsemestre_clone(context, formsemestre_id, REQUEST=None):
clone_partitions=tf[2]["clone_partitions"],
REQUEST=REQUEST,
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Nouveau%%20semestre%%20créé"
% new_formsemestre_id
)
@ -1042,7 +1033,7 @@ def do_formsemestre_clone(
args["date_debut"] = date_debut
args["date_fin"] = date_fin
args["etat"] = 1 # non verrouillé
formsemestre_id = sco_formsemestre.do_formsemestre_create(context, args, REQUEST)
formsemestre_id = sco_formsemestre.do_formsemestre_create(args)
log("created formsemestre %s" % formsemestre_id)
# 2- create moduleimpls
mods_orig = sco_moduleimpl.do_moduleimpl_list(
@ -1063,15 +1054,13 @@ def do_formsemestre_clone(
# optionally, copy evaluations
if clone_evaluations:
evals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": mod_orig["moduleimpl_id"]}
args={"moduleimpl_id": mod_orig["moduleimpl_id"]}
)
for e in evals:
args = e.copy()
del args["jour"] # erase date
args["moduleimpl_id"] = mid
_ = sco_evaluations.do_evaluation_create(
context, REQUEST=REQUEST, **args
)
_ = sco_evaluations.do_evaluation_create(REQUEST=REQUEST, **args)
# 3- copy uecoefs
objs = sco_formsemestre.formsemestre_uecoef_list(
@ -1085,7 +1074,7 @@ def do_formsemestre_clone(
# NB: don't copy notes_formsemestre_custommenu (usually specific)
# 4- Copy new style preferences
prefs = sco_preferences.SemPreferences(context, orig_formsemestre_id)
prefs = sco_preferences.SemPreferences(orig_formsemestre_id)
if orig_formsemestre_id in prefs.base_prefs.prefs:
for pname in prefs.base_prefs.prefs[orig_formsemestre_id]:
@ -1120,7 +1109,6 @@ def do_formsemestre_clone(
context,
formsemestre_id,
partition_name=partname,
REQUEST=REQUEST,
redirect=0,
)
for g in sco_groups.get_partition_groups(context, part):
@ -1136,7 +1124,7 @@ def do_formsemestre_clone(
part_id = g[0]
for group_name in g[1]:
_ = sco_groups.createGroup(
context, part_id, group_name=group_name, REQUEST=REQUEST
context, part_id, group_name=group_name
)
return formsemestre_id
@ -1186,7 +1174,6 @@ def formsemestre_associate_new_version(
)
return scu.confirm_dialog(
context,
"""<h2>Associer à une nouvelle version de formation non verrouillée ?</h2>
<p>Le programme pédagogique ("formation") va être dupliqué pour que vous puissiez le modifier sans affecter les autres semestres. Les autres paramètres (étudiants, notes...) du semestre seront inchangés.</p>
<p>Veillez à ne pas abuser de cette possibilité, car créer trop de versions de formations va vous compliquer la gestion (à vous de garder trace des différences et à ne pas vous tromper par la suite...).
@ -1196,7 +1183,6 @@ def formsemestre_associate_new_version(
+ "</div>",
OK="Associer ces semestres à une nouvelle version",
dest_url="",
REQUEST=REQUEST,
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
parameters={"formsemestre_id": formsemestre_id},
)
@ -1204,7 +1190,7 @@ def formsemestre_associate_new_version(
do_formsemestres_associate_new_version(
context, [formsemestre_id] + other_formsemestre_ids, REQUEST=REQUEST
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s&head_message=Formation%%20dupliquée"
% formsemestre_id
)
@ -1299,7 +1285,7 @@ def formsemestre_delete(context, formsemestre_id, REQUEST=None):
</ol></div>""",
]
evals = sco_evaluations.do_evaluation_list_in_formsemestre(context, formsemestre_id)
evals = sco_evaluations.do_evaluation_list_in_formsemestre(formsemestre_id)
if evals:
H.append(
"""<p class="warning">Attention: il y a %d évaluations dans ce semestre (sa suppression entrainera l'effacement définif des notes) !</p>"""
@ -1326,15 +1312,13 @@ def formsemestre_delete(context, formsemestre_id, REQUEST=None):
)
else:
H.append(tf[1])
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
elif tf[0] == -1: # cancel
return REQUEST.RESPONSE.redirect(
return flask.redirect(
scu.NotesURL() + "/formsemestre_status?formsemestre_id=" + formsemestre_id
)
else:
return REQUEST.RESPONSE.redirect(
"formsemestre_delete2?formsemestre_id=" + formsemestre_id
)
return flask.redirect("formsemestre_delete2?formsemestre_id=" + formsemestre_id)
def formsemestre_delete2(
@ -1344,16 +1328,14 @@ def formsemestre_delete2(
# Confirmation dialog
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Vous voulez vraiment supprimer ce semestre ???</h2><p>(opération irréversible)</p>""",
dest_url="",
REQUEST=REQUEST,
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
parameters={"formsemestre_id": formsemestre_id},
)
# Bon, s'il le faut...
do_formsemestre_delete(context, formsemestre_id, REQUEST)
return REQUEST.RESPONSE.redirect(scu.ScoURL() + "?head_message=Semestre%20supprimé")
do_formsemestre_delete(context, formsemestre_id)
return flask.redirect(scu.ScoURL() + "?head_message=Semestre%20supprimé")
def formsemestre_has_decisions_or_compensations(context, formsemestre_id):
@ -1361,14 +1343,13 @@ def formsemestre_has_decisions_or_compensations(context, formsemestre_id):
ou bien compensation de ce semestre par d'autre ssemestres.
"""
r = ndb.SimpleDictFetch(
context,
"SELECT v.* FROM scolar_formsemestre_validation v WHERE v.formsemestre_id = %(formsemestre_id)s OR v.compense_formsemestre_id = %(formsemestre_id)s",
{"formsemestre_id": formsemestre_id},
)
return r
def do_formsemestre_delete(context, formsemestre_id, REQUEST):
def do_formsemestre_delete(context, formsemestre_id):
"""delete formsemestre, and all its moduleimpls.
No checks, no warnings: erase all !
"""
@ -1382,21 +1363,18 @@ def do_formsemestre_delete(context, formsemestre_id, REQUEST):
for mod in mods:
# evaluations
evals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": mod["moduleimpl_id"]}
args={"moduleimpl_id": mod["moduleimpl_id"]}
)
for e in evals:
ndb.SimpleQuery(
context,
"DELETE FROM notes_notes WHERE evaluation_id=%(evaluation_id)s",
e,
)
ndb.SimpleQuery(
context,
"DELETE FROM notes_notes_log WHERE evaluation_id=%(evaluation_id)s",
e,
)
ndb.SimpleQuery(
context,
"DELETE FROM notes_evaluation WHERE evaluation_id=%(evaluation_id)s",
e,
)
@ -1449,8 +1427,6 @@ def do_formsemestre_delete(context, formsemestre_id, REQUEST):
from app.scodoc import sco_news
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_SEM,
object=formsemestre_id,
text="Suppression du semestre %(titre)s" % sem,
@ -1463,12 +1439,10 @@ def formsemestre_edit_options(context, formsemestre_id, target_url=None, REQUEST
(accessible par ScoImplement ou dir. etudes)
"""
log("formsemestre_edit_options")
ok, err = sco_permissions_check.check_access_diretud(
context, formsemestre_id, REQUEST
)
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
if not ok:
return err
return sco_preferences.SemPreferences(context, formsemestre_id).edit(
return sco_preferences.SemPreferences(formsemestre_id).edit(
REQUEST=REQUEST, categories=["bul"]
)
@ -1479,9 +1453,7 @@ def formsemestre_change_lock(
"""Change etat (verrouille si ouvert, déverrouille si fermé)
nota: etat (1 ouvert, 0 fermé)
"""
ok, err = sco_permissions_check.check_access_diretud(
context, formsemestre_id, REQUEST
)
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
if not ok:
return err
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -1493,7 +1465,6 @@ def formsemestre_change_lock(
else:
msg = "verrouillage"
return scu.confirm_dialog(
context,
"<h2>Confirmer le %s du semestre ?</h2>" % msg,
helpmsg="""Les notes d'un semestre verrouillé ne peuvent plus être modifiées.
Un semestre verrouillé peut cependant être déverrouillé facilement à tout moment
@ -1502,7 +1473,6 @@ def formsemestre_change_lock(
Le programme d'une formation qui a un semestre verrouillé ne peut plus être modifié.
""",
dest_url="",
REQUEST=REQUEST,
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
parameters={"etat": etat, "formsemestre_id": formsemestre_id},
)
@ -1512,7 +1482,7 @@ def formsemestre_change_lock(
args = {"formsemestre_id": formsemestre_id, "etat": etat}
sco_formsemestre.do_formsemestre_edit(context, args)
if REQUEST:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
)
@ -1521,9 +1491,7 @@ def formsemestre_change_publication_bul(
context, formsemestre_id, REQUEST=None, dialog_confirmed=False
):
"""Change etat publication bulletins sur portail"""
ok, err = sco_permissions_check.check_access_diretud(
context, formsemestre_id, REQUEST
)
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
if not ok:
return err
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -1535,7 +1503,6 @@ def formsemestre_change_publication_bul(
else:
msg = ""
return scu.confirm_dialog(
context,
"<h2>Confirmer la %s publication des bulletins ?</h2>" % msg,
helpmsg="""Il est parfois utile de désactiver la diffusion des bulletins,
par exemple pendant la tenue d'un jury ou avant harmonisation des notes.
@ -1543,7 +1510,6 @@ def formsemestre_change_publication_bul(
Ce réglage n'a d'effet que si votre établissement a interfacé ScoDoc et un portail étudiant.
""",
dest_url="",
REQUEST=REQUEST,
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
parameters={"bul_hide_xml": etat, "formsemestre_id": formsemestre_id},
)
@ -1555,7 +1521,7 @@ def formsemestre_change_publication_bul(
args = {"formsemestre_id": formsemestre_id, "bul_hide_xml": etat}
sco_formsemestre.do_formsemestre_edit(context, args)
if REQUEST:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
)
return None
@ -1565,14 +1531,12 @@ def formsemestre_edit_uecoefs(context, formsemestre_id, err_ue_id=None, REQUEST=
"""Changement manuel des coefficients des UE capitalisées."""
from app.scodoc import notes_table
ok, err = sco_permissions_check.check_access_diretud(
context, formsemestre_id, REQUEST
)
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
if not ok:
return err
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
footer = html_sco_header.sco_footer(context, REQUEST)
footer = html_sco_header.sco_footer()
help = """<p class="help">
Seuls les modules ont un coefficient. Cependant, il est nécessaire d'affecter un coefficient aux UE capitalisée pour pouvoir les prendre en compte dans la moyenne générale.
</p>
@ -1741,10 +1705,10 @@ def get_formsemestre_session_id(context, sem, F, parcours):
# parcours = sco_codes_parcours.get_parcours_from_code(F['type_parcours'])
ImputationDept = sco_preferences.get_preference(
context, "ImputationDept", sem["formsemestre_id"]
"ImputationDept", sem["formsemestre_id"]
)
if not ImputationDept:
ImputationDept = sco_preferences.get_preference(context, "DeptName")
ImputationDept = sco_preferences.get_preference("DeptName")
ImputationDept = ImputationDept.upper()
parcours_type = parcours.NAME
modalite = sem["modalite"]

View File

@ -33,6 +33,7 @@ Ces semestres n'auront qu'un seul inscrit !
"""
import time
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -65,9 +66,7 @@ def formsemestre_ext_create(context, etudid, sem_params, REQUEST=None):
sem_params["modalite"] = "EXT"
sem_params["etapes"] = None
sem_params["responsables"] = [str(REQUEST.AUTHENTICATED_USER)]
formsemestre_id = sco_formsemestre.do_formsemestre_create(
context, sem_params, REQUEST, silent=True
)
formsemestre_id = sco_formsemestre.do_formsemestre_create(sem_params, silent=True)
# nota: le semestre est créé vide: pas de modules
# Inscription au semestre
@ -85,7 +84,7 @@ def formsemestre_ext_create_form(context, etudid, formsemestre_id, REQUEST=None)
"""Formulaire creation/inscription à un semestre extérieur"""
etud = sco_etud.get_etud_info(etudid=etudid, filled=1)[0]
H = [
html_sco_header.sco_header(context, REQUEST),
html_sco_header.sco_header(),
"""<h2>Enregistrement d'une inscription antérieure dans un autre établissement</h2>
<p class="help">
Cette opération créé un semestre extérieur ("ancien") et y inscrit juste cet étudiant.
@ -105,7 +104,7 @@ def formsemestre_ext_create_form(context, etudid, formsemestre_id, REQUEST=None)
etud["nomprenom"],
),
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
orig_sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
# Ne propose que des semestres de semestre_id strictement inférieur au semestre courant
# et seulement si pas inscrit au même semestre_id d'un semestre ordinaire ScoDoc.
@ -201,14 +200,14 @@ def formsemestre_ext_create_form(context, etudid, formsemestre_id, REQUEST=None)
)
return "\n".join(H) + "\n" + tf[1] + F
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"%s/formsemestre_bulletinetud?formsemestre_id==%s&etudid=%s"
% (scu.ScoURL(), formsemestre_id, etudid)
)
else:
tf[2]["formation_id"] = orig_sem["formation_id"]
formsemestre_ext_create(context, etudid, tf[2], REQUEST=REQUEST)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
@ -260,7 +259,7 @@ def formsemestre_ext_edit_ue_validations(
_record_ue_validations_and_coefs(
context, formsemestre_id, etudid, ue_list, tf[2], REQUEST=REQUEST
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
% (formsemestre_id, etudid)
)
@ -271,8 +270,6 @@ def _make_page(context, etud, sem, tf, message="", REQUEST=None):
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Validation des UE d'un semestre extérieur",
javascripts=["js/formsemestre_ext_edit_ue_validations.js"],
),
@ -297,7 +294,7 @@ def _make_page(context, etud, sem, tf, message="", REQUEST=None):
</a></div>
"""
% (sem["formsemestre_id"], etud["etudid"]),
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return H

View File

@ -29,6 +29,7 @@
"""
import time
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -102,7 +103,6 @@ def do_formsemestre_inscription_create(context, args, REQUEST, method=None):
)
# Log etudiant
logdb(
REQUEST,
cnx,
method=method,
etudid=args["etudid"],
@ -187,13 +187,10 @@ def do_formsemestre_desinscription(context, etudid, formsemestre_id, REQUEST=Non
"do_formsemestre_desinscription: suppression du semestre extérieur %s"
% formsemestre_id
)
sco_formsemestre_edit.do_formsemestre_delete(
context, formsemestre_id, REQUEST=REQUEST
)
sco_formsemestre_edit.do_formsemestre_delete(context, formsemestre_id)
if REQUEST:
logdb(
REQUEST,
cnx,
method="formsemestre_desinscription",
etudid=etudid,
@ -226,7 +223,7 @@ def do_formsemestre_inscription_with_modules(
)
# inscriptions aux groupes
# 1- inscrit au groupe 'tous'
group_id = sco_groups.get_default_group(context, formsemestre_id)
group_id = sco_groups.get_default_group(formsemestre_id)
sco_groups.set_group(context, etudid, group_id)
gdone = {group_id: 1} # empeche doublons
@ -279,7 +276,7 @@ def formsemestre_inscription_with_modules_form(
"""
etud = sco_etud.get_etud_info(etudid=etudid, filled=1)[0]
H = [
html_sco_header.sco_header(context, REQUEST),
html_sco_header.sco_header(),
"<h2>Inscription de %s" % etud["nomprenom"],
]
if only_ext:
@ -291,7 +288,7 @@ def formsemestre_inscription_with_modules_form(
</p>
<h3>Choisir un semestre:</h3>"""
)
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
sems = sco_formsemestre.do_formsemestre_list(context, args={"etat": "1"})
insem = do_formsemestre_inscription_list(
context, args={"etudid": etudid, "etat": "I"}
@ -348,7 +345,7 @@ def formsemestre_inscription_with_modules(
sem,
)
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
# Check 1: déjà inscrit ici ?
ins = do_formsemestre_inscription_list(context, {"etudid": etudid})
already = False
@ -416,7 +413,7 @@ def formsemestre_inscription_with_modules(
REQUEST=REQUEST,
method="formsemestre_inscription_with_modules",
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
else:
@ -453,9 +450,9 @@ def formsemestre_inscription_option(context, etudid, formsemestre_id, REQUEST=No
etud = sco_etud.get_etud_info(etudid=etudid, filled=1)[0]
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > get_etud_ue_status
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
H = [
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "<h2>Inscription de %s aux modules de %s (%s - %s)</h2>"
% (etud["nomprenom"], sem["titre_num"], sem["date_debut"], sem["date_fin"])
]
@ -569,7 +566,7 @@ function chkbx_select(field_id, state) {
)
return "\n".join(H) + "\n" + tf[1] + F
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
)
else:
@ -725,13 +722,13 @@ def do_moduleimpl_incription_options(
if REQUEST:
H = [
html_sco_header.sco_header(context, REQUEST),
html_sco_header.sco_header(),
"""<h3>Modifications effectuées</h3>
<p><a class="stdlink" href="%s">
Retour à la fiche étudiant</a></p>
"""
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -823,4 +820,4 @@ def formsemestre_inscrits_ailleurs(context, formsemestre_id, REQUEST=None):
)
else:
H.append("""<p>Aucun étudiant en inscription multiple (c'est normal) !</p>""")
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()

View File

@ -28,12 +28,11 @@
"""Tableau de bord semestre
"""
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import cgi
from flask import current_app
from flask import g
from flask import request
from flask import url_for
from flask_login import current_user
from app.scodoc.notes_log import log
import app.scodoc.sco_utils as scu
@ -64,22 +63,6 @@ from app.scodoc.gen_tables import GenTable
from app.scodoc.sco_formsemestre_custommenu import formsemestre_custommenu_html
# H = [ """<span class="barrenav"><ul class="nav">
# <li onmouseover="MenuDisplay(this)" onmouseout="MenuHide(this)"><a href="#" class="menu %s">%s</a><ul>""" % (cssclass, title)
# ]
# for item in items:
# if item.get('enabled', True):
# if base_url:
# item['urlq'] = urllib.quote(item['url'])
# else:
# item['urlq'] = item['url']
# H.append('<li><a href="' + base_url + '%(urlq)s">%(title)s</a></li>' % item)
# else:
# H.append('<li><span class="disabled_menu_item">%(title)s</span></li>' % item)
# H.append('</ul></li></ul></%s>' % elem)
# return ''.join(H)
def defMenuStats(context, formsemestre_id):
"Définition du menu 'Statistiques' "
return [
@ -98,7 +81,7 @@ def defMenuStats(context, formsemestre_id):
"title": "Graphe des parcours",
"endpoint": "notes.formsemestre_graph_parcours",
"args": {"formsemestre_id": formsemestre_id},
"enabled": scu.WITH_PYDOT,
"enabled": True,
},
{
"title": "Codes des parcours",
@ -138,10 +121,9 @@ def defMenuStats(context, formsemestre_id):
]
def formsemestre_status_menubar(context, sem, REQUEST):
def formsemestre_status_menubar(context, sem):
"""HTML to render menubar"""
authuser = REQUEST.AUTHENTICATED_USER
uid = str(authuser)
uid = current_user.user_name
formsemestre_id = sem["formsemestre_id"]
if int(sem["etat"]):
change_lock_msg = "Verrouiller"
@ -175,9 +157,9 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"formsemestre_id": formsemestre_id,
},
"enabled": (
authuser.has_permission(Permission.ScoImplement)
current_user.has_permission(Permission.ScoImplement)
or (
str(REQUEST.AUTHENTICATED_USER) in sem["responsables"]
current_user.user_name in sem["responsables"]
and sem["resp_can_edit"]
)
)
@ -189,9 +171,9 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"endpoint": "scolar.formsemestre_edit_preferences",
"args": {"formsemestre_id": formsemestre_id},
"enabled": (
authuser.has_permission(Permission.ScoImplement)
current_user.has_permission(Permission.ScoImplement)
or (
str(REQUEST.AUTHENTICATED_USER) in sem["responsables"]
current_user.user_name in sem["responsables"]
and sem["resp_can_edit"]
)
)
@ -203,7 +185,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"endpoint": "notes.formsemestre_edit_options",
"args": {"formsemestre_id": formsemestre_id},
"enabled": (uid in sem["responsables"])
or authuser.has_permission(Permission.ScoImplement),
or current_user.has_permission(Permission.ScoImplement),
"helpmsg": "Change les options",
},
{
@ -211,7 +193,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"endpoint": "notes.formsemestre_change_lock",
"args": {"formsemestre_id": formsemestre_id},
"enabled": (uid in sem["responsables"])
or authuser.has_permission(Permission.ScoImplement),
or current_user.has_permission(Permission.ScoImplement),
"helpmsg": "",
},
{
@ -239,14 +221,14 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"title": "Cloner ce semestre",
"endpoint": "notes.formsemestre_clone",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoImplement),
"enabled": current_user.has_permission(Permission.ScoImplement),
"helpmsg": "",
},
{
"title": "Associer à une nouvelle version du programme",
"endpoint": "notes.formsemestre_associate_new_version",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoChangeFormation)
"enabled": current_user.has_permission(Permission.ScoChangeFormation)
and (sem["etat"] == "1"),
"helpmsg": "",
},
@ -254,7 +236,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"title": "Supprimer ce semestre",
"endpoint": "notes.formsemestre_delete",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoImplement),
"enabled": current_user.has_permission(Permission.ScoImplement),
"helpmsg": "",
},
]
@ -281,43 +263,43 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"title": "Passage des étudiants depuis d'autres semestres",
"endpoint": "notes.formsemestre_inscr_passage",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoEtudInscrit)
"enabled": current_user.has_permission(Permission.ScoEtudInscrit)
and (sem["etat"] == "1"),
},
{
"title": "Synchroniser avec étape Apogée",
"endpoint": "notes.formsemestre_synchro_etuds",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoView)
and sco_preferences.get_preference(context, "portal_url")
"enabled": current_user.has_permission(Permission.ScoView)
and sco_preferences.get_preference("portal_url")
and (sem["etat"] == "1"),
},
{
"title": "Inscrire un étudiant",
"endpoint": "notes.formsemestre_inscription_with_modules_etud",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoEtudInscrit)
"enabled": current_user.has_permission(Permission.ScoEtudInscrit)
and (sem["etat"] == "1"),
},
{
"title": "Importer des étudiants dans ce semestre (table Excel)",
"endpoint": "scolar.form_students_import_excel",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoEtudInscrit)
"enabled": current_user.has_permission(Permission.ScoEtudInscrit)
and (sem["etat"] == "1"),
},
{
"title": "Import/export des données admission",
"endpoint": "scolar.form_students_import_infos_admissions",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoView),
"enabled": current_user.has_permission(Permission.ScoView),
},
{
"title": "Resynchroniser données identité",
"endpoint": "scolar.formsemestre_import_etud_admission",
"args": {"formsemestre_id": formsemestre_id},
"enabled": authuser.has_permission(Permission.ScoEtudChangeAdr)
and sco_preferences.get_preference(context, "portal_url"),
"enabled": current_user.has_permission(Permission.ScoEtudChangeAdr)
and sco_preferences.get_preference("portal_url"),
},
{
"title": "Exporter table des étudiants",
@ -325,7 +307,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"args": {
"format": "allxls",
"group_ids": sco_groups.get_default_group(
context, formsemestre_id, fix_if_missing=True, REQUEST=REQUEST
formsemestre_id, fix_if_missing=True
),
},
},
@ -349,7 +331,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"endpoint": "scolar.editPartitionForm",
"args": {"formsemestre_id": formsemestre_id},
"enabled": sco_groups.sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
formsemestre_id
),
},
]
@ -359,9 +341,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
)
submenu = []
enabled = (
sco_groups.sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
)
sco_groups.sco_permissions_check.can_change_groups(formsemestre_id)
and partitions
)
for partition in partitions:
@ -401,7 +381,7 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"endpoint": "notes.formsemestre_bulletins_mailetuds_choice",
"args": {"formsemestre_id": formsemestre_id},
"enabled": sco_bulletins.can_send_bulletin_by_mail(
context, formsemestre_id, REQUEST
context, formsemestre_id
),
},
{
@ -436,17 +416,13 @@ def formsemestre_status_menubar(context, sem, REQUEST):
"hidebac": 1,
"pref_override": 0,
},
"enabled": sco_permissions_check.can_validate_sem(
context, REQUEST, formsemestre_id
),
"enabled": sco_permissions_check.can_validate_sem(formsemestre_id),
},
{
"title": "Editer les PV et archiver les résultats",
"endpoint": "notes.formsemestre_archive",
"args": {"formsemestre_id": formsemestre_id},
"enabled": sco_permissions_check.can_edit_pv(
context, REQUEST, formsemestre_id
),
"enabled": sco_permissions_check.can_edit_pv(formsemestre_id),
},
{
"title": "Documents archivés",
@ -475,27 +451,32 @@ def formsemestre_status_menubar(context, sem, REQUEST):
return "\n".join(H)
def retreive_formsemestre_from_request(context, REQUEST):
def retreive_formsemestre_from_request():
"""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
"""
context = None # XXX #context
if request.method == "GET":
args = request.args
elif request.method == "POST":
args = request.form
else:
return None
# Search formsemestre
group_ids = REQUEST.form.get("group_ids", [])
if "formsemestre_id" in REQUEST.form:
formsemestre_id = REQUEST.form["formsemestre_id"]
elif "moduleimpl_id" in REQUEST.form:
group_ids = args.get("group_ids", [])
if "formsemestre_id" in args:
formsemestre_id = args["formsemestre_id"]
elif "moduleimpl_id" in args:
modimpl = sco_moduleimpl.do_moduleimpl_list(
context, moduleimpl_id=REQUEST.form["moduleimpl_id"]
context, moduleimpl_id=args["moduleimpl_id"]
)
if not modimpl:
return None # suppressed ?
modimpl = modimpl[0]
formsemestre_id = modimpl["formsemestre_id"]
elif "evaluation_id" in REQUEST.form:
E = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": REQUEST.form["evaluation_id"]}
)
elif "evaluation_id" in args:
E = sco_evaluations.do_evaluation_list({"evaluation_id": args["evaluation_id"]})
if not E:
return None # evaluation suppressed ?
E = E[0]
@ -503,8 +484,8 @@ def retreive_formsemestre_from_request(context, REQUEST):
context, moduleimpl_id=E["moduleimpl_id"]
)[0]
formsemestre_id = modimpl["formsemestre_id"]
elif "group_id" in REQUEST.form:
group = sco_groups.get_group(context, REQUEST.form["group_id"])
elif "group_id" in args:
group = sco_groups.get_group(context, args["group_id"])
formsemestre_id = group["formsemestre_id"]
elif group_ids:
if group_ids:
@ -515,8 +496,8 @@ def retreive_formsemestre_from_request(context, REQUEST):
group_id = group_ids[0]
group = sco_groups.get_group(context, group_id)
formsemestre_id = group["formsemestre_id"]
elif "partition_id" in REQUEST.form:
partition = sco_groups.get_partition(context, REQUEST.form["partition_id"])
elif "partition_id" in args:
partition = sco_groups.get_partition(context, args["partition_id"])
formsemestre_id = partition["formsemestre_id"]
else:
return None # no current formsemestre
@ -525,11 +506,11 @@ def retreive_formsemestre_from_request(context, REQUEST):
# Element HTML decrivant un semestre (barre de menu et infos)
def formsemestre_page_title(context, REQUEST):
def formsemestre_page_title(context):
"""Element HTML decrivant un semestre (barre de menu et infos)
Cherche dans REQUEST si un semestre est défini (formsemestre_id ou moduleimpl ou evaluation ou group)
"""
formsemestre_id = retreive_formsemestre_from_request(context, REQUEST)
formsemestre_id = retreive_formsemestre_from_request()
#
if not formsemestre_id:
return ""
@ -539,21 +520,22 @@ def formsemestre_page_title(context, REQUEST):
log("can't find formsemestre_id %s" % formsemestre_id)
return ""
fill_formsemestre(context, sem, REQUEST=REQUEST)
fill_formsemestre(sem)
H = [
"""<div class="formsemestre_page_title">""",
"""<div class="infos">
<span class="semtitle"><a class="stdlink" title="%(session_id)s" href="%(notes_url)s/formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titre)s</a><a title="%(etape_apo_str)s">%(num_sem)s</a>%(modalitestr)s</span><span class="dates"><a title="du %(date_debut)s au %(date_fin)s ">%(mois_debut)s - %(mois_fin)s</a></span><span class="resp"><a title="%(nomcomplet)s">%(resp)s</a></span><span class="nbinscrits"><a class="discretelink" href="%(notes_url)s/formsemestre_lists?formsemestre_id=%(formsemestre_id)s">%(nbinscrits)d inscrits</a></span><span class="lock">%(locklink)s</span><span class="eye">%(eyelink)s</span></div>"""
% sem,
formsemestre_status_menubar(context, sem, REQUEST),
formsemestre_status_menubar(context, sem),
"""</div>""",
]
return "\n".join(H)
def fill_formsemestre(context, sem, REQUEST=None):
def fill_formsemestre(sem):
"""Add some useful fields to help display formsemestres"""
context = None # XXX #context
notes_url = scu.NotesURL()
sem["notes_url"] = notes_url
formsemestre_id = sem["formsemestre_id"]
@ -567,9 +549,7 @@ def fill_formsemestre(context, sem, REQUEST=None):
)
else:
sem["locklink"] = ""
if sco_preferences.get_preference(
context, "bul_display_publication", formsemestre_id
):
if sco_preferences.get_preference("bul_display_publication", formsemestre_id):
if sem["bul_hide_xml"] != "0":
eyeicon = scu.icontag("hide_img", border="0", title="Bulletins NON publiés")
else:
@ -618,9 +598,7 @@ def formsemestre_description_table(
"""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > liste evaluations
use_ue_coefs = sco_preferences.get_preference(
context, "use_ue_coefs", formsemestre_id
)
use_ue_coefs = sco_preferences.get_preference("use_ue_coefs", formsemestre_id)
F = sco_formations.formation_list(
context, args={"formation_id": sem["formation_id"]}
)[0]
@ -705,7 +683,7 @@ def formsemestre_description_table(
sums = {"_css_row_class": "moyenne sortbottom", "ects": sum_ects, "Coef.": sum_coef}
R.append(sums)
columns_ids = ["UE", "Code", "Module", "Coef."]
if sco_preferences.get_preference(context, "bul_show_ects", formsemestre_id):
if sco_preferences.get_preference("bul_show_ects", formsemestre_id):
columns_ids += ["ects"]
columns_ids += ["Inscrits", "Responsable", "Enseignants"]
if with_evals:
@ -742,7 +720,7 @@ def formsemestre_description_table(
context, REQUEST, "Description du semestre", sem, with_page_header=False
),
pdf_title=title,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
@ -772,26 +750,17 @@ def formsemestre_description(
# genere liste html pour accès aux groupes de ce semestre
def _make_listes_sem(context, sem, REQUEST=None, with_absences=True):
context = context
authuser = REQUEST.AUTHENTICATED_USER
r = scu.ScoURL() # root url
# construit l'URL "destination"
# (a laquelle on revient apres saisie absences)
query_args = cgi.parse_qs(REQUEST.QUERY_STRING) # XXX TODO a revoir #py3
# soit via flask soit via https://docs.python.org/3/library/urllib.parse.html#module-urllib.parse
if "head_message" in query_args:
del query_args["head_message"]
destination = "%s?%s" % (
REQUEST.URL,
six.moves.urllib.parse.urlencode(query_args, True),
destination = url_for(
"notes.formsemestre_status",
scodoc_dept=g.scodoc_dept,
formsemestre_id=sem["formsemestre_id"],
)
destination = destination.replace(
"%", "%%"
) # car ici utilisee dans un format string !
#
H = []
# pas de menu absences si pas autorise:
if with_absences and not authuser.has_permission(Permission.ScoAbsChange):
if with_absences and not current_user.has_permission(Permission.ScoAbsChange):
with_absences = False
#
@ -806,39 +775,29 @@ def _make_listes_sem(context, sem, REQUEST=None, with_absences=True):
try:
if with_absences:
first_monday = sco_abs.ddmmyyyy(sem["date_debut"]).prev_monday()
FA = [] # formulaire avec menu saisi absences
FA.append(
'<td><form action="{}" method="get">'.format(
url_for(
form_abs_tmpl = f"""
<td><form action="{url_for(
"absences.SignaleAbsenceGrSemestre", scodoc_dept=g.scodoc_dept
)
)
)
FA.append(
'<input type="hidden" name="datefin" value="%(date_fin)s"/>' % sem
)
FA.append('<input type="hidden" name="group_ids" value="%(group_id)s"/>')
FA.append(
'<input type="hidden" name="destination" value="%s"/>' % destination
)
FA.append('<input type="submit" value="Saisir absences du" />')
FA.append('<select name="datedebut" class="noprint">')
)}" method="get">
<input type="hidden" name="datefin" value="{sem['date_fin']}"/>
<input type="hidden" name="group_ids" value="%(group_id)s"/>
<input type="hidden" name="destination" value="{destination}"/>
<input type="submit" value="Saisir absences du" />
<select name="datedebut" class="noprint">
"""
date = first_monday
for jour in sco_abs.day_names(context):
FA.append('<option value="%s">%s</option>' % (date, jour))
form_abs_tmpl += '<option value="%s">%s</option>' % (date, jour)
date = date.next_day()
FA.append("</select>")
FA.append(
'<a href="Absences/EtatAbsencesGr?group_ids=%%(group_id)s&debut=%(date_debut)s&fin=%(date_fin)s">état</a>'
% sem
)
FA.append("</form></td>")
FormAbs = "\n".join(FA)
form_abs_tmpl += """
</select>
<a href="%(url_etat)s">état</a>
</form></td>
"""
else:
FormAbs = ""
form_abs_tmpl = ""
except ScoInvalidDateError: # dates incorrectes dans semestres ?
FormAbs = ""
form_abs_tmpl = ""
#
H.append('<div id="grouplists">')
# Genere liste pour chaque partition (categorie de groupes)
@ -854,44 +813,70 @@ def _make_listes_sem(context, sem, REQUEST=None, with_absences=True):
n_members = len(
sco_groups.get_group_members(context, group["group_id"])
)
group["url"] = r
group["url_etat"] = url_for(
"absences.EtatAbsencesGr",
group_ids=group["group_id"],
debut=sem["date_debut"],
fin=sem["date_fin"],
scodoc_dept=g.scodoc_dept,
)
if group["group_name"]:
group["label"] = "groupe %(group_name)s" % group
else:
group["label"] = "liste"
H.append('<tr class="listegroupelink">')
H.append(
"""<td>
<a href="%(url)s/groups_view?group_ids=%(group_id)s">%(label)s</a>
f"""
<tr class="listegroupelink">
<td>
<a href="{
url_for("scolar.groups_view",
group_ids=group["group_id"],
scodoc_dept=g.scodoc_dept,
)
}">{group["label"]}</a>
</td><td>
(<a href="%(url)s/groups_view?group_ids=%(group_id)s&format=xls">format tableur</a>)
<a href="%(url)s/groups_view?curtab=tab-photos&group_ids=%(group_id)s&etat=I">Photos</a>
</td>"""
% group
(<a href="{
url_for("scolar.groups_view",
group_ids=group["group_id"],
format="xls",
scodoc_dept=g.scodoc_dept,
)
}">tableur</a>)
<a href="{
url_for("scolar.groups_view",
curtab="tab-photos",
group_ids=group["group_id"],
etat="I",
scodoc_dept=g.scodoc_dept,
)
}">Photos</a>
</td>
<td>({n_members} étudiants)</td>
"""
)
H.append("<td>(%d étudiants)</td>" % n_members)
if with_absences:
H.append(FormAbs % group)
H.append(form_abs_tmpl % group)
H.append("</tr>")
H.append("</table>")
else:
H.append('<p class="help indent">Aucun groupe dans cette partition')
if sco_groups.sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
):
if sco_groups.sco_permissions_check.can_change_groups(formsemestre_id):
H.append(
' (<a href="affectGroups?partition_id=%s" class="stdlink">créer</a>)'
% partition["partition_id"]
)
H.append("</p>")
if sco_groups.sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
):
if sco_groups.sco_permissions_check.can_change_groups(formsemestre_id):
H.append(
'<h4><a href="editPartitionForm?formsemestre_id=%s">Ajouter une partition</a></h4>'
% formsemestre_id
f"""<h4><a
href="{
url_for("scolar.editPartitionForm",
formsemestre_id=formsemestre_id,
scodoc_dept=g.scodoc_dept,
)
}">Ajouter une partition</a></h4>"""
)
H.append("</div>")
@ -966,7 +951,7 @@ def formsemestre_status_head(
)
H.append("</td></tr>")
evals = sco_evaluations.do_evaluation_etat_in_sem(context, formsemestre_id)
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
H.append(
'<tr><td class="fichetitre2">Evaluations: </td><td> %(nb_evals_completes)s ok, %(nb_evals_en_cours)s en cours, %(nb_evals_vides)s vides'
% evals
@ -992,7 +977,7 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
H.append(
'<p class="fontorange"><em>Attention: ce semestre couvre plusieurs années scolaires !</em></p>'
)
# elif sco_preferences.get_preference(context, 'bul_display_publication', formsemestre_id):
# elif sco_preferences.get_preference( 'bul_display_publication', formsemestre_id):
# H.append('<p><em>Bulletins publiés sur le portail</em></p>')
return "".join(H)
@ -1016,9 +1001,7 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
)
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Semestre %s" % sem["titreannee"]
),
html_sco_header.sco_header(page_title="Semestre %s" % sem["titreannee"]),
'<div class="formsemestre_status">',
formsemestre_status_head(
context, formsemestre_id=formsemestre_id, page_title="Tableau de bord"
@ -1072,7 +1055,7 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
prev_ue_id = ue["ue_id"]
acronyme = ue["acronyme"]
titre = ue["titre"]
if sco_preferences.get_preference(context, "use_ue_coefs", formsemestre_id):
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
titre += " <b>(coef. %s)</b>" % (ue["coefficient"] or 0.0)
H.append(
"""<tr class="formsemestre_status_ue"><td colspan="4">
@ -1113,9 +1096,7 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
else:
fontorange = ""
etat = sco_evaluations.do_evaluation_etat_in_mod(
context, nt, M["moduleimpl_id"]
)
etat = sco_evaluations.do_evaluation_etat_in_mod(nt, M["moduleimpl_id"])
if (
etat["nb_evals_completes"] > 0
and etat["nb_evals_en_cours"] == 0
@ -1183,7 +1164,7 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
H.append("</td></tr>")
H.append("</table></p>")
if sco_preferences.get_preference(context, "use_ue_coefs", formsemestre_id):
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
H.append(
"""
<p class="infop">utilise les coefficients d'UE pour calculer la moyenne générale.</p>
@ -1202,4 +1183,4 @@ def formsemestre_status(context, formsemestre_id=None, REQUEST=None):
'<p><a class="stdlink" href="mailto:?cc=%s">Courrier aux %d enseignants du semestre</a></p>'
% (",".join(adrlist), len(adrlist))
)
return "".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "".join(H) + html_sco_header.sco_footer()

View File

@ -29,6 +29,7 @@
"""
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime
import flask
from flask import url_for, g
import app.scodoc.notesdb as ndb
@ -106,8 +107,6 @@ def formsemestre_validation_etud_form(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Parcours %(nomprenom)s" % etud,
javascripts=["js/recap_parcours.js"],
)
@ -128,7 +127,7 @@ def formsemestre_validation_etud_form(
% (formsemestre_id, etud_index_next, etud_n["nomprenom"])
)
Footer.append("</p>")
Footer.append(html_sco_header.sco_footer(context, REQUEST))
Footer.append(html_sco_header.sco_footer())
H.append('<table style="width: 100%"><tr><td>')
if not check:
@ -224,7 +223,7 @@ def formsemestre_validation_etud_form(
'<a href="formsemestre_validation_suppress_etud?etudid=%s&formsemestre_id=%s" class="stdlink">Supprimer décision existante</a>'
% (etudid, formsemestre_id)
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
# Infos sur decisions déjà saisies
@ -267,7 +266,7 @@ def formsemestre_validation_etud_form(
H.append('<input type="hidden" name="sortcol" value="%s"/>' % sortcol)
H.append("</form></div>")
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
# Explication sur barres actuelles
@ -419,17 +418,17 @@ def _redirect_valid_choice(
adr += "&sortcol=" + sortcol
# if desturl:
# desturl += "&desturl=" + desturl
return REQUEST.RESPONSE.redirect(adr)
return flask.redirect(adr)
# Si le precedent a été modifié, demande relecture du parcours.
# sinon renvoie au listing general,
# if choice.new_code_prev:
# REQUEST.RESPONSE.redirect( 'formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1&desturl=%s' % (formsemestre_id, etudid, desturl) )
# flask.redirect( 'formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1&desturl=%s' % (formsemestre_id, etudid, desturl) )
# else:
# if not desturl:
# desturl = 'formsemestre_recapcomplet?modejury=1&hidemodules=1&formsemestre_id=' + formsemestre_id
# REQUEST.RESPONSE.redirect(desturl)
# flask.redirect(desturl)
def _dispcode(c):
@ -664,9 +663,7 @@ def formsemestre_recap_parcours_table(
H.append("</tr>")
# 3eme ligne: ECTS
if (
sco_preferences.get_preference(
context, "bul_show_ects", sem["formsemestre_id"]
)
sco_preferences.get_preference("bul_show_ects", sem["formsemestre_id"])
or nt.parcours.ECTS_ONLY
):
etud_moy_infos = nt.get_etud_moy_infos(etudid)
@ -853,7 +850,7 @@ def formsemestre_validation_auto(context, formsemestre_id, REQUEST):
</form>
"""
% formsemestre_id,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -923,7 +920,7 @@ def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
"do_formsemestre_validation_auto: %d validations, %d conflicts"
% (nb_valid, len(conflicts))
)
H = [html_sco_header.sco_header(context, REQUEST, page_title="Saisie automatique")]
H = [html_sco_header.sco_header(page_title="Saisie automatique")]
H.append(
"""<h2>Saisie automatique des décisions du semestre %s</h2>
<p>Opération effectuée.</p>
@ -946,7 +943,7 @@ def do_formsemestre_validation_auto(context, formsemestre_id, REQUEST):
'<a href="formsemestre_recapcomplet?formsemestre_id=%s&modejury=1&hidemodules=1&hidebac=1&pref_override=0">continuer</a>'
% formsemestre_id
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -994,8 +991,6 @@ def formsemestre_validate_previous_ue(context, formsemestre_id, etudid, REQUEST=
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Validation UE",
javascripts=["js/validate_previous_ue.js"],
),
@ -1084,15 +1079,9 @@ def formsemestre_validate_previous_ue(context, formsemestre_id, etudid, REQUEST=
<div id="ue_list_code"><!-- filled by ue_sharing_code --></div>
"""
warn, ue_multiples = check_formation_ues(context, Fo["formation_id"])
return (
"\n".join(H)
+ tf[1]
+ X
+ warn
+ html_sco_header.sco_footer(context, REQUEST)
)
return "\n".join(H) + tf[1] + X + warn + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
scu.NotesURL() + "/formsemestre_status?formsemestre_id=" + formsemestre_id
)
else:
@ -1110,7 +1099,7 @@ def formsemestre_validate_previous_ue(context, formsemestre_id, etudid, REQUEST=
semestre_id=semestre_id,
REQUEST=REQUEST,
)
return REQUEST.RESPONSE.redirect(
return flask.redirect(
scu.ScoURL()
+ "/Notes/formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s&head_message=Validation%%20d'UE%%20enregistree"
% (formsemestre_id, etudid)
@ -1158,7 +1147,6 @@ def do_formsemestre_validate_previous_ue(
)
logdb(
REQUEST,
cnx,
method="formsemestre_validate_previous_ue",
etudid=etudid,
@ -1172,7 +1160,6 @@ def do_formsemestre_validate_previous_ue(
def _invalidate_etud_formation_caches(context, etudid, formation_id):
"Invalide tous les semestres de cette formation où l'etudiant est inscrit..."
r = ndb.SimpleDictFetch(
context,
"""SELECT sem.*
FROM notes_formsemestre sem, notes_formsemestre_inscription i
WHERE sem.formation_id = %(formation_id)s
@ -1190,7 +1177,6 @@ def _invalidate_etud_formation_caches(context, etudid, formation_id):
def get_etud_ue_cap_html(context, etudid, formsemestre_id, ue_id, REQUEST=None):
"""Ramene bout de HTML pour pouvoir supprimer une validation de cette UE"""
valids = ndb.SimpleDictFetch(
context,
"""SELECT SFV.* FROM scolar_formsemestre_validation SFV
WHERE ue_id=%(ue_id)s AND etudid=%(etudid)s""",
{"etudid": etudid, "ue_id": ue_id},
@ -1235,7 +1221,7 @@ def etud_ue_suppress_validation(context, etudid, formsemestre_id, ue_id, REQUEST
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
_invalidate_etud_formation_caches(context, etudid, sem["formation_id"])
return REQUEST.RESPONSE.redirect(
return flask.redirect(
scu.NotesURL()
+ "/formsemestre_validate_previous_ue?etudid=%s&formsemestre_id=%s"
% (etudid, formsemestre_id)
@ -1253,7 +1239,6 @@ def check_formation_ues(context, formation_id):
for ue in ues:
# formsemestres utilisant cette ue ?
sems = ndb.SimpleDictFetch(
context,
"""SELECT DISTINCT sem.*
FROM notes_formsemestre sem, notes_modules mod, notes_moduleimpl mi
WHERE sem.formation_id = %(formation_id)s

View File

@ -33,15 +33,18 @@ Optimisation possible:
et éviter ainsi l'appel ulterieur à get_etud_groups() dans _make_table_notes
"""
import time
import collections
import re
import operator
import re
import time
import xml.dom.minidom
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
import flask
from flask import g
from flask import url_for
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import log
@ -95,7 +98,6 @@ group_list = groupEditor.list
def get_group(context, group_id):
"""Returns group object, with partition"""
r = ndb.SimpleDictFetch(
context,
"SELECT gd.*, p.* FROM group_descr gd, partition p WHERE gd.group_id=%(group_id)s AND p.partition_id = gd.partition_id",
{"group_id": group_id},
)
@ -109,18 +111,13 @@ def group_delete(context, group, force=False):
# if not group['group_name'] and not force:
# raise ValueError('cannot suppress this group')
# remove memberships:
ndb.SimpleQuery(
context, "DELETE FROM group_membership WHERE group_id=%(group_id)s", group
)
ndb.SimpleQuery("DELETE FROM group_membership WHERE group_id=%(group_id)s", group)
# delete group:
ndb.SimpleQuery(
context, "DELETE FROM group_descr WHERE group_id=%(group_id)s", group
)
ndb.SimpleQuery("DELETE FROM group_descr WHERE group_id=%(group_id)s", group)
def get_partition(context, partition_id):
r = ndb.SimpleDictFetch(
context,
"SELECT p.* FROM partition p WHERE p.partition_id = %(partition_id)s",
{"partition_id": partition_id},
)
@ -132,7 +129,6 @@ def get_partition(context, partition_id):
def get_partitions_list(context, formsemestre_id, with_default=True):
"""Liste des partitions pour ce semestre (list of dicts)"""
partitions = ndb.SimpleDictFetch(
context,
"SELECT * FROM partition WHERE formsemestre_id=%(formsemestre_id)s order by numero",
{"formsemestre_id": formsemestre_id},
)
@ -146,7 +142,6 @@ def get_partitions_list(context, formsemestre_id, with_default=True):
def get_default_partition(context, formsemestre_id):
"""Get partition for 'all' students (this one always exists, with NULL name)"""
r = ndb.SimpleDictFetch(
context,
"SELECT * FROM partition WHERE formsemestre_id=%(formsemestre_id)s AND partition_name is NULL",
{"formsemestre_id": formsemestre_id},
)
@ -174,16 +169,15 @@ def get_partition_groups(context, partition):
"""List of groups in this partition (list of dicts).
Some groups may be empty."""
return ndb.SimpleDictFetch(
context,
"SELECT gd.*, p.* FROM group_descr gd, partition p WHERE gd.partition_id=%(partition_id)s AND gd.partition_id=p.partition_id ORDER BY group_name",
partition,
)
def get_default_group(context, formsemestre_id, fix_if_missing=False, REQUEST=None):
def get_default_group(formsemestre_id, fix_if_missing=False):
"""Returns group_id for default ('tous') group"""
context = None # #context
r = ndb.SimpleDictFetch(
context,
"SELECT gd.group_id FROM group_descr gd, partition p WHERE p.formsemestre_id=%(formsemestre_id)s AND p.partition_name is NULL AND p.partition_id = gd.partition_id",
{"formsemestre_id": formsemestre_id},
)
@ -200,10 +194,8 @@ def get_default_group(context, formsemestre_id, fix_if_missing=False, REQUEST=No
]
except ScoException:
log("creating default partition for %s" % formsemestre_id)
partition_id = partition_create(
context, formsemestre_id, default=True, REQUEST=REQUEST
)
group_id = createGroup(context, partition_id, default=True, REQUEST=REQUEST)
partition_id = partition_create(context, formsemestre_id, default=True)
group_id = createGroup(context, partition_id, default=True)
return group_id
# debug check
if len(r) != 1:
@ -215,7 +207,6 @@ def get_default_group(context, formsemestre_id, fix_if_missing=False, REQUEST=No
def get_sem_groups(context, formsemestre_id):
"""Returns groups for this sem (in all partitions)."""
return ndb.SimpleDictFetch(
context,
"SELECT gd.*, p.* FROM group_descr gd, partition p WHERE p.formsemestre_id=%(formsemestre_id)s AND p.partition_id = gd.partition_id",
{"formsemestre_id": formsemestre_id},
)
@ -230,7 +221,7 @@ def get_group_members(context, group_id, etat=None):
if etat is not None:
req += " and ins.etat = %(etat)s"
r = ndb.SimpleDictFetch(context, req, {"group_id": group_id, "etat": etat})
r = ndb.SimpleDictFetch(req, {"group_id": group_id, "etat": etat})
for etud in r:
sco_etud.format_etud_ident(etud)
@ -329,7 +320,6 @@ def get_etud_groups(context, etudid, sem, exclude_default=False):
if exclude_default:
req += " and p.partition_name is not NULL"
groups = ndb.SimpleDictFetch(
context,
req + " ORDER BY p.numero",
{"etudid": etudid, "formsemestre_id": sem["formsemestre_id"]},
)
@ -342,7 +332,7 @@ def get_etud_main_group(context, etudid, sem):
if groups:
return groups[0]
else:
return get_group(context, get_default_group(context, sem["formsemestre_id"]))
return get_group(context, get_default_group(sem["formsemestre_id"]))
def formsemestre_get_main_partition(context, formsemestre_id):
@ -357,7 +347,6 @@ def formsemestre_get_etud_groupnames(context, formsemestre_id, attr="group_name"
{ etudid : { partition_id : group_name }} (attr=group_name or group_id)
"""
infos = ndb.SimpleDictFetch(
context,
"select i.etudid, p.partition_id, gd.group_name, gd.group_id from notes_formsemestre_inscription i, partition p, group_descr gd, group_membership gm where i.formsemestre_id=%(formsemestre_id)s and i.formsemestre_id=p.formsemestre_id and p.partition_id=gd.partition_id and gm.etudid=i.etudid and gm.group_id = gd.group_id and p.partition_name is not NULL",
{"formsemestre_id": formsemestre_id},
)
@ -380,7 +369,6 @@ def etud_add_group_infos(context, etud, sem, sep=" "):
return etud
infos = ndb.SimpleDictFetch(
context,
"SELECT p.partition_name, g.* from group_descr g, partition p, group_membership gm WHERE gm.etudid=%(etudid)s and gm.group_id = g.group_id and g.partition_id = p.partition_id and p.formsemestre_id = %(formsemestre_id)s ORDER BY p.numero",
{"etudid": etud["etudid"], "formsemestre_id": sem["formsemestre_id"]},
)
@ -407,7 +395,6 @@ def etud_add_group_infos(context, etud, sem, sep=" "):
def get_etud_groups_in_partition(context, partition_id):
"""Returns { etudid : group }, with all students in this partition"""
infos = ndb.SimpleDictFetch(
context,
"SELECT gd.*, etudid from group_descr gd, group_membership gm where gd.partition_id = %(partition_id)s and gm.group_id = gd.group_id",
{"partition_id": partition_id},
)
@ -535,7 +522,6 @@ def set_group(context, etudid, group_id):
args = {"etudid": etudid, "group_id": group_id}
# déjà inscrit ?
r = ndb.SimpleDictFetch(
context,
"SELECT * FROM group_membership gm WHERE etudid=%(etudid)s and group_id=%(group_id)s",
args,
cursor=cursor,
@ -544,7 +530,6 @@ def set_group(context, etudid, group_id):
return False
# inscrit
ndb.SimpleQuery(
context,
"INSERT INTO group_membership (etudid, group_id) VALUES (%(etudid)s, %(group_id)s)",
args,
cursor=cursor,
@ -571,7 +556,6 @@ def change_etud_group_in_partition(
partition = get_partition(context, group["partition_id"])
# 1- Supprime membership dans cette partition
ndb.SimpleQuery(
context,
"""DELETE FROM group_membership WHERE group_membership_id IN
(SELECT gm.group_membership_id
FROM group_membership gm, group_descr gd
@ -587,7 +571,6 @@ def change_etud_group_in_partition(
if REQUEST:
cnx = ndb.GetDBConnexion()
logdb(
REQUEST,
cnx,
method="changeGroup",
etudid=etudid,
@ -618,7 +601,7 @@ def setGroups(
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
log("***setGroups: partition_id=%s" % partition_id)
log("groupsLists=%s" % groupsLists)
@ -660,13 +643,11 @@ def setGroups(
for etudid in old_members_set:
log("removing %s from group %s" % (etudid, group_id))
ndb.SimpleQuery(
context,
"DELETE FROM group_membership WHERE etudid=%(etudid)s and group_id=%(group_id)s",
{"etudid": etudid, "group_id": group_id},
cursor=cursor,
)
logdb(
REQUEST,
cnx,
method="removeFromGroup",
etudid=etudid,
@ -688,7 +669,7 @@ def setGroups(
# group_name = six.text_type(group_name, "utf-8").encode(
# scu.SCO_ENCODING
# ) # #py3 #sco8
group_id = createGroup(context, partition_id, group_name, REQUEST=REQUEST)
group_id = createGroup(context, partition_id, group_name)
# Place dans ce groupe les etudiants indiqués:
for etudid in fs[1:-1]:
change_etud_group_in_partition(
@ -701,15 +682,11 @@ def setGroups(
)
def createGroup(context, partition_id, group_name="", default=False, REQUEST=None):
"""Create a new group in this partition
(called from JS)
"""
def createGroup(context, partition_id, group_name="", default=False):
"""Create a new group in this partition"""
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if REQUEST and not sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
#
if group_name:
@ -746,9 +723,7 @@ def suppressGroup(context, group_id, partition_id=None, REQUEST=None):
else:
partition_id = group["partition_id"]
partition = get_partition(context, partition_id)
if not sco_permissions_check.can_change_groups(
context, REQUEST, partition["formsemestre_id"]
):
if not sco_permissions_check.can_change_groups(partition["formsemestre_id"]):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
log(
"suppressGroup: group_id=%s group_name=%s partition_name=%s"
@ -763,13 +738,10 @@ def partition_create(
partition_name="",
default=False,
numero=None,
REQUEST=None,
redirect=1,
):
"""Create a new partition"""
if REQUEST and not sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if partition_name:
partition_name = partition_name.strip()
@ -793,14 +765,18 @@ def partition_create(
log("createPartition: created partition_id=%s" % partition_id)
#
if redirect:
return REQUEST.RESPONSE.redirect(
"editPartitionForm?formsemestre_id=" + formsemestre_id
return flask.redirect(
url_for(
"scolar.editPartitionForm",
scodoc_dept=g.scodoc_dept,
formsemestre_id=formsemestre_id,
)
)
else:
return partition_id
def getArrowIconsTags(context, REQUEST):
def getArrowIconsTags():
"""returns html tags for arrows"""
#
arrow_up = scu.icontag("arrow_up", title="remonter")
@ -813,18 +789,16 @@ def getArrowIconsTags(context, REQUEST):
def editPartitionForm(context, formsemestre_id=None, REQUEST=None):
"""Form to create/suppress partitions"""
# ad-hoc form
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
partitions = get_partitions_list(context, formsemestre_id)
arrow_up, arrow_down, arrow_none = getArrowIconsTags(context, REQUEST)
arrow_up, arrow_down, arrow_none = getArrowIconsTags()
suppricon = scu.icontag(
"delete_small_img", border="0", alt="supprimer", title="Supprimer"
)
#
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Partitions...",
javascripts=["js/editPartitionForm.js"],
),
@ -933,7 +907,7 @@ def editPartitionForm(context, formsemestre_id=None, REQUEST=None):
</div>
"""
)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def partition_set_attr(context, partition_id, attr, value, REQUEST=None):
@ -943,7 +917,7 @@ def partition_set_attr(context, partition_id, attr, value, REQUEST=None):
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
log("partition_set_attr(%s, %s, %s)" % (partition_id, attr, value))
@ -966,7 +940,7 @@ def partition_delete(
default partition cannot be suppressed (unless force)"""
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
if not partition["partition_name"] and not force:
@ -981,13 +955,11 @@ def partition_delete(
else:
grnames = ""
return scu.confirm_dialog(
context,
"""<h2>Supprimer la partition "%s" ?</h2>
<p>Les groupes %s de cette partition seront supprimés</p>
"""
% (partition["partition_name"], grnames),
dest_url="",
REQUEST=REQUEST,
cancel_url="editPartitionForm?formsemestre_id=%s" % formsemestre_id,
parameters={"redirect": redirect, "partition_id": partition_id},
)
@ -1001,16 +973,14 @@ def partition_delete(
# redirect to partition edit page:
if redirect:
return REQUEST.RESPONSE.redirect(
"editPartitionForm?formsemestre_id=" + formsemestre_id
)
return flask.redirect("editPartitionForm?formsemestre_id=" + formsemestre_id)
def partition_move(context, partition_id, after=0, REQUEST=None, redirect=1):
"""Move before/after previous one (decrement/increment numero)"""
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
#
redirect = int(redirect)
@ -1036,16 +1006,14 @@ def partition_move(context, partition_id, after=0, REQUEST=None, redirect=1):
# redirect to partition edit page:
if redirect:
return REQUEST.RESPONSE.redirect(
"editPartitionForm?formsemestre_id=" + formsemestre_id
)
return flask.redirect("editPartitionForm?formsemestre_id=" + formsemestre_id)
def partition_rename(context, partition_id, REQUEST=None):
"""Form to rename a partition"""
partition = get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
H = ["<h2>Renommer une partition</h2>"]
tf = TrivialFormulator(
@ -1068,16 +1036,14 @@ def partition_rename(context, partition_id, REQUEST=None):
)
if tf[0] == 0:
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "\n".join(H)
+ "\n"
+ tf[1]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
"editPartitionForm?formsemestre_id=" + formsemestre_id
)
return flask.redirect("editPartitionForm?formsemestre_id=" + formsemestre_id)
else:
# form submission
return partition_set_name(
@ -1097,7 +1063,6 @@ def partition_set_name(context, partition_id, partition_name, REQUEST=None, redi
# check unicity
r = ndb.SimpleDictFetch(
context,
"SELECT p.* FROM partition p WHERE p.partition_name = %(partition_name)s AND formsemestre_id = %(formsemestre_id)s",
{"partition_name": partition_name, "formsemestre_id": formsemestre_id},
)
@ -1106,7 +1071,7 @@ def partition_set_name(context, partition_id, partition_name, REQUEST=None, redi
"Partition %s déjà existante dans ce semestre !" % partition_name
)
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
redirect = int(redirect)
cnx = ndb.GetDBConnexion()
@ -1116,9 +1081,7 @@ def partition_set_name(context, partition_id, partition_name, REQUEST=None, redi
# redirect to partition edit page:
if redirect:
return REQUEST.RESPONSE.redirect(
"editPartitionForm?formsemestre_id=" + formsemestre_id
)
return flask.redirect("editPartitionForm?formsemestre_id=" + formsemestre_id)
def group_set_name(context, group_id, group_name, REQUEST=None, redirect=1):
@ -1131,7 +1094,7 @@ def group_set_name(context, group_id, group_name, REQUEST=None, redirect=1):
if group["group_name"] is None:
raise ValueError("can't set a name to default group")
formsemestre_id = group["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
redirect = int(redirect)
cnx = ndb.GetDBConnexion()
@ -1139,16 +1102,14 @@ def group_set_name(context, group_id, group_name, REQUEST=None, redirect=1):
# redirect to partition edit page:
if redirect:
return REQUEST.RESPONSE.redirect(
"affectGroups?partition_id=" + group["partition_id"]
)
return flask.redirect("affectGroups?partition_id=" + group["partition_id"])
def group_rename(context, group_id, REQUEST=None):
"""Form to rename a group"""
group = get_group(context, group_id)
formsemestre_id = group["formsemestre_id"]
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
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(
@ -1171,16 +1132,14 @@ def group_rename(context, group_id, REQUEST=None):
)
if tf[0] == 0:
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "\n".join(H)
+ "\n"
+ tf[1]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
"affectGroups?partition_id=" + group["partition_id"]
)
return flask.redirect("affectGroups?partition_id=" + group["partition_id"])
else:
# form submission
return group_set_name(
@ -1198,7 +1157,7 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None):
formsemestre_id = partition["formsemestre_id"]
# renvoie sur page édition groupes
dest_url = "affectGroups?partition_id=%s" % partition_id
if not sco_permissions_check.can_change_groups(context, REQUEST, formsemestre_id):
if not sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
@ -1216,9 +1175,7 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None):
]
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Répartition des groupes"
),
html_sco_header.sco_header(page_title="Répartition des groupes"),
"<h2>Répartition des groupes de %s</h2>" % partition["partition_name"],
"<p>Semestre %s</p>" % sem["titreannee"],
"""<p class="help">Les groupes existants seront <b>effacés</b> et remplacés par
@ -1238,11 +1195,9 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None):
name="tf",
)
if tf[0] == 0:
return (
"\n".join(H) + "\n" + tf[1] + html_sco_header.sco_footer(context, REQUEST)
)
return "\n".join(H) + "\n" + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
else:
# form submission
log(
@ -1261,10 +1216,8 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None):
# checkGroupName(group_name)
# except:
# H.append('<p class="warning">Nom de groupe invalide: %s</p>'%group_name)
# return '\n'.join(H) + tf[1] + html_sco_header.sco_footer(context, REQUEST)
group_ids.append(
createGroup(context, partition_id, group_name, REQUEST=REQUEST)
)
# return '\n'.join(H) + tf[1] + html_sco_header.sco_footer( REQUEST)
group_ids.append(createGroup(context, partition_id, group_name))
#
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > identdict
identdict = nt.identdict
@ -1294,7 +1247,7 @@ def groups_auto_repartition(context, partition_id=None, REQUEST=None):
context, etudid, group_id, partition, REQUEST=REQUEST
)
log("%s in group %s" % (etudid, group_id))
return REQUEST.RESPONSE.redirect(dest_url)
return flask.redirect(dest_url)
def get_prev_moy(context, etudid, formsemestre_id):
@ -1362,7 +1315,7 @@ def create_etapes_partition(context, formsemestre_id, partition_name="apo_etapes
def do_evaluation_listeetuds_groups(
context, evaluation_id, groups=None, getallstudents=False, include_dems=False
evaluation_id, groups=None, getallstudents=False, include_dems=False
):
"""Donne la liste des etudids inscrits a cette evaluation dans les
groupes indiqués.

View File

@ -40,15 +40,11 @@ def affectGroups(context, partition_id, REQUEST=None):
# Ported from DTML and adapted to new group management (nov 2009)
partition = sco_groups.get_partition(context, partition_id)
formsemestre_id = partition["formsemestre_id"]
if not sco_groups.sco_permissions_check.can_change_groups(
context, REQUEST, formsemestre_id
):
if not sco_groups.sco_permissions_check.can_change_groups(formsemestre_id):
raise AccessDenied("vous n'avez pas la permission d'effectuer cette opération")
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Affectation aux groupes",
javascripts=["js/groupmgr.js"],
cssstyles=["css/groups.css"],
@ -97,6 +93,6 @@ Editer groupes de
</div>
""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)

File diff suppressed because it is too large Load Diff

View File

@ -32,10 +32,14 @@ import collections
import os
import re
import time
from datetime import date
import flask
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import log
from app.scodoc.sco_excel import COLORS
from app.scodoc.sco_formsemestre_inscriptions import (
do_formsemestre_inscription_with_modules,
)
@ -163,8 +167,8 @@ def sco_import_generate_excel_sample(
(only columns from these tables will be generated)
If group_ids, liste les etudiants de ces groupes
"""
style = sco_excel.Excel_MakeStyle(bold=True)
style_required = sco_excel.Excel_MakeStyle(bold=True, color="red")
style = sco_excel.excel_make_style(bold=True)
style_required = sco_excel.excel_make_style(bold=True, color=COLORS.RED)
titles = []
titlesStyles = []
for l in fmt:
@ -213,8 +217,8 @@ def sco_import_generate_excel_sample(
lines.append(l)
else:
lines = [[]] # empty content, titles only
return sco_excel.Excel_SimpleTable(
titles=titles, titlesStyles=titlesStyles, SheetName="Etudiants", lines=lines
return sco_excel.excel_simple_table(
titles=titles, titles_styles=titlesStyles, sheet_name="Etudiants", lines=lines
)
@ -238,19 +242,17 @@ def students_import_excel(
)
if REQUEST:
if formsemestre_id:
dest = "formsemestre_status?formsemestre_id=%s" % formsemestre_id
dest = "Notes/formsemestre_status?formsemestre_id=%s" % formsemestre_id
else:
dest = scu.NotesURL()
H = [
html_sco_header.sco_header(context, REQUEST, page_title="Import etudiants")
]
H = [html_sco_header.sco_header(page_title="Import etudiants")]
H.append("<ul>")
for d in diag:
H.append("<li>%s</li>" % d)
H.append("</ul>")
H.append("<p>Import terminé !</p>")
H.append('<p><a class="stdlink" href="%s">Continuer</a></p>' % dest)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def scolars_import_excel_file(
@ -269,16 +271,15 @@ def scolars_import_excel_file(
cnx = ndb.GetDBConnexion(autocommit=False)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
annee_courante = time.localtime()[0]
always_require_ine = sco_preferences.get_preference(context, "always_require_ine")
always_require_ine = sco_preferences.get_preference("always_require_ine")
exceldata = datafile.read()
if not exceldata:
raise ScoValueError("Ficher excel vide ou invalide")
diag, data = sco_excel.Excel_to_list(exceldata)
diag, data = sco_excel.excel_bytes_to_list(exceldata)
if not data: # probably a bug
raise ScoException("scolars_import_excel_file: empty file !")
formsemestre_to_invalidate = set()
# 1- --- check title line
titles = {}
fmt = sco_import_format()
@ -380,8 +381,8 @@ def scolars_import_excel_file(
# Excel date conversion:
if scu.strlower(titleslist[i]) == "date_naissance":
if val:
if re.match(r"^[0-9]*\.?[0-9]*$", str(val)):
val = sco_excel.xldate_as_datetime(float(val))
# if re.match(r"^[0-9]*\.?[0-9]*$", str(val)):
val = sco_excel.xldate_as_datetime(val)
# INE
if (
scu.strlower(titleslist[i]) == "code_ine"
@ -475,8 +476,6 @@ def scolars_import_excel_file(
diag.append("Import et inscription de %s étudiants" % len(created_etudids))
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_INSCR,
text="Inscription de %d étudiants" # peuvent avoir ete inscrits a des semestres differents
% len(created_etudids),
@ -505,11 +504,7 @@ def students_import_admission(
type_admission=type_admission,
)
if REQUEST:
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Import données admissions"
)
]
H = [html_sco_header.sco_header(page_title="Import données admissions")]
H.append("<p>Import terminé !</p>")
H.append(
'<p><a class="stdlink" href="%s">Continuer</a></p>'
@ -519,7 +514,7 @@ def students_import_admission(
if diag:
H.append("<p>Diagnostic: <ul><li>%s</li></ul></p>" % "</li><li>".join(diag))
return "\n".join(H) + html_sco_header.sco_footer(REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()
def _import_one_student(
@ -620,7 +615,7 @@ def scolars_import_admission(
log("scolars_import_admission: formsemestre_id=%s" % formsemestre_id)
members = sco_groups.get_group_members(
context, sco_groups.get_default_group(context, formsemestre_id)
context, sco_groups.get_default_group(formsemestre_id)
)
etuds_by_nomprenom = {} # { nomprenom : etud }
diag = []
@ -633,7 +628,7 @@ def scolars_import_admission(
etuds_by_nomprenom[np] = m
exceldata = datafile.read()
diag2, data = sco_excel.Excel_to_list(exceldata, convert_to_string=False)
diag2, data = sco_excel.excel_bytes_to_list(exceldata)
if not data:
raise ScoException("scolars_import_admission: empty file !")
diag += diag2
@ -822,8 +817,6 @@ def adm_table_description_format(context):
rows=list(Fmt.values()),
html_sortable=True,
html_class="table_leftalign",
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab

View File

@ -46,11 +46,11 @@ TITLES = ("user_name", "nom", "prenom", "email", "roles", "dept")
def generate_excel_sample():
"""generates an excel document suitable to import users"""
style = sco_excel.Excel_MakeStyle(bold=True)
style = sco_excel.excel_make_style(bold=True)
titles = TITLES
titlesStyles = [style] * len(titles)
return sco_excel.Excel_SimpleTable(
titles=titles, titlesStyles=titlesStyles, SheetName="Utilisateurs ScoDoc"
return sco_excel.excel_simple_table(
titles=titles, titlesStyles=titlesStyles, sheet_name="Utilisateurs ScoDoc"
)
@ -225,7 +225,7 @@ Pour plus d'informations sur ce logiciel, voir %s
msg["Subject"] = Header("Mot de passe ScoDoc", scu.SCO_ENCODING)
else:
msg["Subject"] = Header("Votre accès ScoDoc", scu.SCO_ENCODING)
msg["From"] = sco_preferences.get_preference(context, "email_from_addr")
msg["From"] = sco_preferences.get_preference("email_from_addr")
msg["To"] = u["email"]
msg.epilogue = ""
txt = MIMEText(txt, "plain", scu.SCO_ENCODING)

View File

@ -287,10 +287,8 @@ def formsemestre_inscr_passage(
# -- check lock
if sem["etat"] != "1":
raise ScoValueError("opération impossible: semestre verrouille")
header = html_sco_header.sco_header(
context, REQUEST, page_title="Passage des étudiants"
)
footer = html_sco_header.sco_footer(context, REQUEST)
header = html_sco_header.sco_header(page_title="Passage des étudiants")
footer = html_sco_header.sco_footer()
H = [header]
if type(etuds) == type(""):
etuds = etuds.split(",") # vient du form de confirmation
@ -352,7 +350,6 @@ def formsemestre_inscr_passage(
H.append("""<h3>Il n'y a rien à modifier !</h3>""")
H.append(
scu.confirm_dialog(
context,
dest_url="formsemestre_inscr_passage",
add_headers=False,
cancel_url="formsemestre_inscr_passage?formsemestre_id="
@ -364,7 +361,6 @@ def formsemestre_inscr_passage(
"inscrit_groupes": inscrit_groupes,
"submitted": 1,
},
REQUEST=REQUEST,
)
)
else:
@ -643,8 +639,6 @@ def etuds_select_box_xls(context, src_cat):
columns_ids=columns_ids,
rows=etuds,
caption="%(title)s. %(help)s" % src_cat["infos"],
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
return tab.excel()

View File

@ -30,6 +30,7 @@
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
from operator import itemgetter
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -64,15 +65,11 @@ def do_evaluation_listenotes(context, REQUEST):
if "evaluation_id" in REQUEST.form:
evaluation_id = REQUEST.form["evaluation_id"]
mode = "eval"
evals = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": evaluation_id}
)
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if "moduleimpl_id" in REQUEST.form:
moduleimpl_id = REQUEST.form["moduleimpl_id"]
mode = "module"
evals = sco_evaluations.do_evaluation_list(
context, {"moduleimpl_id": moduleimpl_id}
)
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
if not mode:
raise ValueError("missing argument: evaluation or module")
if not evals:
@ -84,7 +81,7 @@ def do_evaluation_listenotes(context, REQUEST):
if mode == "eval":
H = [
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
evaluation_id=evaluation_id, REQUEST=REQUEST
)
]
else:
@ -196,7 +193,7 @@ def do_evaluation_listenotes(context, REQUEST):
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1]
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"%s/Notes/moduleimpl_status?moduleimpl_id=%s"
% (scu.ScoURL(), E["moduleimpl_id"])
)
@ -250,14 +247,14 @@ def _make_table_notes(
keep_numeric = False
# Si pas de groupe, affiche tout
if not group_ids:
group_ids = [sco_groups.get_default_group(context, M["formsemestre_id"])]
group_ids = [sco_groups.get_default_group(M["formsemestre_id"])]
groups = sco_groups.listgroups(context, group_ids)
gr_title = sco_groups.listgroups_abbrev(groups)
gr_title_filename = sco_groups.listgroups_filename(groups)
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, E["evaluation_id"], groups, include_dems=True
E["evaluation_id"], groups, include_dems=True
)
if anonymous_listing:
@ -314,9 +311,9 @@ def _make_table_notes(
grc = inscr["etat"]
code = "" # code pour listings anonyme, à la place du nom
if sco_preferences.get_preference(context, "anonymous_lst_code") == "INE":
if sco_preferences.get_preference("anonymous_lst_code") == "INE":
code = etud["code_ine"]
elif sco_preferences.get_preference(context, "anonymous_lst_code") == "NIP":
elif sco_preferences.get_preference("anonymous_lst_code") == "NIP":
code = etud["code_nip"]
if not code: # laisser le code vide n'aurait aucun sens, prenons l'etudid
code = etudid
@ -367,9 +364,7 @@ def _make_table_notes(
}
# Ajoute les notes de chaque évaluation:
for e in evals:
e["eval_state"] = sco_evaluations.do_evaluation_etat(
context, e["evaluation_id"]
)
e["eval_state"] = sco_evaluations.do_evaluation_etat(e["evaluation_id"])
notes, nb_abs, nb_att = _add_eval_columns(
context,
e,
@ -485,7 +480,7 @@ def _make_table_notes(
html_title=html_title,
pdf_title=pdf_title,
html_class="table_leftalign notes_evaluation",
preferences=sco_preferences.SemPreferences(context, M["formsemestre_id"]),
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
# html_generate_cells=False # la derniere ligne (moyennes) est incomplete
)
@ -534,7 +529,7 @@ def _make_table_notes(
return (
sco_evaluations.evaluation_describe(
context, evaluation_id=E["evaluation_id"], REQUEST=REQUEST
evaluation_id=E["evaluation_id"], REQUEST=REQUEST
)
+ eval_info
+ html_form
@ -553,7 +548,7 @@ def _add_eval_columns(
sum_notes = 0
notes = [] # liste des notes numeriques, pour calcul histogramme uniquement
evaluation_id = e["evaluation_id"]
NotesDB = sco_evaluations.do_evaluation_get_all_notes(context, evaluation_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
for row in rows:
etudid = row["etudid"]
if etudid in NotesDB:
@ -717,12 +712,12 @@ def evaluation_check_absences(context, evaluation_id):
EXC et pas justifie
Ramene 3 listes d'etudid
"""
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
if not E["jour"]:
return [], [], [], [], [] # evaluation sans date
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True
evaluation_id, getallstudents=True
)
am, pm, demijournee = _eval_demijournee(E)
@ -738,7 +733,7 @@ def evaluation_check_absences(context, evaluation_id):
Justs = set([x["etudid"] for x in Just]) # ensemble des etudiants avec justif
# Les notes:
NotesDB = sco_evaluations.do_evaluation_get_all_notes(context, evaluation_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
ValButAbs = [] # une note mais noté absent
AbsNonSignalee = [] # note ABS mais pas noté absent
ExcNonSignalee = [] # note EXC mais pas noté absent
@ -773,7 +768,7 @@ def evaluation_check_absences_html(
):
"""Affiche etat verification absences d'une evaluation"""
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
am, pm, demijournee = _eval_demijournee(E)
(
@ -790,7 +785,7 @@ def evaluation_check_absences_html(
context, REQUEST, "Vérification absences à l'évaluation"
),
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
evaluation_id=evaluation_id, REQUEST=REQUEST
),
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
]
@ -867,7 +862,7 @@ def evaluation_check_absences_html(
etudlist(AbsButExc)
if with_header:
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -892,7 +887,7 @@ def formsemestre_check_absences_html(context, formsemestre_id, REQUEST=None):
)
for M in Mlist:
evals = sco_evaluations.do_evaluation_list(
context, {"moduleimpl_id": M["moduleimpl_id"]}
{"moduleimpl_id": M["moduleimpl_id"]}
)
if evals:
H.append(
@ -911,5 +906,5 @@ def formsemestre_check_absences_html(context, formsemestre_id, REQUEST=None):
)
if evals:
H.append("</div>")
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)

View File

@ -59,7 +59,7 @@ def formsemestre_table_etuds_lycees(
etuds,
group_lycees,
title,
sco_preferences.SemPreferences(context, formsemestre_id),
sco_preferences.SemPreferences(formsemestre_id),
)
@ -75,9 +75,7 @@ def scodoc_table_etuds_lycees(context, format="html", REQUEST=None):
etuds,
False,
"Lycées de TOUS les étudiants",
sco_preferences.SemPreferences(
context,
),
sco_preferences.SemPreferences(),
no_links=True,
)
tab.base_url = REQUEST.URL0
@ -86,8 +84,6 @@ def scodoc_table_etuds_lycees(context, format="html", REQUEST=None):
return t
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=tab.page_title,
init_google_maps=True,
init_qtip=True,
@ -99,7 +95,7 @@ def scodoc_table_etuds_lycees(context, format="html", REQUEST=None):
"""<div id="lyc_map_canvas"></div>
""",
js_coords_lycees(etuds_by_lycee),
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -201,8 +197,6 @@ def formsemestre_etuds_lycees(
]
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=tab.page_title,
init_google_maps=True,
init_qtip=True,
@ -214,7 +208,7 @@ def formsemestre_etuds_lycees(
"""<div id="lyc_map_canvas"></div>
""",
js_coords_lycees(etuds_by_lycee),
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)

View File

@ -222,7 +222,6 @@ def do_moduleimpl_inscription_create(context, args, REQUEST=None, formsemestre_i
) # > moduleimpl_inscription
if REQUEST:
scolog.logdb(
REQUEST,
cnx,
method="moduleimpl_inscription",
etudid=args["etudid"],

View File

@ -29,6 +29,7 @@
"""
from operator import itemgetter
import flask
from flask import url_for, g
import app.scodoc.notesdb as ndb
@ -71,13 +72,11 @@ def moduleimpl_inscriptions_edit(
if sem["etat"] != "1":
raise ScoValueError("opération impossible: semestre verrouille")
header = html_sco_header.sco_header(
context,
REQUEST,
page_title="Inscription au module",
init_qtip=True,
javascripts=["js/etud_info.js"],
)
footer = html_sco_header.sco_footer(context, REQUEST)
footer = html_sco_header.sco_footer()
H = [
header,
"""<h2>Inscriptions au module <a href="moduleimpl_status?moduleimpl_id=%s">%s</a> (%s)</a></h2>
@ -202,9 +201,7 @@ def moduleimpl_inscriptions_edit(
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
context, moduleimpl_id, formsemestre_id, etuds, reset=True, REQUEST=REQUEST
)
return REQUEST.RESPONSE.redirect(
"moduleimpl_status?moduleimpl_id=%s" % (moduleimpl_id)
)
return flask.redirect("moduleimpl_status?moduleimpl_id=%s" % (moduleimpl_id))
#
H.append(footer)
return "\n".join(H)
@ -425,7 +422,7 @@ def moduleimpl_inscriptions_stats(context, formsemestre_id, REQUEST=None):
"""
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -521,7 +518,6 @@ def is_inscrit_ue(context, etudid, formsemestre_id, ue_id):
auxquels l'étudiant est inscrit.
"""
r = ndb.SimpleDictFetch(
context,
"""SELECT mod.*
FROM notes_moduleimpl mi, notes_modules mod,
notes_formsemestre sem, notes_moduleimpl_inscription i
@ -560,7 +556,6 @@ def do_etud_desinscrit_ue(context, etudid, formsemestre_id, ue_id, REQUEST=None)
)
if REQUEST:
logdb(
REQUEST,
cnx,
method="etud_desinscrit_ue",
etudid=etudid,

View File

@ -31,6 +31,7 @@ import time
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
from flask import g, url_for
from flask_login import current_user
import app.scodoc.sco_utils as scu
from app.scodoc.sco_permissions import Permission
@ -55,16 +56,16 @@ from app.scodoc import sco_users
# menu evaluation dans moduleimpl
def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
"Menu avec actions sur une evaluation"
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
modimpl = sco_moduleimpl.do_moduleimpl_list(
context, moduleimpl_id=E["moduleimpl_id"]
)[0]
group_id = sco_groups.get_default_group(context, modimpl["formsemestre_id"])
group_id = sco_groups.get_default_group(modimpl["formsemestre_id"])
if (
sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
)
and nbnotes != 0
):
@ -80,7 +81,7 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
),
},
{
@ -90,7 +91,7 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -101,7 +102,7 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
},
"enabled": nbnotes == 0
and sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -111,7 +112,7 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
"evaluation_id": evaluation_id,
},
"enabled": sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
),
},
{
@ -130,7 +131,7 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
},
"enabled": nbnotes == 0
and sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
),
},
{
@ -157,7 +158,6 @@ def moduleimpl_evaluation_menu(context, evaluation_id, nbnotes=0, REQUEST=None):
def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=None):
"""Tableau de bord module (liste des evaluations etc)"""
authuser = REQUEST.AUTHENTICATED_USER
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0]
formsemestre_id = M["formsemestre_id"]
Mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
@ -170,26 +170,20 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
)
nt = sco_cache.NotesTableCache.get(formsemestre_id)
ModEvals = sco_evaluations.do_evaluation_list(
context, {"moduleimpl_id": moduleimpl_id}
)
ModEvals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
ModEvals.sort(
key=lambda x: (x["numero"], x["jour"], x["heure_debut"]), reverse=True
) # la plus RECENTE en tête
#
caneditevals = sco_permissions_check.can_edit_notes(
context, authuser, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"]
current_user, moduleimpl_id, allow_ens=sem["ens_can_edit_eval"]
)
caneditnotes = sco_permissions_check.can_edit_notes(
context, authuser, moduleimpl_id
)
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags(context, REQUEST)
caneditnotes = sco_permissions_check.can_edit_notes(current_user, moduleimpl_id)
arrow_up, arrow_down, arrow_none = sco_groups.getArrowIconsTags()
#
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Module %(titre)s" % Mod
),
html_sco_header.sco_header(page_title="Module %(titre)s" % Mod),
"""<h2 class="formsemestre">Module <tt>%(code)s</tt> %(titre)s</h2>""" % Mod,
# XXX """caneditevals=%s caneditnotes=%s""" % (caneditevals,caneditnotes),
"""<div class="moduleimpl_tableaubord">
@ -244,7 +238,7 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
"""<tr><td class="fichetitre2">Inscrits: </td><td> %d étudiants"""
% len(ModInscrits)
)
if authuser.has_permission(Permission.ScoEtudInscrit):
if current_user.has_permission(Permission.ScoEtudInscrit):
H.append(
"""<a class="stdlink" style="margin-left:2em;" href="moduleimpl_inscriptions_edit?moduleimpl_id=%s">modifier</a>"""
% M["moduleimpl_id"]
@ -283,11 +277,11 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
)
# Adapté à partir d'une suggestion de DS (Le Havre)
# Liens saisies absences seulement si permission et date courante dans le semestre
if authuser.has_permission(
if current_user.has_permission(
Permission.ScoAbsChange
) and sco_formsemestre.sem_est_courant(context, sem):
datelundi = sco_abs.ddmmyyyy(time.strftime("%d/%m/%Y")).prev_monday()
group_id = sco_groups.get_default_group(context, formsemestre_id)
group_id = sco_groups.get_default_group(formsemestre_id)
H.append(
f"""
<span class="moduleimpl_abs_link"><a class="stdlink"
@ -369,7 +363,6 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
first = True
for eval in ModEvals:
etat = sco_evaluations.do_evaluation_etat(
context,
eval["evaluation_id"],
partition_id=partition_id,
select_first_partition=True,
@ -654,5 +647,5 @@ def moduleimpl_status(context, moduleimpl_id=None, partition_id=None, REQUEST=No
scu.icontag("status_visible_img"),
)
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "".join(H)

View File

@ -31,16 +31,14 @@ import datetime
import re
import time
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
from io import StringIO
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from operator import itemgetter
import six
import PyRSS2Gen # pylint: disable=import-error
from flask_login import current_user
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
@ -83,12 +81,12 @@ scolar_news_list = _scolar_news_editor.list
_LAST_NEWS = {} # { (authuser_name, type, object) : time }
def add(context, REQUEST, typ, object=None, text="", url=None, max_frequency=False):
def add(typ, object=None, text="", url=None, max_frequency=False):
"""Ajoute une nouvelle.
Si max_frequency, ne genere pas 2 nouvelles identiques à moins de max_frequency
secondes d'intervalle.
"""
authuser_name = str(REQUEST.AUTHENTICATED_USER)
authuser_name = current_user.user_name
cnx = ndb.GetDBConnexion()
args = {
"authenticated_user": authuser_name,
@ -98,19 +96,19 @@ def add(context, REQUEST, typ, object=None, text="", url=None, max_frequency=Fal
"text": text,
"url": url,
}
log("news: %s" % args)
t = time.time()
if max_frequency:
last_news_time = _LAST_NEWS.get((authuser_name, typ, object), False)
if last_news_time and (t - last_news_time < max_frequency):
log("not recording")
# log("not recording")
return
log("news: %s" % args)
_LAST_NEWS[(authuser_name, typ, object)] = t
_send_news_by_mail(context, args)
return scolar_news_create(cnx, args, has_uniq_values=False)
_send_news_by_mail(args)
return scolar_news_create(cnx, args)
def scolar_news_summary(context, n=5):
@ -141,7 +139,6 @@ def scolar_news_summary(context, n=5):
n["date822"] = n["date"].strftime("%a, %d %b %Y %H:%M:%S %z")
# heure
n["hm"] = n["date"].strftime("%Hh%M")
n["rssdate"] = n["date"].strftime("%d/%m %Hh%M") # pour affichage
for k in n.keys():
if n[k] is None:
n[k] = ""
@ -198,14 +195,12 @@ def _get_formsemestre_infos_from_news(context, n):
return {"formsemestre_id": formsemestre_id, "sem": sem, "descr_sem": descr_sem}
def scolar_news_summary_html(context, n=5, rssicon=None):
def scolar_news_summary_html(context, n=5):
"""News summary, formated in HTML"""
news = scolar_news_summary(context, n=n)
if not news:
return ""
H = ['<div class="news"><span class="newstitle">Dernières opérations']
if rssicon: # 2020-12-30 plus utilisé
H.append('<a href="rssnews">' + rssicon + "</a>")
H.append('</span><ul class="newslist">')
for n in news:
@ -231,39 +226,12 @@ def scolar_news_summary_html(context, n=5, rssicon=None):
return "\n".join(H)
def scolar_news_summary_rss(context, title, sco_url, n=5):
"""rss feed for scolar news"""
news = scolar_news_summary(context, n=n)
items = []
for n in news:
text = safehtml.convert_html_to_text(n["text"])
items.append(
PyRSS2Gen.RSSItem(
title=six.text_type("%s %s" % (n["rssdate"], text), SCO_ENCODING),
link=sco_url + "/" + n["url"],
pubDate=n["date822"],
)
)
rss = PyRSS2Gen.RSS2(
title=six.text_type(title, SCO_ENCODING),
link=sco_url,
description=six.text_type(title, SCO_ENCODING),
lastBuildDate=datetime.datetime.now(),
items=items,
)
f = StringIO()
rss.write_xml(f, encoding=SCO_ENCODING)
f.seek(0)
data = f.read()
f.close()
return data
def _send_news_by_mail(context, n):
def _send_news_by_mail(n):
"""Notify by email"""
context = None # #context
infos = _get_formsemestre_infos_from_news(context, n)
formsemestre_id = infos.get("formsemestre_id", None)
prefs = sco_preferences.SemPreferences(context, formsemestre_id=formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id=formsemestre_id)
destinations = prefs["emails_notifications"] or ""
destinations = [x.strip() for x in destinations.split(",")]
destinations = [x for x in destinations if x]

View File

@ -279,7 +279,7 @@ def ficheEtud(context, etudid=None, REQUEST=None):
alist = []
annos = sco_etud.etud_annotations_list(cnx, args={"etudid": etudid})
for a in annos:
if not sco_permissions_check.can_suppress_annotation(context, a["id"], REQUEST):
if not sco_permissions_check.can_suppress_annotation(a["id"]):
a["dellink"] = ""
else:
a[
@ -355,7 +355,7 @@ def ficheEtud(context, etudid=None, REQUEST=None):
# Devenir de l'étudiant:
has_debouche = True # info['debouche']
if sco_permissions_check.can_edit_suivi(context, REQUEST):
if sco_permissions_check.can_edit_suivi():
suivi_readonly = "0"
link_add_suivi = """<li class="adddebouche">
<a id="adddebouchelink" class="stdlink" href="#">ajouter une ligne</a>
@ -478,8 +478,6 @@ def ficheEtud(context, etudid=None, REQUEST=None):
</div>
"""
header = html_sco_header.sco_header(
context,
REQUEST,
page_title="Fiche étudiant %(prenom)s %(nom)s" % info,
cssstyles=["libjs/jQuery-tagEditor/jquery.tag-editor.css"],
javascripts=[
@ -491,7 +489,7 @@ def ficheEtud(context, etudid=None, REQUEST=None):
"js/etud_debouche.js",
],
)
return header + tmpl % info + html_sco_header.sco_footer(context, REQUEST)
return header + tmpl % info + html_sco_header.sco_footer()
def menus_etud(context, REQUEST=None):
@ -547,12 +545,7 @@ def etud_info_html(context, etudid, with_photo="1", REQUEST=None, debug=False):
context = context
except:
pass
# log('etud_info_html: %s' % REQUEST.QUERY_STRING)
formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request(
context, REQUEST
)
# log('etud_info_html: formsemestre_id=%s' % formsemestre_id)
formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request()
with_photo = int(with_photo)
etud = sco_etud.get_etud_info(filled=1, REQUEST=REQUEST)[0]
photo_html = sco_photos.etud_photo_html(

View File

@ -574,7 +574,6 @@ class SituationEtudParcoursGeneric(object):
decision.formsemestre_id_utilise_pour_compenser,
)
logdb(
REQUEST,
cnx,
method="validate_sem",
etudid=self.etudid,
@ -607,7 +606,6 @@ class SituationEtudParcoursGeneric(object):
formsemestre_id_utilise_pour_compenser=fsid,
)
logdb(
REQUEST,
cnx,
method="validate_sem",
etudid=self.etudid,
@ -932,7 +930,6 @@ def formsemestre_validate_ues(
if REQUEST:
logdb(
REQUEST,
cnx,
method="validate_ue",
etudid=etudid,

View File

@ -31,17 +31,13 @@
Tout accès à ReportLab doit donc être précédé d'un PDFLOCK.acquire()
et terminé par un PDFLOCK.release()
"""
import io
import time
import re
import os
import unicodedata
import traceback
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
import reportlab
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
@ -59,7 +55,6 @@ from reportlab.lib.pagesizes import letter, A4, landscape
import app.scodoc.sco_utils as scu
from app.scodoc.sco_utils import (
CONFIG,
SCO_ENCODING,
SCODOC_LOGOS_DIR,
LOGOS_IMAGES_ALLOWED_TYPES,
)
@ -78,13 +73,15 @@ DEFAULT_PDF_FOOTER_TEMPLATE = CONFIG.DEFAULT_PDF_FOOTER_TEMPLATE
def SU(s):
"convert s from string to string suitable for ReportLab"
# A priori inutile en Python 3, mais tester les "combining accents"
return s or ""
# Remplace caractères composés
# eg 'e\xcc\x81' COMBINING ACUTE ACCENT par '\xc3\xa9' LATIN SMALL LETTER E WITH ACUTE
# car les "combining accents" ne sont pas traités par ReportLab mais peuvent
# nous être envoyés par certains navigateurs ou imports
# return unicodedata.normalize("NFC", s)
if not s:
return ""
else:
# Remplace caractères composés
# eg 'e\xcc\x81' COMBINING ACUTE ACCENT par '\xc3\xa9' LATIN SMALL LETTER E WITH ACUTE
# car les "combining accents" ne sont pas traités par ReportLab mais peuvent
# nous être envoyés par certains navigateurs ou imports
# (on en a dans les bases de données)
return unicodedata.normalize("NFC", s)
def _splitPara(txt):
@ -189,7 +186,10 @@ class ScolarsPageTemplate(PageTemplate):
self.server_name = server_name
self.filigranne = filigranne
self.footer_template = footer_template
self.with_page_background = self.preferences["bul_pdf_with_background"]
if self.preferences:
self.with_page_background = self.preferences["bul_pdf_with_background"]
else:
self.with_page_background = False
self.background_image_filename = None
# Our doc is made of a single frame
left, top, right, bottom = [float(x) for x in margins]
@ -229,6 +229,8 @@ class ScolarsPageTemplate(PageTemplate):
server_url: URL du serveur ScoDoc
"""
if not self.preferences:
return
canvas.saveState()
# ---- Background image
if self.background_image_filename and self.with_page_background:
@ -308,7 +310,7 @@ def pdf_basic_page(
adding a title if specified.
"""
StyleSheet = styles.getSampleStyleSheet()
report = StringIO.StringIO() # in-memory document, no disk file
report = io.BytesIO() # in-memory document, no disk file
document = BaseDocTemplate(report)
document.addPageTemplates(
ScolarsPageTemplate(

View File

@ -14,7 +14,7 @@ from app.scodoc import sco_exceptions
from app.scodoc import sco_moduleimpl
def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True):
def can_edit_notes(authuser, moduleimpl_id, allow_ens=True):
"""True if authuser can enter or edit notes in this module.
If allow_ens, grant access to all ens in this module
@ -24,6 +24,7 @@ def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True):
from app.scodoc import sco_formsemestre
from app.scodoc import sco_parcours_dut
context = None # XXX #context
uid = str(authuser)
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
@ -52,27 +53,26 @@ def can_edit_notes(context, authuser, moduleimpl_id, allow_ens=True):
return True
def can_edit_evaluation(context, REQUEST, moduleimpl_id=None):
def can_edit_evaluation(moduleimpl_id=None):
"""Vérifie que l'on a le droit de modifier, créer ou détruire une
évaluation dans ce module.
Sinon, lance une exception.
(nb: n'implique pas le droit de saisir ou modifier des notes)
"""
context = None # #context
# was _evaluation_check_write_access
# AccessDenied("Modification évaluation impossible pour %s" % (uid,))
from app.scodoc import sco_formsemestre
from app.scodoc import sco_moduleimpl
# acces pour resp. moduleimpl et resp. form semestre (dir etud)
if moduleimpl_id is None:
raise ValueError("no moduleimpl specified") # bug
authuser = REQUEST.AUTHENTICATED_USER
uid = str(authuser)
uid = current_user.user_name
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=moduleimpl_id)[0]
sem = sco_formsemestre.get_formsemestre(context, M["formsemestre_id"])
if (
authuser.has_permission(Permission.ScoEditAllEvals)
current_user.has_permission(Permission.ScoEditAllEvals)
or uid == M["responsable_id"]
or uid in sem["responsables"]
):
@ -85,7 +85,7 @@ def can_edit_evaluation(context, REQUEST, moduleimpl_id=None):
return False
def can_suppress_annotation(context, annotation_id, REQUEST):
def can_suppress_annotation(annotation_id):
"""True if current user can suppress this annotation
Seuls l'auteur de l'annotation et le chef de dept peuvent supprimer
une annotation.
@ -95,77 +95,71 @@ def can_suppress_annotation(context, annotation_id, REQUEST):
if len(annos) != 1:
raise sco_exceptions.ScoValueError("annotation inexistante !")
anno = annos[0]
authuser = REQUEST.AUTHENTICATED_USER
return (
str(authuser) == anno["zope_authenticated_user"]
) or authuser.has_permission(Permission.ScoEtudAddAnnotations)
current_user.user_name == anno["zope_authenticated_user"]
) or current_user.has_permission(Permission.ScoEtudAddAnnotations)
def can_edit_suivi(context, REQUEST=None):
def can_edit_suivi():
"""Vrai si l'utilisateur peut modifier les informations de suivi sur la page etud" """
authuser = REQUEST.AUTHENTICATED_USER
return authuser.has_permission(Permission.ScoEtudChangeAdr)
return current_user.has_permission(Permission.ScoEtudChangeAdr)
def can_validate_sem(context, REQUEST, formsemestre_id):
def can_validate_sem(formsemestre_id):
"Vrai si utilisateur peut saisir decision de jury dans ce semestre"
from app.scodoc import sco_formsemestre
context = None # XXX #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
if sem["etat"] != "1":
return False # semestre verrouillé
return is_chef_or_diretud(context, REQUEST, sem)
return is_chef_or_diretud(sem)
def can_edit_pv(context, REQUEST, formsemestre_id):
def can_edit_pv(formsemestre_id):
"Vrai si utilisateur peut editer un PV de jury de ce semestre"
from app.scodoc import sco_formsemestre
context = None # XXX #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
if is_chef_or_diretud(context, REQUEST, sem):
if is_chef_or_diretud(sem):
return True
# Autorise les secrétariats, repérés via la permission ScoEtudChangeAdr
# (ceci nous évite d'ajouter une permission Zope aux installations existantes)
authuser = REQUEST.AUTHENTICATED_USER
return authuser.has_permission(Permission.ScoEtudChangeAdr)
return current_user.has_permission(Permission.ScoEtudChangeAdr)
def is_chef_or_diretud(context, REQUEST, sem):
def is_chef_or_diretud(sem):
"Vrai si utilisateur est admin, chef dept ou responsable du semestre"
authuser = REQUEST.AUTHENTICATED_USER
if authuser.has_permission(Permission.ScoImplement):
return True # admin, chef dept
uid = str(authuser)
if uid in sem["responsables"]:
if (
current_user.has_permission(Permission.ScoImplement)
or current_user.user_name in sem["responsables"]
):
return True
return False
def check_access_diretud(
context, formsemestre_id, REQUEST, required_permission=Permission.ScoImplement
):
def check_access_diretud(formsemestre_id, required_permission=Permission.ScoImplement):
"""Check if access granted: responsable or ScoImplement
Return True|False, HTML_error_page
"""
from app.scodoc import sco_formsemestre
authuser = REQUEST.AUTHENTICATED_USER
context = None # XXX #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
header = html_sco_header.sco_header(
context, page_title="Accès interdit", REQUEST=REQUEST
)
footer = html_sco_header.sco_footer(context, REQUEST)
if (str(authuser) not in sem["responsables"]) and not authuser.has_permission(
required_permission
):
header = html_sco_header.sco_header(page_title="Accès interdit")
footer = html_sco_header.sco_footer()
if (
current_user.user_name not in sem["responsables"]
) and not current_user.has_permission(required_permission):
return (
False,
"\n".join(
[
header,
"<h2>Opération non autorisée pour %s</h2>" % authuser,
"<h2>Opération non autorisée pour %s</h2>" % current_user,
"<p>Responsable de ce semestre : <b>%s</b></p>"
% ", ".join(sem["responsables"]),
footer,
@ -176,18 +170,17 @@ def check_access_diretud(
return True, ""
def can_change_groups(context, REQUEST, formsemestre_id):
def can_change_groups(formsemestre_id):
"Vrai si l'utilisateur peut changer les groupes dans ce semestre"
from app.scodoc import sco_formsemestre
context = None # XXX #context
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
if sem["etat"] != "1":
return False # semestre verrouillé
authuser = REQUEST.AUTHENTICATED_USER
if authuser.has_permission(Permission.ScoEtudChangeGroups):
if current_user.has_permission(Permission.ScoEtudChangeGroups):
return True # admin, chef dept
uid = str(authuser)
if uid in sem["responsables"]:
if current_user.user_name in sem["responsables"]:
return True
return False

View File

@ -43,6 +43,7 @@ Les images sont servies par ScoDoc, via la méthode getphotofile?etudid=xxx
"""
import io
import os
import time
import datetime
@ -51,10 +52,6 @@ import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse
import traceback
from PIL import Image as PILImage
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
import glob
from flask import request
@ -241,7 +238,7 @@ def photo_pathname(context, etud, size="orig"):
def store_photo(context, etud, data, REQUEST=None):
"""Store image for this etud.
If there is an existing photo, it is erased and replaced.
data is a string with image raw data.
data is a bytes string with image raw data.
Update database to store filename.
@ -260,8 +257,7 @@ def store_photo(context, etud, data, REQUEST=None):
sco_etud.identite_edit_nocheck(cnx, etud)
cnx.commit()
#
if REQUEST:
logdb(REQUEST, cnx, method="changePhoto", msg=filename, etudid=etud["etudid"])
logdb(cnx, method="changePhoto", msg=filename, etudid=etud["etudid"])
#
return 1, "ok"
@ -285,9 +281,7 @@ def suppress_photo(context, etud, REQUEST=None):
os.remove(filename)
# 3- log
if REQUEST:
logdb(
REQUEST, cnx, method="changePhoto", msg="suppression", etudid=etud["etudid"]
)
logdb(cnx, method="changePhoto", msg="suppression", etudid=etud["etudid"])
# ---------------------------------------------------------------------------
@ -295,11 +289,11 @@ def suppress_photo(context, etud, REQUEST=None):
def save_image(context, etudid, data):
"""img_file is a file-like object.
"""data is a bytes string.
Save image in JPEG in 2 sizes (original and h90).
Returns filename (relative to PHOTO_DIR), without extension
"""
data_file = StringIO()
data_file = io.BytesIO()
data_file.write(data)
data_file.seek(0)
img = PILImage.open(data_file)
@ -355,7 +349,7 @@ def copy_portal_photo_to_fs(context, etud, REQUEST=None):
url = photo_portal_url(context, etud)
if not url:
return None, "%(nomprenom)s: pas de code NIP" % etud
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
f = None
try:
log("copy_portal_photo_to_fs: getting %s" % url)

View File

@ -33,6 +33,8 @@ Contribution M. Salomon, UFC / IUT DE BELFORT-MONTBÉLIARD, 2016
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
import random
import flask
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import log
@ -59,7 +61,7 @@ def do_placement_selectetuds(context, REQUEST):
Choisi les étudiants et les infos sur la salle pour leur placement.
"""
evaluation_id = REQUEST.form["evaluation_id"]
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not E:
raise ScoValueError("invalid evaluation_id")
E = E[0]
@ -75,7 +77,7 @@ def do_placement_selectetuds(context, REQUEST):
# description de l'evaluation
H = [
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
evaluation_id=evaluation_id, REQUEST=REQUEST
),
"<h3>Placement et émargement des étudiants</h3>",
]
@ -195,7 +197,7 @@ def do_placement_selectetuds(context, REQUEST):
H.append("""<div class="saisienote_etape1">""")
return "\n".join(H) + "\n" + tf[1] + "\n</div>"
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"%s/Notes/moduleimpl_status?moduleimpl_id=%s"
% (scu.ScoURL(), E["moduleimpl_id"])
)
@ -225,7 +227,7 @@ def do_placement_selectetuds(context, REQUEST):
)
+ "&".join(gs)
)
return REQUEST.RESPONSE.redirect(scu.NotesURL() + "/do_placement?" + query)
return flask.redirect(scu.NotesURL() + "/do_placement?" + query)
else:
raise ValueError(
"invalid placement_method (%s)" % tf[2]["placement_method"]
@ -244,11 +246,11 @@ def do_placement(context, REQUEST):
raise ScoValueError(
"Formulaire incomplet ! Vous avez sans doute attendu trop longtemps, veuillez vous reconnecter. Si le problème persiste, contacter l'administrateur. Merci."
)
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
# Check access
# (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
return (
"<h2>Génération du placement impossible pour %s</h2>" % authusername
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
@ -278,7 +280,7 @@ def do_placement(context, REQUEST):
else:
getallstudents = False
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, groups, getallstudents=getallstudents, include_dems=True
evaluation_id, groups, getallstudents=getallstudents, include_dems=True
)
if not etudids:
return "<p>Aucun groupe sélectionné !</p>"
@ -320,11 +322,11 @@ def do_placement(context, REQUEST):
maxlines = sem_preferences.get("feuille_placement_positions")
if placement_method == "xls":
filename = "placement_%s_%s.xls" % (evalname, gr_title_filename)
filename = f"placement_{evalname}_{gr_title_filename}{scu.XLSX_SUFFIX}"
xls = Excel_feuille_placement(
E, desceval, listetud, columns, space, maxlines, building, room, numbering
)
return sco_excel.sendExcelFile(REQUEST, xls, filename)
return sco_excel.send_excel_file(REQUEST, xls, filename)
else:
nbcolumns = int(columns)
@ -392,7 +394,7 @@ def do_placement(context, REQUEST):
+ "",
pdf_title=pdf_title,
# pdf_shorttitle = '',
preferences=sco_preferences.SemPreferences(context, M["formsemestre_id"]),
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
# html_generate_cells=False # la derniere ligne (moyennes) est incomplete
)
t = tab.make_page(
@ -403,9 +405,7 @@ def do_placement(context, REQUEST):
def placement_eval_selectetuds(context, evaluation_id, REQUEST=None):
"""Dialogue placement etudiants: choix methode et localisation"""
evals = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": evaluation_id}
)
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")
theeval = evals[0]
@ -414,7 +414,7 @@ def placement_eval_selectetuds(context, evaluation_id, REQUEST=None):
page_title = 'Placement "%s"' % theeval["description"]
else:
page_title = "Placement des étudiants"
H = [html_sco_header.sco_header(context, REQUEST, page_title=page_title)]
H = [html_sco_header.sco_header(page_title=page_title)]
formid = "placementfile"
if not REQUEST.form.get("%s-submitted" % formid, False):
@ -444,7 +444,7 @@ def placement_eval_selectetuds(context, evaluation_id, REQUEST=None):
</ul>
"""
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
@ -663,8 +663,8 @@ def Excel_feuille_placement(
li = 0
line = 0
dt = time.strftime("%d/%m/%Y a %Hh%M")
ws0.write(li, 0, u"Feuille placement etudiants éditée le %s" % dt, style_titres)
ws1.write(li, 0, u"Feuille placement etudiants éditée le %s" % dt, style_titres)
ws0.write(li, 0, "Feuille placement etudiants éditée le %s" % dt, style_titres)
ws1.write(li, 0, "Feuille placement etudiants éditée le %s" % dt, style_titres)
for desceval in description:
if line % 2 == 0:
li += 2
@ -677,41 +677,39 @@ def Excel_feuille_placement(
ws0.write(
li,
0,
u"Date : %s - Horaire : %s à %s"
% (E["jour"], E["heure_debut"], E["heure_fin"]),
"Date : %s - Horaire : %s à %s" % (E["jour"], E["heure_debut"], E["heure_fin"]),
style_titres,
)
ws1.write(
li,
0,
u"Date : %s - Horaire : %s à %s"
% (E["jour"], E["heure_debut"], E["heure_fin"]),
"Date : %s - Horaire : %s à %s" % (E["jour"], E["heure_debut"], E["heure_fin"]),
style_titres,
)
li += 1
# entetes colonnes - feuille0
for col in range(nbcolumns):
ws0.write(li, col + 1, u"colonne %s" % (col + 1), style2b)
ws0.write(li, col + 1, "colonne %s" % (col + 1), style2b)
# entetes colonnes - feuille1
if numbering == "coordinate":
ws1.write(li, 0, u"Nom", style2bi)
ws1.write(li, 1, u"Prénom", style2bi)
ws1.write(li, 2, u"Colonne", style2bi)
ws1.write(li, 3, u"Ligne", style2bi)
ws1.write(li, 0, "Nom", style2bi)
ws1.write(li, 1, "Prénom", style2bi)
ws1.write(li, 2, "Colonne", style2bi)
ws1.write(li, 3, "Ligne", style2bi)
ws1.write(li, 5, u"Nom", style2bi)
ws1.write(li, 6, u"Prénom", style2bi)
ws1.write(li, 7, u"Colonne", style2bi)
ws1.write(li, 8, u"Ligne", style2bi)
ws1.write(li, 5, "Nom", style2bi)
ws1.write(li, 6, "Prénom", style2bi)
ws1.write(li, 7, "Colonne", style2bi)
ws1.write(li, 8, "Ligne", style2bi)
else:
ws1.write(li, 0, u"Nom", style2bi)
ws1.write(li, 1, u"Prénom", style2bi)
ws1.write(li, 2, u"Place", style2bi)
ws1.write(li, 0, "Nom", style2bi)
ws1.write(li, 1, "Prénom", style2bi)
ws1.write(li, 2, "Place", style2bi)
ws1.write(li, 4, u"Nom", style2bi)
ws1.write(li, 5, u"Prénom", style2bi)
ws1.write(li, 6, u"Place", style2bi)
ws1.write(li, 4, "Nom", style2bi)
ws1.write(li, 5, "Prénom", style2bi)
ws1.write(li, 6, "Place", style2bi)
# etudiants
line = 1
@ -751,7 +749,7 @@ def Excel_feuille_placement(
ws0.write(li0 + 2, col, " ", style1bb)
else:
ws0.write(
li0 + 2, col, u"place %s" % (col + (line - 1) * nbcolumns), style1bb
li0 + 2, col, "place %s" % (col + (line - 1) * nbcolumns), style1bb
)
# ws0.write(li+3,col, ' ', style1bm )
# ws0.write(li+4,col, ' ', style1bb )
@ -793,21 +791,21 @@ def Excel_feuille_placement(
nbcol = 0
col = 0
if numbering == "coordinate":
ws1.write(li, 0, u"Nom", style2bi)
ws1.write(li, 1, u"Prénom", style2bi)
ws1.write(li, 2, u"Colonne", style2bi)
ws1.write(li, 3, u"Ligne", style2bi)
ws1.write(li, 0, "Nom", style2bi)
ws1.write(li, 1, "Prénom", style2bi)
ws1.write(li, 2, "Colonne", style2bi)
ws1.write(li, 3, "Ligne", style2bi)
ws1.write(li, 5, u"Nom", style2bi)
ws1.write(li, 6, u"Prénom", style2bi)
ws1.write(li, 7, u"Colonne", style2bi)
ws1.write(li, 8, u"Ligne", style2bi)
ws1.write(li, 5, "Nom", style2bi)
ws1.write(li, 6, "Prénom", style2bi)
ws1.write(li, 7, "Colonne", style2bi)
ws1.write(li, 8, "Ligne", style2bi)
else:
ws1.write(li, 0, u"Nom", style2bi)
ws1.write(li, 1, u"Prénom", style2bi)
ws1.write(li, 2, u"Place", style2bi)
ws1.write(li, 0, "Nom", style2bi)
ws1.write(li, 1, "Prénom", style2bi)
ws1.write(li, 2, "Place", style2bi)
ws1.write(li, 4, u"Nom", style2bi)
ws1.write(li, 5, u"Prénom", style2bi)
ws1.write(li, 6, u"Place", style2bi)
ws1.write(li, 4, "Nom", style2bi)
ws1.write(li, 5, "Prénom", style2bi)
ws1.write(li, 6, "Place", style2bi)
return wb.savetostr()

View File

@ -56,7 +56,7 @@ class PortalInterface(object):
def get_portal_url(self, context):
"URL of portal"
portal_url = sco_preferences.get_preference(context, "portal_url")
portal_url = sco_preferences.get_preference("portal_url")
if not self.warning:
if portal_url:
log("Portal URL=%s" % portal_url)
@ -67,7 +67,7 @@ class PortalInterface(object):
def get_etapes_url(self, context):
"Full URL of service giving list of etapes (in XML)"
etapes_url = sco_preferences.get_preference(context, "etapes_url")
etapes_url = sco_preferences.get_preference("etapes_url")
if not etapes_url:
# Default:
portal_url = self.get_portal_url(context)
@ -82,7 +82,7 @@ class PortalInterface(object):
def get_etud_url(self, context):
"Full URL of service giving list of students (in XML)"
etud_url = sco_preferences.get_preference(context, "etud_url")
etud_url = sco_preferences.get_preference("etud_url")
if not etud_url:
# Default:
portal_url = self.get_portal_url(context)
@ -97,7 +97,7 @@ class PortalInterface(object):
def get_photo_url(self, context):
"Full URL of service giving photo of student"
photo_url = sco_preferences.get_preference(context, "photo_url")
photo_url = sco_preferences.get_preference("photo_url")
if not photo_url:
# Default:
portal_url = self.get_portal_url(context)
@ -112,7 +112,7 @@ class PortalInterface(object):
def get_maquette_url(self, context):
"""Full URL of service giving Apogee maquette pour une étape (fichier "CSV")"""
maquette_url = sco_preferences.get_preference(context, "maquette_url")
maquette_url = sco_preferences.get_preference("maquette_url")
if not maquette_url:
# Default:
portal_url = self.get_portal_url(context)
@ -123,7 +123,7 @@ class PortalInterface(object):
def get_portal_api_version(self, context):
"API version of the portal software"
api_ver = sco_preferences.get_preference(context, "portal_api")
api_ver = sco_preferences.get_preference("portal_api")
if not api_ver:
# Default:
api_ver = 1
@ -152,7 +152,7 @@ def get_inscrits_etape(context, code_etape, anneeapogee=None, ntrials=2):
api_ver = get_portal_api_version(context)
if not etud_url:
return []
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
if api_ver > 1:
req = (
etud_url
@ -208,7 +208,7 @@ def query_apogee_portal(context, **args):
# Ne fonctionne pas avec l'API 2 sur nom et prenom
# XXX TODO : va poser problème pour la page modif données étudiants : A VOIR
return []
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
req = etud_url + "?" + six.moves.urllib.parse.urlencode(list(args.items()))
doc = scu.query_portal(req, timeout=portal_timeout) # sco_utils
return xml_to_list_of_dicts(doc, req=req)
@ -267,20 +267,14 @@ def xml_to_list_of_dicts(doc, req=None):
def get_infos_apogee_allaccents(context, nom, prenom):
"essai recup infos avec differents codages des accents"
if nom:
unom = six.text_type(nom, scu.SCO_ENCODING)
nom_noaccents = scu.suppress_accents(unom)
nom_utf8 = unom.encode("utf-8")
nom_noaccents = scu.suppress_accents(nom)
else:
nom_noaccents = nom
nom_utf8 = nom
if prenom:
uprenom = six.text_type(prenom, scu.SCO_ENCODING)
prenom_noaccents = scu.suppress_accents(uprenom)
prenom_utf8 = uprenom.encode("utf-8")
prenom_noaccents = scu.suppress_accents(prenom)
else:
prenom_noaccents = prenom
prenom_utf8 = prenom
# avec accents
infos = query_apogee_portal(context, nom=nom, prenom=prenom)
@ -289,9 +283,6 @@ def get_infos_apogee_allaccents(context, nom, prenom):
infos += query_apogee_portal(
context, nom=nom_noaccents, prenom=prenom_noaccents
)
# avec accents en UTF-8
if nom_utf8 != nom_noaccents or prenom_utf8 != prenom_noaccents:
infos += query_apogee_portal(context, nom=nom_utf8, prenom=prenom_utf8)
return infos
@ -330,7 +321,7 @@ def get_etud_apogee(context, code_nip):
etud_url = get_etud_url(context)
if not etud_url:
return {}
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
req = etud_url + "?" + six.moves.urllib.parse.urlencode((("nip", code_nip),))
doc = scu.query_portal(req, timeout=portal_timeout)
d = _normalize_apo_fields(xml_to_list_of_dicts(doc, req=req))
@ -363,7 +354,7 @@ def _parse_etapes_from_xml(context, doc):
"""
may raise exception if invalid xml doc
"""
xml_etapes_by_dept = sco_preferences.get_preference(context, "xml_etapes_by_dept")
xml_etapes_by_dept = sco_preferences.get_preference("xml_etapes_by_dept")
# parser XML
dom = xml.dom.minidom.parseString(doc)
infos = {}
@ -391,7 +382,7 @@ def get_etapes_apogee(context):
etapes_url = get_etapes_url(context)
infos = {}
if etapes_url:
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
log(
"get_etapes_apogee: requesting '%s' with timeout=%s"
% (etapes_url, portal_timeout)
@ -439,9 +430,9 @@ def get_etapes_apogee_dept(context):
Returns [ ( code, intitule) ], ordonnée
"""
xml_etapes_by_dept = sco_preferences.get_preference(context, "xml_etapes_by_dept")
xml_etapes_by_dept = sco_preferences.get_preference("xml_etapes_by_dept")
if xml_etapes_by_dept:
portal_dept_name = sco_preferences.get_preference(context, "portal_dept_name")
portal_dept_name = sco_preferences.get_preference("portal_dept_name")
log('get_etapes_apogee_dept: portal_dept_name="%s"' % portal_dept_name)
else:
portal_dept_name = ""
@ -565,7 +556,7 @@ def get_maquette_apogee(context, etape="", annee_scolaire=""):
maquette_url = get_maquette_url(context)
if not maquette_url:
return None
portal_timeout = sco_preferences.get_preference(context, "portal_timeout")
portal_timeout = sco_preferences.get_preference("portal_timeout")
req = (
maquette_url
+ "?"

View File

@ -170,7 +170,7 @@ def formsemestre_poursuite_report(
"""Table avec informations "poursuite" """
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
etuds = _getEtudInfoGroupes(
context, [sco_groups.get_default_group(context, formsemestre_id)]
context, [sco_groups.get_default_group(formsemestre_id)]
)
infos = []
@ -206,7 +206,7 @@ def formsemestre_poursuite_report(
html_sortable=True,
html_class="table_leftalign table_listegroupe",
pdf_link=False, # pas d'export pdf
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
tab.filename = scu.make_filename("poursuite " + sem["titreannee"])

View File

@ -68,18 +68,18 @@ des tuples (name, value, formsemestre_id).
Si formsemestre_id est NULL, la valeur concerne tous les semestres,
sinon, elle ne concerne que le semestre indiqué.
* Utilisation dans ScoDoc7
* Utilisation dans ScoDoc8
- lire une valeur:
get_preference(context, name, formsemestre_id)
get_preference(name, formsemestre_id)
nb: les valeurs sont des chaines, sauf:
. si le type est spécfié (float ou int)
. les boolcheckbox qui sont des entiers 0 ou 1
- avoir un mapping (read only) de toutes les valeurs:
sco_preferences.SemPreferences(context,formsemestre_id)
sco_preferences.SemPreferences(formsemestre_id)
- editer les preferences globales:
sco_preferences.get_base_preferences(self).edit(REQUEST=REQUEST)
- editer les preferences d'un semestre:
SemPreferences(context,formsemestre_id).edit()
SemPreferences(formsemestre_id).edit()
* Implémentation: sco_preferences.py
@ -98,7 +98,7 @@ Une instance unique par site (département, repéré par URL).
.deleteformsemestre_id, name)
.edit() (HTML dialog)
class SemPreferences(context,formsemestre_id)
class SemPreferences(formsemestre_id)
Une instance par semestre, et une instance pour prefs globales.
L'attribut .base_prefs point sur BasePreferences.
.__getitem__ [name]
@ -106,10 +106,11 @@ L'attribut .base_prefs point sur BasePreferences.
.edit(categories=[])
get_base_preferences(context, formsemestre_id)
Return base preferences for this context (instance BasePreferences)
get_base_preferences(formsemestre_id)
Return base preferences for current scodoc_dept (instance BasePreferences)
"""
import flask
from flask import g
from app.scodoc import sco_cache
@ -122,20 +123,20 @@ import app.scodoc.sco_utils as scu
_SCO_BASE_PREFERENCES = {} # { URL: BasePreferences instance }
def get_base_preferences(context):
def get_base_preferences():
"""Return global preferences for the current department"""
dept = g.scodoc_dept
if not dept in _SCO_BASE_PREFERENCES:
_SCO_BASE_PREFERENCES[dept] = BasePreferences(context)
_SCO_BASE_PREFERENCES[dept] = BasePreferences()
return _SCO_BASE_PREFERENCES[dept]
def get_preference(context, name, formsemestre_id=None):
def get_preference(name, formsemestre_id=None):
"""Returns value of named preference.
All preferences have a sensible default value, so this
function always returns a usable value for all defined preferences names.
"""
return get_base_preferences(context).get(formsemestre_id, name)
return get_base_preferences().get(formsemestre_id, name)
PREF_CATEGORIES = (
@ -205,8 +206,7 @@ class BasePreferences(object):
filter_nulls=False,
)
def __init__(self, context):
self.context = context
def __init__(self):
self.init()
self.load()
@ -1957,8 +1957,9 @@ class BasePreferences(object):
"""HTML dialog: edit global preferences"""
from app.scodoc import html_sco_header
context = None # XXX TO REMOVE #context
H = [
html_sco_header.sco_header(self.context, REQUEST, page_title="Préférences"),
html_sco_header.sco_header(page_title="Préférences"),
"<h2>Préférences globales pour %s</h2>" % scu.ScoURL(),
"""<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>
@ -1973,18 +1974,14 @@ class BasePreferences(object):
submitlabel="Enregistrer les modifications",
)
if tf[0] == 0:
return (
"\n".join(H) + tf[1] + html_sco_header.sco_footer(self.context, REQUEST)
)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(scu.ScoURL()) # cancel
return flask.redirect(scu.ScoURL()) # cancel
else:
for pref in self.prefs_definition:
self.prefs[None][pref[0]] = tf[2][pref[0]]
self.save()
return REQUEST.RESPONSE.redirect(
scu.ScoURL() + "?head_message=Préférences modifiées"
)
return flask.redirect(scu.ScoURL() + "?head_message=Préférences modifiées")
def build_tf_form(self, categories=[], formsemestre_id=None):
"""Build list of elements for TrivialFormulator.
@ -2058,10 +2055,9 @@ class BasePreferences(object):
class SemPreferences(object):
"""Preferences for a formsemestre"""
def __init__(self, context, formsemestre_id=None):
self.context = context
def __init__(self, formsemestre_id=None):
self.formsemestre_id = formsemestre_id
self.base_prefs = get_base_preferences(self.context)
self.base_prefs = get_base_preferences()
def __getitem__(self, name):
return self.base_prefs.get(self.formsemestre_id, name)
@ -2091,10 +2087,11 @@ class SemPreferences(object):
raise ScoValueError(
"sem_preferences.edit doit etre appele sur un semestre !"
) # a bug !
sem = sco_formsemestre.get_formsemestre(self.context, self.formsemestre_id)
context = None # XXX TO REMOVE
sem = sco_formsemestre.get_formsemestre(context, self.formsemestre_id)
H = [
html_sco_header.html_sem_header(
self.context, REQUEST, "Préférences du semestre", sem
context, REQUEST, "Préférences du semestre", sem
),
"""
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
@ -2149,14 +2146,11 @@ function set_global_pref(el, pref_name) {
scu.NotesURL()
+ "/formsemestre_status?formsemestre_id=%s" % self.formsemestre_id
)
context = None # XXX TO REMOVE
if tf[0] == 0:
return (
"\n".join(H) + tf[1] + html_sco_header.sco_footer(self.context, REQUEST)
)
return "\n".join(H) + tf[1] + html_sco_header.sco_footer()
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
dest_url + "&head_message=Annulé"
) # cancel
return flask.redirect(dest_url + "&head_message=Annulé") # cancel
else:
# Supprime pref locale du semestre (retour à la valeur globale)
if tf[2]["suppress"]:
@ -2190,19 +2184,17 @@ function set_global_pref(el, pref_name) {
# done: change prefs and redirect to semestre status
destination = tf[2]["destination"]
if destination == "done" or destination == "":
return REQUEST.RESPONSE.redirect(
dest_url + "&head_message=Préférences modifiées"
)
return flask.redirect(dest_url + "&head_message=Préférences modifiées")
elif destination == "again":
return REQUEST.RESPONSE.redirect(
return flask.redirect(
REQUEST.URL0 + "?formsemestre_id=" + self.formsemestre_id
)
elif destination == "global":
return REQUEST.RESPONSE.redirect(scu.ScoURL() + "/edit_preferences")
return flask.redirect(scu.ScoURL() + "/edit_preferences")
#
def doc_preferences(context):
def doc_preferences():
""" Liste les preferences en MarkDown, pour la documentation"""
L = []
for cat, cat_descr in PREF_CATEGORIES:
@ -2211,7 +2203,7 @@ def doc_preferences(context):
L.append([""])
L.append(["Nom", "&nbsp;", "&nbsp;"])
L.append(["----", "----", "----"])
for pref_name, pref in get_base_preferences(context).prefs_definition:
for pref_name, pref in get_base_preferences().prefs_definition:
if pref["category"] == cat:
L.append(
["`" + pref_name + "`", pref["title"], pref.get("explanation", "")]

View File

@ -40,6 +40,7 @@ from app.scodoc import sco_codes_parcours
from app.scodoc import VERSION
from app.scodoc import sco_etud
from app.scodoc import sco_preferences
from app.scodoc.sco_excel import ScoExcelSheet
def feuille_preparation_jury(context, formsemestre_id, REQUEST):
@ -59,7 +60,7 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
prev_ue_acro = {} # ue_code_s : acronyme (à afficher)
prev_moy = {} # moyennes gen sem prec
moy_ue = scu.DictDefault(defaultvalue={}) # ue_acro : moyennes { etudid : moy ue }
ue_acro = {} # ue_code_s : acronyme (à afficher)
ue_acro = {} # ue_code_s : acronyme (à afficher)
moy = {} # moyennes gen
moy_inter = {} # moyenne gen. sur les 2 derniers semestres
code = {} # decision existantes s'il y en a
@ -129,9 +130,9 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
main_partition_id, ""
)
# absences:
nbabs, nbabsjust = sco_abs.get_abs_count(etudid, sem)
nbabs[etudid] = nbabs
nbabsjust[etudid] = nbabs - nbabsjust
e_nbabs, e_nbabsjust = sco_abs.get_abs_count(etudid, sem)
nbabs[etudid] = e_nbabs
nbabsjust[etudid] = e_nbabs - e_nbabsjust
# Codes des UE "semestre précédent":
ue_prev_codes = list(prev_moy_ue.keys())
@ -153,14 +154,30 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
if prev_moy: # si qq chose dans precedent
sp = "S%s" % (sid - 1)
L = sco_excel.ScoExcelSheet(sheet_name="Prepa Jury %s" % sn)
L.append(["Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"])])
L.append([]) # empty line
ws = sco_excel.ScoExcelSheet(sheet_name="Prepa Jury %s" % sn)
# génération des styles
style_bold = sco_excel.excel_make_style(size=10, bold=True)
style_center = sco_excel.excel_make_style(halign="center")
style_boldcenter = sco_excel.excel_make_style(bold=True, halign="center")
style_moy = sco_excel.excel_make_style(
bold=True, halign="center", bgcolor=sco_excel.COLORS.LIGHT_YELLOW
)
style_note = sco_excel.excel_make_style(halign="right", format_number="General")
style_note_bold = sco_excel.excel_make_style(
halign="right", bold=True, format_number="General"
)
# Première ligne
ws.append_single_cell_row(
"Feuille préparation Jury %s" % scu.unescape_html(sem["titreannee"]), style_bold
)
ws.append_blank_row()
# Ligne de titre
titles = ["Rang"]
if sco_preferences.get_preference(context, "prepa_jury_nip"):
if sco_preferences.get_preference("prepa_jury_nip"):
titles.append("NIP")
if sco_preferences.get_preference(context, "prepa_jury_ine"):
if sco_preferences.get_preference("prepa_jury_ine"):
titles.append("INE")
titles += [
"etudid",
@ -174,7 +191,6 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
"Parcours",
"Groupe",
]
if prev_moy: # si qq chose dans precedent
titles += [prev_ue_acro[x][1] for x in ue_prev_codes] + [
"Moy %s" % sp,
@ -189,15 +205,7 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
if autorisations:
titles.append("Autorisations")
# titles.append('Assidu')
L.append(titles)
style_bold = sco_excel.Excel_MakeStyle(bold=True)
style_center = sco_excel.Excel_MakeStyle(halign="center")
style_boldcenter = sco_excel.Excel_MakeStyle(bold=True, halign="center")
style_moy = sco_excel.Excel_MakeStyle(
bold=True, halign="center", bgcolor="lightyellow"
)
style_note = sco_excel.Excel_MakeStyle(halign="right")
style_note_bold = sco_excel.Excel_MakeStyle(halign="right", bold=True)
ws.append_row(ws.make_row(titles, style_boldcenter))
if prev_moy:
tit_prev_moy = "Moy " + sp
col_prev_moy = titles.index(tit_prev_moy)
@ -205,9 +213,6 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
col_moy = titles.index(tit_moy)
col_abs = titles.index("Abs")
L.set_style(style_bold, li=0)
L.set_style(style_boldcenter, li=2)
def fmt(x):
"reduit les notes a deux chiffres"
x = scu.fmt_note(x, keep_numeric=False)
@ -218,101 +223,100 @@ def feuille_preparation_jury(context, formsemestre_id, REQUEST):
i = 1 # numero etudiant
for etudid in etudids:
cells = []
etud = nt.identdict[etudid]
l = [str(i)]
if sco_preferences.get_preference(context, "prepa_jury_nip"):
l.append(etud["code_nip"])
if sco_preferences.get_preference(context, "prepa_jury_ine"):
l.append(etud["code_ine"])
l += [
etudid,
etud["civilite_str"],
sco_etud.format_nom(etud["nom"]),
sco_etud.format_prenom(etud["prenom"]),
etud["date_naissance"],
etud["bac"],
etud["specialite"],
etud["classement"],
parcours[etudid],
groupestd[etudid],
]
co = len(l)
cells.append(ws.make_cell(str(i)))
if sco_preferences.get_preference("prepa_jury_nip"):
cells.append(ws.make_cell(etud["code_nip"]))
if sco_preferences.get_preference("prepa_jury_ine"):
cells.append(ws.make_cell(["code_ine"]))
cells += ws.make_row(
[
etudid,
etud["civilite_str"],
sco_etud.format_nom(etud["nom"]),
sco_etud.format_prenom(etud["prenom"]),
etud["date_naissance"],
etud["bac"],
etud["specialite"],
etud["classement"],
parcours[etudid],
groupestd[etudid],
]
)
co = len(cells)
if prev_moy:
for ue_acro in ue_prev_codes:
l.append(fmt(prev_moy_ue.get(ue_acro, {}).get(etudid, "")))
L.set_style(style_note, li=i + 2, co=co)
cells.append(
ws.make_cell(
fmt(prev_moy_ue.get(ue_acro, {}).get(etudid, "")), style_note
)
)
co += 1
l.append(fmt(prev_moy.get(etudid, "")))
l.append(prev_code.get(etudid, ""))
# L.set_style(style_bold, li=i+2, co=col_prev_moy+1) # moy gen prev
# L.set_style(style_moy, li=i+2, co=col_prev_moy+2) # decision prev
L.set_style(style_bold, li=i + 2, co=col_prev_moy) # moy gen prev
L.set_style(style_moy, li=i + 2, co=col_prev_moy + 1) # decision prev
cells.append(
ws.make_cell(fmt(prev_moy.get(etudid, "")), style_bold)
) # moy gen prev
cells.append(
ws.make_cell(fmt(prev_code.get(etudid, "")), style_moy)
) # decision prev
co += 2
for ue_acro in ue_codes:
l.append(fmt(moy_ue.get(ue_acro, {}).get(etudid, "")))
L.set_style(style_note, li=i + 2, co=co)
cells.append(
ws.make_cell(fmt(moy_ue.get(ue_acro, {}).get(etudid, "")), style_note)
)
co += 1
l.append(fmt(moy.get(etudid, "")))
# L.set_style(style_note_bold, li=i+2, co=col_moy+1) # moy gen
L.set_style(style_note_bold, li=i + 2, co=col_moy) # moy gen
cells.append(ws.make_cell(fmt(moy.get(etudid, "")), style_note_bold)) # moy gen
co += 1
if moy_inter:
l.append(fmt(moy_inter.get(etudid, "")))
L.set_style(style_note, li=i + 2, co=co)
l.append(fmt(str(nbabs.get(etudid, ""))))
l.append(fmt(str(nbabsjust.get(etudid, ""))))
cells.append(ws.make_cell(fmt(moy_inter.get(etudid, "")), style_note))
cells.append(ws.make_cell(str(nbabs.get(etudid, "")), style_center))
cells.append(ws.make_cell(str(nbabsjust.get(etudid, "")), style_center))
if code:
l.append(code.get(etudid, ""))
if autorisations:
l.append(autorisations.get(etudid, ""))
cells.append(ws.make_cell(code.get(etudid, ""), style_moy))
cells.append(ws.make_cell(autorisations.get(etudid, ""), style_moy))
# l.append(assidu.get(etudid, ''))
L.append(l)
ws.append_row(cells)
i += 1
L.set_style(style_center, li=i + 1, co=col_abs) # absences
L.set_style(style_center, li=i + 1, co=col_abs + 1) # absences injustifiées
L.set_style(style_moy, li=i + 1, co=col_abs + 2) # décision semestre
L.set_style(style_center, li=i + 1, co=col_abs + 3) # Autorisations
#
L.append([""])
ws.append_blank_row()
# Explications des codes
codes = list(sco_codes_parcours.CODES_EXPL.keys())
codes.sort()
L.append(["Explication des codes"])
ws.append_single_cell_row("Explication des codes")
for code in codes:
L.append(["", "", "", code, sco_codes_parcours.CODES_EXPL[code]])
L.append(
[
"",
"",
"",
"ADM+",
"indique que le semestre a déjà servi à en compenser un autre",
]
ws.append_row(
ws.make_row(["", "", "", code, sco_codes_parcours.CODES_EXPL[code]])
)
ws.append_row(
ws.make_row(
[
"",
"",
"",
"ADM+",
"indique que le semestre a déjà servi à en compenser un autre",
]
)
)
# UE : Correspondances acronyme et titre complet
L.append([""])
L.append(["Titre des UE"])
ws.append_blank_row()
ws.append_single_cell_row("Titre des UE")
if prev_moy:
for ue in ntp.get_ues(filter_sport=True):
L.append(["", "", "", ue["acronyme"], ue["titre"]])
ws.append_row(ws.make_row(["", "", "", ue["acronyme"], ue["titre"]]))
for ue in nt.get_ues(filter_sport=True):
L.append(["", "", "", ue["acronyme"], ue["titre"]])
ws.append_row(ws.make_row(["", "", "", ue["acronyme"], ue["titre"]]))
#
L.append([""])
L.append(
[
"Préparé par %s le %s sur %s pour %s"
% (
VERSION.SCONAME,
time.strftime("%d/%m/%Y"),
REQUEST.BASE0,
REQUEST.AUTHENTICATED_USER,
)
]
ws.append_blank_row()
ws.append_single_cell_row(
"Préparé par %s le %s sur %s pour %s"
% (
VERSION.SCONAME,
time.strftime("%d/%m/%Y"),
REQUEST.BASE0,
REQUEST.AUTHENTICATED_USER,
)
)
xls = L.gen_workbook()
return sco_excel.sendExcelFile(REQUEST, xls, "PrepaJury%s.xls" % sn)
xls = ws.generate_standalone()
return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")

View File

@ -51,6 +51,7 @@ from operator import itemgetter
from reportlab.platypus import Paragraph
from reportlab.lib import styles
import flask
from flask import url_for, g
import app.scodoc.sco_utils as scu
@ -419,10 +420,10 @@ def pvjury_table(
columns_ids += ["prev_decision"]
columns_ids += ["decision"]
if sco_preferences.get_preference(context, "bul_show_mention", formsemestre_id):
if sco_preferences.get_preference("bul_show_mention", formsemestre_id):
columns_ids += ["mention"]
columns_ids += ["ue_cap"]
if sco_preferences.get_preference(context, "bul_show_ects", formsemestre_id):
if sco_preferences.get_preference("bul_show_ects", formsemestre_id):
columns_ids += ["ects"]
# XXX if not dpv["semestre_non_terminal"]:
@ -459,13 +460,13 @@ def pvjury_table(
if with_paragraph_nom:
cell_style = styles.ParagraphStyle({})
cell_style.fontSize = sco_preferences.get_preference(
context, "SCOLAR_FONT_SIZE", formsemestre_id
"SCOLAR_FONT_SIZE", formsemestre_id
)
cell_style.fontName = sco_preferences.get_preference(
context, "PV_FONTNAME", formsemestre_id
"PV_FONTNAME", formsemestre_id
)
cell_style.leading = 1.0 * sco_preferences.get_preference(
context, "SCOLAR_FONT_SIZE", formsemestre_id
"SCOLAR_FONT_SIZE", formsemestre_id
) # vertical space
i = e["identite"]
l["nomprenom"] = [
@ -510,13 +511,13 @@ def formsemestre_pvjury(
"""Page récapitulant les décisions de jury
dpv: result of dict_pvjury
"""
footer = html_sco_header.sco_footer(context, REQUEST)
footer = html_sco_header.sco_footer()
dpv = dict_pvjury(context, formsemestre_id, with_prev=True)
if not dpv:
if format == "html":
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "<h2>Aucune information disponible !</h2>"
+ footer
)
@ -540,7 +541,7 @@ def formsemestre_pvjury(
caption="Décisions jury pour " + sem["titreannee"],
html_class="table_leftalign",
html_sortable=True,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
if format != "html":
return tab.make_page(
@ -602,7 +603,7 @@ def formsemestre_pvjury(
columns_ids=("code", "count", "expl"),
html_class="table_leftalign",
html_sortable=True,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
).html()
)
H.append("<p></p>") # force space at bottom
@ -634,7 +635,7 @@ def formsemestre_pvjury_pdf(
etuddescr = ""
if not group_ids:
# tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(context, formsemestre_id)]
group_ids = [sco_groups.get_default_group(formsemestre_id)]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
@ -659,7 +660,7 @@ def formsemestre_pvjury_pdf(
F = [
"""<p><em>Voir aussi si besoin les réglages sur la page "Paramétrage" (accessible à l'administrateur du département).</em>
</p>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
descr = descrform_pvjury(context, sem)
if etudid:
@ -687,7 +688,7 @@ def formsemestre_pvjury_pdf(
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] + "\n".join(F)
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_pvjury?formsemestre_id=%s" % (formsemestre_id)
)
else:
@ -818,7 +819,7 @@ def formsemestre_lettres_individuelles(
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
if not group_ids:
# tous les inscrits du semestre
group_ids = [sco_groups.get_default_group(context, formsemestre_id)]
group_ids = [sco_groups.get_default_group(formsemestre_id)]
groups_infos = sco_groups_view.DisplayedGroupsInfos(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
)
@ -839,7 +840,7 @@ def formsemestre_lettres_individuelles(
"""
% formsemestre_id,
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
descr = descrform_lettres_individuelles()
menu_choix_groupe = (
"""<div class="group_ids_sel_menu">Groupes d'étudiants à lister: """
@ -861,7 +862,7 @@ def formsemestre_lettres_individuelles(
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] + F
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_pvjury?formsemestre_id=%s" % (formsemestre_id)
)
else:
@ -881,7 +882,7 @@ def formsemestre_lettres_individuelles(
finally:
PDFLOCK.release()
if not pdfdoc:
return REQUEST.RESPONSE.redirect(
return flask.redirect(
"formsemestre_status?formsemestre_id={}&head_message=Aucun%20%C3%A9tudiant%20n%27a%20de%20d%C3%A9cision%20de%20jury".format(
formsemestre_id
)

View File

@ -349,7 +349,7 @@ def pdf_lettres_individuelles(
sco_etud.fill_etuds_info(etuds)
#
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
prefs = sco_preferences.SemPreferences(context, formsemestre_id)
prefs = sco_preferences.SemPreferences(formsemestre_id)
params = {
"date_jury": date_jury,
"date_commission": date_commission,
@ -358,10 +358,8 @@ def pdf_lettres_individuelles(
"htab2": "1cm",
}
# copie preferences
for name in sco_preferences.get_base_preferences(context).prefs_name:
params[name] = sco_preferences.get_preference(
context, name, sem["formsemestre_id"]
)
for name in sco_preferences.get_base_preferences().prefs_name:
params[name] = sco_preferences.get_preference(name, sem["formsemestre_id"])
bookmarks = {}
objects = [] # list of PLATYPUS objects
@ -432,9 +430,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
objects = []
style = reportlab.lib.styles.ParagraphStyle({})
style.fontSize = 14
style.fontName = sco_preferences.get_preference(
context, "PV_FONTNAME", formsemestre_id
)
style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id)
style.leading = 18
style.alignment = TA_JUSTIFY
@ -449,7 +445,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
params["decisions_ue_descr_plural"] = ""
params["INSTITUTION_CITY"] = sco_preferences.get_preference(
context, "INSTITUTION_CITY", formsemestre_id
"INSTITUTION_CITY", formsemestre_id
)
if decision["prev_decision_sem"]:
params["prev_semestre_id"] = decision["prev"]["semestre_id"]
@ -517,9 +513,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
# Corps de la lettre:
objects += sco_bulletins_pdf.process_field(
context,
sco_preferences.get_preference(
context, "PV_LETTER_TEMPLATE", sem["formsemestre_id"]
),
sco_preferences.get_preference("PV_LETTER_TEMPLATE", sem["formsemestre_id"]),
params,
style,
suppress_empty_pars=True,
@ -531,7 +525,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
if Se.semestre_non_terminal:
sig = (
sco_preferences.get_preference(
context, "PV_LETTER_PASSAGE_SIGNATURE", formsemestre_id
"PV_LETTER_PASSAGE_SIGNATURE", formsemestre_id
)
% params
)
@ -548,7 +542,7 @@ def pdf_lettre_individuelle(sem, decision, etud, params, signature=None, context
else:
sig = (
sco_preferences.get_preference(
context, "PV_LETTER_DIPLOMA_SIGNATURE", formsemestre_id
"PV_LETTER_DIPLOMA_SIGNATURE", formsemestre_id
)
% params
)
@ -592,16 +586,14 @@ def _make_signature_image(signature, leftindent, formsemestre_id, context=None):
width, height = im.size
pdfheight = (
1.0
* sco_preferences.get_preference(
context, "pv_sig_image_height", formsemestre_id
)
* sco_preferences.get_preference("pv_sig_image_height", formsemestre_id)
* mm
)
f.seek(0, 0)
style = styles.ParagraphStyle({})
style.leading = 1.0 * sco_preferences.get_preference(
context, "SCOLAR_FONT_SIZE", formsemestre_id
"SCOLAR_FONT_SIZE", formsemestre_id
) # vertical space
style.leftIndent = leftindent
return Table(
@ -681,7 +673,7 @@ def pvjury_pdf(
author="%s %s (E. Viennet)" % (VERSION.SCONAME, VERSION.SCOVERSION),
title=SU("PV du jury de %s" % sem["titre_num"]),
subject="PV jury",
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
)
@ -719,9 +711,7 @@ def _pvjury_pdf_type(
style = reportlab.lib.styles.ParagraphStyle({})
style.fontSize = 12
style.fontName = sco_preferences.get_preference(
context, "PV_FONTNAME", formsemestre_id
)
style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id)
style.leading = 18
style.alignment = TA_JUSTIFY
@ -729,7 +719,7 @@ def _pvjury_pdf_type(
bulletStyle = reportlab.lib.styles.ParagraphStyle({})
bulletStyle.fontSize = 12
bulletStyle.fontName = sco_preferences.get_preference(
context, "PV_FONTNAME", formsemestre_id
"PV_FONTNAME", formsemestre_id
)
bulletStyle.leading = 12
bulletStyle.alignment = TA_JUSTIFY
@ -748,7 +738,7 @@ def _pvjury_pdf_type(
"""
% (
titre_jury,
sco_preferences.get_preference(context, "DeptName", formsemestre_id),
sco_preferences.get_preference("DeptName", formsemestre_id),
sem["anneescolaire"],
),
style,
@ -766,7 +756,7 @@ def _pvjury_pdf_type(
objects += sco_pdf.makeParas(
"""<para align="center"><b>Semestre: %s</b></para>""" % sem["titre"], style
)
if sco_preferences.get_preference(context, "PV_TITLE_WITH_VDI", formsemestre_id):
if sco_preferences.get_preference("PV_TITLE_WITH_VDI", formsemestre_id):
objects += sco_pdf.makeParas(
"""<para align="center">VDI et Code: %s</para>""" % (VDICode or ""), style
)
@ -778,13 +768,11 @@ def _pvjury_pdf_type(
objects += sco_pdf.makeParas(
"<para>"
+ sco_preferences.get_preference(context, "PV_INTRO", formsemestre_id)
+ sco_preferences.get_preference("PV_INTRO", formsemestre_id)
% {
"Decnum": numeroArrete,
"VDICode": VDICode,
"UnivName": sco_preferences.get_preference(
context, "UnivName", formsemestre_id
),
"UnivName": sco_preferences.get_preference("UnivName", formsemestre_id),
"Type": titre_jury,
"Date": date_commission, # deprecated
"date_commission": date_commission,
@ -811,13 +799,11 @@ def _pvjury_pdf_type(
# Make a new cell style and put all cells in paragraphs
cell_style = styles.ParagraphStyle({})
cell_style.fontSize = sco_preferences.get_preference(
context, "SCOLAR_FONT_SIZE", formsemestre_id
)
cell_style.fontName = sco_preferences.get_preference(
context, "PV_FONTNAME", formsemestre_id
"SCOLAR_FONT_SIZE", formsemestre_id
)
cell_style.fontName = sco_preferences.get_preference("PV_FONTNAME", formsemestre_id)
cell_style.leading = 1.0 * sco_preferences.get_preference(
context, "SCOLAR_FONT_SIZE", formsemestre_id
"SCOLAR_FONT_SIZE", formsemestre_id
) # vertical space
LINEWIDTH = 0.5
table_style = [
@ -825,7 +811,7 @@ def _pvjury_pdf_type(
"FONTNAME",
(0, 0),
(-1, 0),
sco_preferences.get_preference(context, "PV_FONTNAME", formsemestre_id),
sco_preferences.get_preference("PV_FONTNAME", formsemestre_id),
),
("LINEBELOW", (0, 0), (-1, 0), LINEWIDTH, Color(0, 0, 0)),
("GRID", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
@ -844,7 +830,7 @@ def _pvjury_pdf_type(
widths = [6 * cm, 2.8 * cm, 2.8 * cm, None, None, None, None]
if dpv["has_prev"]:
widths[2:2] = [2.8 * cm]
if sco_preferences.get_preference(context, "bul_show_mention", formsemestre_id):
if sco_preferences.get_preference("bul_show_mention", formsemestre_id):
widths += [None]
objects.append(Table(Pt, repeatRows=1, colWidths=widths, style=table_style))
@ -853,10 +839,8 @@ def _pvjury_pdf_type(
"""<para spaceBefore="10mm" align="right">
%s, %s</para>"""
% (
sco_preferences.get_preference(context, "DirectorName", formsemestre_id)
or "",
sco_preferences.get_preference(context, "DirectorTitle", formsemestre_id)
or "",
sco_preferences.get_preference("DirectorName", formsemestre_id) or "",
sco_preferences.get_preference("DirectorTitle", formsemestre_id) or "",
),
style,
)
@ -877,7 +861,7 @@ def _pvjury_pdf_type(
"FONTNAME",
(0, 0),
(-1, 0),
sco_preferences.get_preference(context, "PV_FONTNAME", formsemestre_id),
sco_preferences.get_preference("PV_FONTNAME", formsemestre_id),
),
("LINEBELOW", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),
("LINEABOVE", (0, 0), (-1, -1), LINEWIDTH, Color(0, 0, 0)),

View File

@ -85,9 +85,7 @@ def formsemestre_recapcomplet(
) # cache les colonnes des modules
pref_override = int(pref_override)
if pref_override:
hidebac = int(
sco_preferences.get_preference(context, "recap_hidebac", formsemestre_id)
)
hidebac = int(sco_preferences.get_preference("recap_hidebac", formsemestre_id))
else:
hidebac = int(hidebac)
xml_with_decisions = int(xml_with_decisions)
@ -97,8 +95,6 @@ def formsemestre_recapcomplet(
if not isFile:
H += [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Récapitulatif",
no_side_bar=True,
init_qtip=True,
@ -175,7 +171,7 @@ def formsemestre_recapcomplet(
"""<p><a class="stdlink" href="formsemestre_pvjury?formsemestre_id=%s">Voir les décisions du jury</a></p>"""
% formsemestre_id
)
if sco_permissions_check.can_validate_sem(context, REQUEST, formsemestre_id):
if sco_permissions_check.can_validate_sem(formsemestre_id):
H.append("<p>")
if modejury:
H.append(
@ -188,13 +184,13 @@ def formsemestre_recapcomplet(
% formsemestre_id
)
H.append("</p>")
if sco_preferences.get_preference(context, "use_ue_coefs", formsemestre_id):
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
H.append(
"""
<p class="infop">utilise les coefficients d'UE pour calculer la moyenne générale.</p>
"""
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "".join(H) # HTML or binary data...
@ -220,7 +216,7 @@ def do_formsemestre_recapcomplet(
elif format == "csv":
return scu.sendCSVFile(REQUEST, data, filename)
elif format[:3] == "xls":
return sco_excel.sendExcelFile(REQUEST, data, filename)
return sco_excel.send_excel_file(REQUEST, data, filename)
elif format == "json":
return scu.sendJSON(REQUEST, data)
else:
@ -497,9 +493,7 @@ def make_formsemestre_recapcomplet(
if key == "nb_valid_evals":
l.append("")
elif key == "coef":
if sco_preferences.get_preference(
context, "use_ue_coefs", formsemestre_id
):
if sco_preferences.get_preference("use_ue_coefs", formsemestre_id):
l.append("%2.3f" % ue["coefficient"])
else:
l.append("")
@ -782,15 +776,15 @@ def make_formsemestre_recapcomplet(
semname = sem["titre_num"].replace(" ", "_")
date = time.strftime("%d-%m-%Y")
if format == "xls":
filename = "notes_modules-%s-%s.xls" % (semname, date)
filename = "notes_modules-%s-%s%s" % (semname, date, scu.XLSX_SUFFIX)
else:
filename = "notes_modules_evals-%s-%s.xls" % (semname, date)
xls = sco_excel.Excel_SimpleTable(
filename = "notes_modules_evals-%s-%s%s" % (semname, date, scu.XLSX_SUFFIX)
xls = sco_excel.excel_simple_table(
titles=["etudid", "code_nip"] + F[0][:-2],
lines=[
[x[-1], x[-2]] + x[:-2] for x in F[1:]
], # reordonne cols (etudid et nip en 1er)
SheetName="notes %s %s" % (semname, date),
sheet_name="notes %s %s" % (semname, date),
)
return xls, filename, "xls"
else:
@ -808,9 +802,7 @@ def _list_notes_evals(context, evals, etudid):
or e["etat"]["evalattente"]
or e["publish_incomplete"]
):
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
context, e["evaluation_id"]
)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(e["evaluation_id"])
if etudid in NotesDB:
val = NotesDB[etudid]["value"]
else:
@ -880,7 +872,7 @@ def _formsemestre_recapcomplet_xml(
doc = ElementTree.Element(
"recapsemestre", formsemestre_id=formsemestre_id, date=docdate
)
evals = sco_evaluations.do_evaluation_etat_in_sem(context, formsemestre_id)
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
doc.append(
ElementTree.Element(
"evals_info",
@ -924,7 +916,7 @@ def _formsemestre_recapcomplet_json(
docdate = ""
else:
docdate = datetime.datetime.now().isoformat()
evals = sco_evaluations.do_evaluation_etat_in_sem(context, formsemestre_id)
evals = sco_evaluations.do_evaluation_etat_in_sem(formsemestre_id)
J = {
"docdate": docdate,
"formsemestre_id": formsemestre_id,

View File

@ -38,6 +38,7 @@ import datetime
from operator import itemgetter
from flask import url_for, g
import pydot
import app.scodoc.sco_utils as scu
from app.scodoc import notesdb as ndb
@ -201,7 +202,7 @@ def _results_by_category(
bottom_titles=bottom_titles,
html_col_width="4em",
html_sortable=True,
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
@ -364,7 +365,7 @@ def formsemestre_report_counts(
if format != "html":
return t
H = [
html_sco_header.sco_header(context, REQUEST, page_title=title),
html_sco_header.sco_header(page_title=title),
t,
"\n".join(F),
"""<p class="help">Le tableau affiche le nombre d'étudiants de ce semestre dans chacun
@ -372,7 +373,7 @@ def formsemestre_report_counts(
pour les lignes et les colonnes. Le <tt>codedecision</tt> est le code de la décision
du jury.
</p>""",
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -654,7 +655,7 @@ def table_suivi_cohorte(
caption="Suivi cohorte " + pp + sem["titreannee"] + dbac,
page_title="Suivi cohorte " + sem["titreannee"],
html_class="table_cohorte",
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
# Explication: liste des semestres associés à chaque date
if not P:
@ -755,7 +756,7 @@ def formsemestre_suivi_cohorte(
)
H = [
html_sco_header.sco_header(context, REQUEST, page_title=tab.page_title),
html_sco_header.sco_header(page_title=tab.page_title),
"""<h2 class="formsemestre">Suivi cohorte: devenir des étudiants de ce semestre</h2>""",
_gen_form_selectetuds(
formsemestre_id,
@ -776,7 +777,7 @@ def formsemestre_suivi_cohorte(
t,
help,
expl,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -1167,7 +1168,7 @@ def table_suivi_parcours(
"nb": len(etuds),
"codeparcours": len(etuds),
},
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
)
return tab
@ -1228,8 +1229,6 @@ def formsemestre_suivi_parcours(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=tab.page_title,
init_qtip=True,
javascripts=["js/etud_info.js"],
@ -1237,7 +1236,7 @@ def formsemestre_suivi_parcours(
"""<h2 class="formsemestre">Parcours suivis par les étudiants de ce semestre</h2>""",
"\n".join(F),
t,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
@ -1255,8 +1254,6 @@ def graph_parcours(
statut="",
):
""""""
if not scu.WITH_PYDOT:
raise ScoValueError("pydot module is not installed")
etuds, bacs, bacspecialites, annee_bacs, civilites, statuts = tsp_etud_list(
context,
formsemestre_id,
@ -1342,10 +1339,10 @@ def graph_parcours(
edges[(s["formsemestre_id"], nid)].add(etudid)
diploma_nodes.append(nid)
#
g = scu.pydot.graph_from_edges(list(edges.keys()))
g = scu.graph_from_edges(list(edges.keys()))
for fid in isolated_nodes:
if not fid in connected_nodes:
n = scu.pydot.Node(name=fid)
n = pydot.Node(name=fid)
g.add_node(n)
g.set("rankdir", "LR") # left to right
g.set_fontname("Helvetica")
@ -1353,7 +1350,7 @@ def graph_parcours(
g.set_bgcolor("#fffff0") # ou 'transparent'
# titres des semestres:
for s in sems.values():
n = scu.pydot_get_node(g, s["formsemestre_id"])
n = g.get_node(s["formsemestre_id"])[0]
log("s['formsemestre_id'] = %s" % s["formsemestre_id"])
log("n=%s" % n)
log("get=%s" % g.get_node(s["formsemestre_id"]))
@ -1378,31 +1375,31 @@ def graph_parcours(
n.set_shape("box")
n.set_URL("formsemestre_status?formsemestre_id=" + s["formsemestre_id"])
# semestre de depart en vert
n = scu.pydot_get_node(g, formsemestre_id)
n = g.get_node(formsemestre_id)[0]
n.set_color("green")
# demissions en rouge, octagonal
for nid in dem_nodes.values():
n = scu.pydot_get_node(g, nid)
n = g.get_node(nid)[0]
n.set_color("red")
n.set_shape("octagon")
n.set("label", "Dem.")
# NAR en rouge, Mcircle
for nid in nar_nodes.values():
n = scu.pydot_get_node(g, nid)
n = g.get_node(nid)[0]
n.set_color("red")
n.set_shape("Mcircle")
n.set("label", sco_codes_parcours.NAR)
# diplomes:
for nid in diploma_nodes:
n = scu.pydot_get_node(g, nid)
n = g.get_node(nid)[0]
n.set_color("red")
n.set_shape("ellipse")
n.set("label", "Diplome") # bug si accent (pas compris pourquoi)
# Arètes:
bubbles = {} # substitue titres pour bulle aides: src_id:dst_id : etud_descr
for (src_id, dst_id) in edges.keys():
e = g.get_edge(src_id, dst_id)
e = g.get_edge(src_id, dst_id)[0]
e.set("arrowhead", "normal")
e.set("arrowsize", 1)
e.set_label(len(edges[(src_id, dst_id)]))
@ -1416,7 +1413,7 @@ def graph_parcours(
# Genere graphe
_, path = tempfile.mkstemp(".gr")
g.write(path=path, format=format)
data = open(path, "r").read()
data = open(path, "rb").read()
log("dot generated %d bytes in %s format" % (len(data), format))
if not data:
log("graph.to_string=%s" % g.to_string())
@ -1426,7 +1423,8 @@ def graph_parcours(
os.unlink(path)
if format == "svg":
# dot génère un document XML complet, il faut enlever l'en-tête
data = "<svg" + "<svg".join(data.split("<svg")[1:])
data_str = data.decode("utf-8")
data = "<svg" + "<svg".join(data_str.split("<svg")[1:])
# Substitution des titres des URL des aretes pour bulles aide
def repl(m):
return '<a title="%s"' % bubbles[m.group("sd")]
@ -1528,14 +1526,16 @@ def formsemestre_graph_parcours(
REQUEST.RESPONSE.setHeader("content-type", "image/png")
return doc
elif format == "html":
url_kw = {
"scodoc_dept": g.scodoc_dept,
"formsemestre_id": formsemestre_id,
"bac": bac,
"specialite": bacspecialite,
"civilite": civilite,
"statut": statut,
}
if only_primo:
op = "only_primo=on&"
else:
op = ""
url = six.moves.urllib.parse.quote(
"formsemestre_graph_parcours?formsemestre_id=%s&%sbac=%s&bacspecialite=%s&civilite=%s&statut=%s&format="
% (formsemestre_id, op, bac, bacspecialite, civilite, statut)
)
url_kw["only_primo"] = "on"
(
doc,
etuds,
@ -1557,8 +1557,6 @@ def formsemestre_graph_parcours(
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Parcours étudiants de %(titreannee)s" % sem,
no_side_bar=True,
),
@ -1583,12 +1581,11 @@ def formsemestre_graph_parcours(
),
"""<p>Origine et devenir des étudiants inscrits dans %(titreannee)s"""
% sem,
# En Debian 4, dot ne genere pas du pdf, et epstopdf ne marche pas sur le .ps ou ps2 générés par dot
# mais c'est OK en Debian 5
"""(<a href="%spdf">version pdf</a>""" % url,
""", <a href="%spng">image PNG</a>)""" % url,
"""(<a href="%s">version pdf</a>"""
% url_for("notes.formsemestre_graph_parcours", format="pdf", **url_kw),
""", <a href="%s">image PNG</a>)"""
% url_for("notes.formsemestre_graph_parcours", format="png", **url_kw),
"""</p>""",
"""<p class="help">Cette page ne s'affiche correctement que sur les navigateurs récents.</p>""",
"""<p class="help">Le graphe permet de suivre les étudiants inscrits dans le semestre
sélectionné (dessiné en vert). Chaque rectangle représente un semestre (cliquez dedans
pour afficher son tableau de bord). Les flèches indiquent le nombre d'étudiants passant
@ -1596,7 +1593,7 @@ def formsemestre_graph_parcours(
passant la souris sur le chiffre).
</p>"""
% MAX_ETUD_IN_DESCR,
html_sco_header.sco_footer(context, REQUEST),
html_sco_header.sco_footer(),
]
return "\n".join(H)
else:

View File

@ -29,10 +29,14 @@
Formulaire revu en juillet 2016
"""
import sys
import time
import datetime
import psycopg2
import flask
from flask_login import current_user
import app.scodoc.sco_utils as scu
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import log
@ -165,18 +169,17 @@ def do_evaluation_upload_xls(context, REQUEST):
authuser = REQUEST.AUTHENTICATED_USER
evaluation_id = REQUEST.form["evaluation_id"]
comment = REQUEST.form["comment"]
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(
context, moduleimpl_id=E["moduleimpl_id"]
)[0]
# Check access
# (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
# XXX imaginer un redirect + msg erreur
raise AccessDenied("Modification des notes impossible pour %s" % authuser)
#
data = REQUEST.form["notefile"].read()
diag, lines = sco_excel.Excel_to_list(data)
diag, lines = sco_excel.excel_file_to_list(REQUEST.form["notefile"])
try:
if not lines:
raise InvalidNoteValue()
@ -221,7 +224,7 @@ def do_evaluation_upload_xls(context, REQUEST):
ni += 1
except:
diag.append(
'Erreur: feuille invalide ! (erreur ligne %d)<br/>"%s"'
'Erreur: Ligne invalide ! (erreur ligne %d)<br/>"%s"'
% (ni, str(lines[ni]))
)
raise InvalidNoteValue()
@ -243,9 +246,7 @@ def do_evaluation_upload_xls(context, REQUEST):
context, authuser, evaluation_id, L, comment
)
# news
E = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": evaluation_id}
)[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(
context, moduleimpl_id=E["moduleimpl_id"]
)[0]
@ -255,8 +256,6 @@ def do_evaluation_upload_xls(context, REQUEST):
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=M["moduleimpl_id"],
text='Chargement notes dans <a href="%(url)s">%(titre)s</a>' % mod,
@ -284,25 +283,23 @@ def do_evaluation_upload_xls(context, REQUEST):
return 0, msg + "<p>(pas de notes modifiées)</p>"
def do_evaluation_set_missing(
context, evaluation_id, value, REQUEST=None, dialog_confirmed=False
):
def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
"""Initialisation des notes manquantes"""
authuser = REQUEST.AUTHENTICATED_USER
evaluation_id = REQUEST.form["evaluation_id"]
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
# ? evaluation_id = REQUEST.form["evaluation_id"]
context = None # XXX #context
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_withmodule_list(
context, moduleimpl_id=E["moduleimpl_id"]
)[0]
# Check access
# (admin, respformation, and responsable_id)
if not sco_permissions_check.can_edit_notes(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
# XXX imaginer un redirect + msg erreur
raise AccessDenied("Modification des notes impossible pour %s" % authuser)
raise AccessDenied("Modification des notes impossible pour %s" % current_user)
#
NotesDB = sco_evaluations.do_evaluation_get_all_notes(context, evaluation_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True, include_dems=False
evaluation_id, getallstudents=True, include_dems=False
)
notes = []
for etudid in etudids: # pour tous les inscrits
@ -315,15 +312,14 @@ def do_evaluation_set_missing(
diag = "Valeur %s invalide" % value
if diag:
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ '<h2>%s</h2><p><a href="saisie_notes?evaluation_id=%s">Recommencer</a>'
% (diag, evaluation_id)
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
# Confirm action
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Mettre toutes les notes manquantes de l'évaluation
à la valeur %s ?</h2>
<p>Seuls les étudiants pour lesquels aucune note (ni valeur, ni ABS, ni EXC)
@ -333,28 +329,25 @@ def do_evaluation_set_missing(
"""
% (value, len(L)),
dest_url="",
REQUEST=REQUEST,
cancel_url="saisie_notes?evaluation_id=%s" % evaluation_id,
parameters={"evaluation_id": evaluation_id, "value": value},
)
# ok
comment = "Initialisation notes manquantes"
nb_changed, _, _ = _notes_add(context, authuser, evaluation_id, L, comment)
nb_changed, _, _ = _notes_add(context, current_user, evaluation_id, L, comment)
# news
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=M["moduleimpl_id"],
text='Initialisation notes dans <a href="%(url)s">%(titre)s</a>' % mod,
url=mod["url"],
)
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ """<h2>%d notes changées</h2>
<ul>
<li><a class="stdlink" href="saisie_notes?evaluation_id=%s">
@ -364,45 +357,43 @@ def do_evaluation_set_missing(
</ul>
"""
% (nb_changed, evaluation_id, M["moduleimpl_id"])
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
def evaluation_suppress_alln(context, evaluation_id, REQUEST, dialog_confirmed=False):
def evaluation_suppress_alln(evaluation_id, dialog_confirmed=False):
"suppress all notes in this eval"
authuser = REQUEST.AUTHENTICATED_USER
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
context = None # XXX #context
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
if sco_permissions_check.can_edit_notes(
context, authuser, E["moduleimpl_id"], allow_ens=False
current_user, E["moduleimpl_id"], allow_ens=False
):
# On a le droit de modifier toutes les notes
# recupere les etuds ayant une note
NotesDB = sco_evaluations.do_evaluation_get_all_notes(context, evaluation_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
elif sco_permissions_check.can_edit_notes(
context, authuser, E["moduleimpl_id"], allow_ens=True
current_user, E["moduleimpl_id"], allow_ens=True
):
# Enseignant associé au module: ne peut supprimer que les notes qu'il a saisi
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
context, evaluation_id, by_uid=str(authuser)
evaluation_id, by_uid=current_user.user_name
)
else:
raise AccessDenied("Modification des notes impossible pour %s" % authuser)
raise AccessDenied("Modification des notes impossible pour %s" % current_user)
notes = [(etudid, scu.NOTES_SUPPRESS) for etudid in NotesDB.keys()]
if not dialog_confirmed:
nb_changed, nb_suppress, existing_decisions = _notes_add(
context, authuser, evaluation_id, notes, do_it=False
context, current_user, evaluation_id, notes, do_it=False
)
msg = "<p>Confirmer la suppression des %d notes ?</p>" % nb_suppress
if existing_decisions:
msg += """<p class="warning">Important: il y a déjà des décisions de jury enregistrées, qui seront potentiellement à revoir suite à cette modification !</p>"""
return scu.confirm_dialog(
context,
msg,
dest_url="",
REQUEST=REQUEST,
OK="Supprimer les notes",
cancel_url="moduleimpl_status?moduleimpl_id=%s" % E["moduleimpl_id"],
parameters={"evaluation_id": evaluation_id},
@ -410,7 +401,7 @@ def evaluation_suppress_alln(context, evaluation_id, REQUEST, dialog_confirmed=F
# modif
nb_changed, nb_suppress, existing_decisions = _notes_add(
context, authuser, evaluation_id, notes, comment="effacer tout"
context, current_user, evaluation_id, notes, comment="effacer tout"
)
assert nb_changed == nb_suppress
H = ["<p>%s notes supprimées</p>" % nb_suppress]
@ -428,8 +419,6 @@ def evaluation_suppress_alln(context, evaluation_id, REQUEST, dialog_confirmed=F
mod["moduleimpl_id"] = M["moduleimpl_id"]
mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % mod
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=M["moduleimpl_id"],
text='Suppression des notes d\'une évaluation dans <a href="%(url)s">%(titre)s</a>'
@ -437,11 +426,7 @@ def evaluation_suppress_alln(context, evaluation_id, REQUEST, dialog_confirmed=F
url=mod["url"],
)
return (
html_sco_header.sco_header(context, REQUEST)
+ "\n".join(H)
+ html_sco_header.sco_footer(context, REQUEST)
)
return html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True):
@ -461,7 +446,7 @@ def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True):
# Verifie inscription et valeur note
_ = {}.fromkeys(
sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True, include_dems=True
evaluation_id, getallstudents=True, include_dems=True
)
)
for (etudid, value) in notes:
@ -470,13 +455,13 @@ def _notes_add(context, uid, evaluation_id, notes, comment=None, do_it=True):
"etudiant %s: valeur de note invalide (%s)" % (etudid, value)
)
# Recherche notes existantes
NotesDB = sco_evaluations.do_evaluation_get_all_notes(context, evaluation_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(evaluation_id)
# Met a jour la base
cnx = ndb.GetDBConnexion(autocommit=False)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
nb_changed = 0
nb_suppress = 0
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
existing_decisions = (
[]
@ -578,24 +563,22 @@ def saisie_notes_tableur(context, 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(
context, {"evaluation_id": evaluation_id}
)
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(context, moduleimpl_id=E["moduleimpl_id"])[0]
formsemestre_id = M["formsemestre_id"]
if not sco_permissions_check.can_edit_notes(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
+ """<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>
"""
% E["moduleimpl_id"]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
if E["description"]:
@ -615,15 +598,13 @@ def saisie_notes_tableur(context, evaluation_id, group_ids=[], REQUEST=None):
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=page_title,
javascripts=sco_groups_view.JAVASCRIPTS,
cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True,
),
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
evaluation_id=evaluation_id, REQUEST=REQUEST
),
"""<span class="eval_title">Saisie des notes par fichier</span>""",
]
@ -648,7 +629,7 @@ def saisie_notes_tableur(context, evaluation_id, group_ids=[], REQUEST=None):
H.append(
"""<div class="saisienote_etape2">
<span class="titredivsaisienote">Etape 2 : chargement d'un fichier de notes</span>""" #'
<span class="titredivsaisienote">Etape 2 : chargement d'un fichier de notes</span>""" # '
)
nf = TrivialFormulator(
@ -706,12 +687,12 @@ def saisie_notes_tableur(context, evaluation_id, group_ids=[], REQUEST=None):
#
H.append("""</div><h3>Autres opérations</h3><ul>""")
if sco_permissions_check.can_edit_notes(
context, REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
):
H.append(
"""
<li>
<form action="do_evaluation_set_missing" method="get">
<form action="do_evaluation_set_missing" method="GET">
Mettre toutes les notes manquantes à <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/>
<input type="hidden" name="evaluation_id" value="%s"/>
@ -721,7 +702,7 @@ def saisie_notes_tableur(context, evaluation_id, group_ids=[], REQUEST=None):
<li><a class="stdlink" href="evaluation_suppress_alln?evaluation_id=%s">Effacer toutes les notes de cette évaluation</a> (ceci permet ensuite de supprimer l'évaluation si besoin)
</li>"""
% (evaluation_id, evaluation_id)
) #'
) # '
H.append(
"""<li><a class="stdlink" href="moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s">Revenir au module</a></li>
<li><a class="stdlink" href="saisie_notes?evaluation_id=%(evaluation_id)s">Revenir au formulaire de saisie</a></li>
@ -750,15 +731,13 @@ def saisie_notes_tableur(context, evaluation_id, group_ids=[], REQUEST=None):
</ol>
"""
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def feuille_saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
evals = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": evaluation_id}
)
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")
E = evals[0]
@ -801,7 +780,7 @@ def feuille_saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
else:
getallstudents = False
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, groups, getallstudents=getallstudents, include_dems=True
evaluation_id, groups, getallstudents=getallstudents, include_dems=True
)
# une liste de liste de chaines: lignes de la feuille de calcul
@ -825,9 +804,9 @@ def feuille_saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
]
)
filename = "notes_%s_%s.xls" % (evalname, gr_title_filename)
xls = sco_excel.Excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
return sco_excel.sendExcelFile(REQUEST, xls, filename)
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)
def has_existing_decision(context, M, E, etudid):
@ -859,9 +838,7 @@ def saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
authuser = REQUEST.AUTHENTICATED_USER
authusername = str(authuser)
evals = sco_evaluations.do_evaluation_list(
context, {"evaluation_id": evaluation_id}
)
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
if not evals:
raise ScoValueError("invalid evaluation_id")
E = evals[0]
@ -871,16 +848,16 @@ def saisie_notes(context, 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(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
return (
html_sco_header.sco_header(context, REQUEST)
html_sco_header.sco_header()
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
+ """<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>
"""
% E["moduleimpl_id"]
+ html_sco_header.sco_footer(context, REQUEST)
+ html_sco_header.sco_footer()
)
# Informations sur les groupes à afficher:
@ -901,15 +878,13 @@ def saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
# HTML page:
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=page_title,
javascripts=sco_groups_view.JAVASCRIPTS + ["js/saisie_notes.js"],
cssstyles=sco_groups_view.CSSSTYLES,
init_qtip=True,
),
sco_evaluations.evaluation_describe(
context, evaluation_id=evaluation_id, REQUEST=REQUEST
evaluation_id=evaluation_id, REQUEST=REQUEST
),
'<div id="saisie_notes"><span class="eval_title">Saisie des notes</span>',
]
@ -954,7 +929,7 @@ def saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
context, E, M, groups_infos.group_ids, destination=destination, REQUEST=REQUEST
)
if form is None:
return REQUEST.RESPONSE.redirect(destination)
return flask.redirect(destination)
H.append(form)
#
H.append("</div>") # /saisie_notes
@ -972,14 +947,14 @@ def saisie_notes(context, evaluation_id, group_ids=[], REQUEST=None):
</div>"""
)
H.append(html_sco_header.sco_footer(context, REQUEST))
H.append(html_sco_header.sco_footer())
return "\n".join(H)
def _get_sorted_etuds(context, E, etudids, formsemestre_id):
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
NotesDB = sco_evaluations.do_evaluation_get_all_notes(
context, E["evaluation_id"]
E["evaluation_id"]
) # Notes existantes
cnx = ndb.GetDBConnexion()
etuds = []
@ -1053,7 +1028,7 @@ def _form_saisie_notes(context, E, M, group_ids, destination="", REQUEST=None):
formsemestre_id = M["formsemestre_id"]
etudids = sco_groups.do_evaluation_listeetuds_groups(
context, evaluation_id, getallstudents=True, include_dems=True
evaluation_id, getallstudents=True, include_dems=True
)
if not etudids:
return '<div class="ue_warning"><span>Aucun étudiant sélectionné !</span></div>'
@ -1200,7 +1175,7 @@ def _form_saisie_notes(context, E, M, group_ids, destination="", REQUEST=None):
H.append(
"""
<div>
<form action="do_evaluation_set_missing" method="get">
<form action="do_evaluation_set_missing" method="GET">
Mettre <em>toutes</em> les notes manquantes à <input type="text" size="5" name="value"/>
<input type="submit" value="OK"/>
<input type="hidden" name="evaluation_id" value="%s"/>
@ -1227,13 +1202,13 @@ def save_note(
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
% (evaluation_id, etudid, authuser, value)
)
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.do_module_list(context, args={"module_id": M["module_id"]})[0]
Mod["url"] = "Notes/moduleimpl_status?moduleimpl_id=%(moduleimpl_id)s" % M
result = {"nbchanged": 0} # JSON
# Check access: admin, respformation, or responsable_id
if not sco_permissions_check.can_edit_notes(context, authuser, E["moduleimpl_id"]):
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
result["status"] = "unauthorized"
else:
L, _, _, _, _ = _check_notes([(etudid, value)], E, Mod)
@ -1242,8 +1217,6 @@ def save_note(
context, authuser, evaluation_id, L, comment=comment, do_it=True
)
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_NOTE,
object=M["moduleimpl_id"],
text='Chargement notes dans <a href="%(url)s">%(titre)s</a>' % Mod,

View File

@ -39,6 +39,8 @@ sem_set_list(context)
"""
import flask
from app.scodoc import html_sco_header
from app.scodoc import sco_cache
from app.scodoc import sco_etape_apogee
@ -86,7 +88,6 @@ class SemSet(dict):
self["annee_scolaire"] = L[0]["annee_scolaire"]
self["sem_id"] = L[0]["sem_id"]
r = ndb.SimpleDictFetch(
context,
"SELECT formsemestre_id FROM notes_semset_formsemestre WHERE semset_id = %(semset_id)s",
{"semset_id": semset_id},
)
@ -136,9 +137,9 @@ class SemSet(dict):
self["semlinks"] = [(pattern % sem) for sem in self.sems]
self["semtitles_str"] = "<br/>".join(self["semlinks"])
def fill_formsemestres(self, REQUEST):
def fill_formsemestres(self):
for sem in self.sems:
sco_formsemestre_status.fill_formsemestre(self.context, sem, REQUEST)
sco_formsemestre_status.fill_formsemestre(sem)
ets = sco_etape_apogee.apo_get_sem_etapes(self.context, sem)
sem["etapes_apo_str"] = sco_formsemestre.etapes_apo_str(sorted(list(ets)))
@ -354,7 +355,7 @@ def do_semset_create(context, title="", annee_scolaire=None, sem_id=None, REQUES
% (title, annee_scolaire, sem_id)
)
SemSet(context, title=title, annee_scolaire=annee_scolaire, sem_id=sem_id)
return REQUEST.RESPONSE.redirect("semset_page")
return flask.redirect("semset_page")
def do_semset_delete(context, semset_id, dialog_confirmed=False, REQUEST=None):
@ -364,15 +365,13 @@ def do_semset_delete(context, semset_id, dialog_confirmed=False, REQUEST=None):
s = SemSet(context, semset_id=semset_id)
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"<h2>Suppression de l'ensemble %(title)s ?</h2>" % s,
dest_url="",
REQUEST=REQUEST,
parameters={"semset_id": semset_id},
cancel_url="semset_page",
)
s.delete()
return REQUEST.RESPONSE.redirect("semset_page")
return flask.redirect("semset_page")
def edit_semset_set_title(context, id=None, value=None, REQUEST=None):
@ -396,7 +395,7 @@ def do_semset_add_sem(context, semset_id, formsemestre_id, REQUEST=None):
s.add(formsemestre_id)
return REQUEST.RESPONSE.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
def do_semset_remove_sem(context, semset_id, formsemestre_id, REQUEST=None):
@ -407,7 +406,7 @@ def do_semset_remove_sem(context, semset_id, formsemestre_id, REQUEST=None):
s.remove(formsemestre_id)
return REQUEST.RESPONSE.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
return flask.redirect("apo_semset_maq_status?semset_id=%s" % semset_id)
# ----------------------------------------
@ -460,9 +459,7 @@ def semset_page(context, format="html", REQUEST=None):
html_sortable=True,
html_class="table_leftalign",
filename="semsets",
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
if format != "html":
return tab.make_page(context, format=format, REQUEST=REQUEST)
@ -470,8 +467,6 @@ def semset_page(context, format="html", REQUEST=None):
page_title = "Ensembles de semestres"
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title=page_title,
init_qtip=True,
javascripts=["libjs/jinplace-1.2.1.min.js"],
@ -484,7 +479,7 @@ def semset_page(context, format="html", REQUEST=None):
]
H.append(tab.html())
annee_courante = int(scu.AnneeScolaire(REQUEST))
annee_courante = int(scu.AnneeScolaire())
menu_annee = "\n".join(
[
'<option value="%s">%s</option>' % (i, i)
@ -530,4 +525,4 @@ def semset_page(context, format="html", REQUEST=None):
"""
)
return "\n".join(H) + html_sco_header.sco_footer(context, REQUEST)
return "\n".join(H) + html_sco_header.sco_footer()

View File

@ -107,10 +107,8 @@ def formsemestre_synchro_etuds(
"""
% sem
)
header = html_sco_header.sco_header(
context, REQUEST, page_title="Synchronisation étudiants"
)
footer = html_sco_header.sco_footer(context, REQUEST)
header = html_sco_header.sco_header(page_title="Synchronisation étudiants")
footer = html_sco_header.sco_footer()
base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
if anneeapogee:
base_url += "&anneeapogee=%s" % anneeapogee
@ -145,7 +143,7 @@ def formsemestre_synchro_etuds(
base_url=base_url,
read_only=read_only,
)
return sco_excel.sendExcelFile(REQUEST, xls, filename + ".xls")
return sco_excel.send_excel_file(REQUEST, xls, filename + scu.XLSX_SUFFIX)
H = [header]
if not submitted:
@ -210,13 +208,11 @@ def formsemestre_synchro_etuds(
H.append(
scu.confirm_dialog(
context,
dest_url="formsemestre_synchro_etuds",
add_headers=False,
cancel_url="formsemestre_synchro_etuds?formsemestre_id="
+ formsemestre_id,
OK="Effectuer l'opération",
REQUEST=REQUEST,
parameters={
"formsemestre_id": formsemestre_id,
"etuds": ",".join(etuds),
@ -522,9 +518,7 @@ def list_all(context, etudsapo_set):
def formsemestre_synchro_etuds_help(context, sem):
sem["default_group_id"] = sco_groups.get_default_group(
context, sem["formsemestre_id"]
)
sem["default_group_id"] = sco_groups.get_default_group(sem["formsemestre_id"])
return (
"""<div class="pas_help pas_help_left"><h3><a name="help">Explications</a></h3>
<p>Cette page permet d'importer dans le semestre destination
@ -676,8 +670,6 @@ def do_import_etuds_from_portal(context, sem, a_importer, etudsapo_ident, REQUES
raise
sco_news.add(
context,
REQUEST,
typ=sco_news.NEWS_INSCR,
text="Import Apogée de %d étudiants en " % len(created_etudids),
object=sem["formsemestre_id"],
@ -701,7 +693,7 @@ def do_import_etud_admission(
"codelycee": get_opt_str(etud, "lycee"),
"boursier": get_opt_str(etud, "bourse"),
}
log("do_import_etud_admission: etud=%s" % pprint.pformat(etud))
# log("do_import_etud_admission: etud=%s" % pprint.pformat(etud))
al = sco_etud.admission_list(cnx, args={"etudid": etudid})
if not al:
sco_etud.admission_create(cnx, args) # -> adm_id

View File

@ -73,7 +73,6 @@ class ScoTag(object):
if not self.title:
raise ScoValueError("invalid empty tag")
r = ndb.SimpleDictFetch(
context,
"SELECT * FROM " + self.tag_table + " WHERE title = %(title)s",
{"title": self.title},
)
@ -81,13 +80,12 @@ class ScoTag(object):
self.tag_id = r[0]["tag_id"]
else:
# Create new tag:
log("creating new tag: %s" % self.title)
# log("creating new tag: %s" % self.title)
cnx = ndb.GetDBConnexion()
oid = ndb.DBInsertDict(
cnx, self.tag_table, {"title": self.title}, commit=True
)
self.tag_id = ndb.SimpleDictFetch(
context,
"SELECT tag_id FROM " + self.tag_table + " WHERE oid=%(oid)s",
{"oid": oid},
)[0]["tag_id"]
@ -123,7 +121,7 @@ class ScoTag(object):
args,
)
if not r:
log("tag %s with %s" % (object_id, self.title))
# log("tag %s with %s" % (object_id, self.title))
cnx = ndb.GetDBConnexion()
ndb.DBInsertDict(cnx, self.assoc_table, args, commit=True)
@ -132,7 +130,7 @@ class ScoTag(object):
If no more modules tagged with this tag, delete it.
Return True if Tag still exists.
"""
log("removing tag %s from %s" % (self.title, object_id))
# log("removing tag %s from %s" % (self.title, object_id))
args = {"object_id": object_id, "tag_id": self.tag_id}
ndb.SimpleQuery(
self.context,
@ -206,7 +204,6 @@ def module_tag_search(context, term, REQUEST=None):
data = []
else:
r = ndb.SimpleDictFetch(
context,
"SELECT title FROM notes_tags WHERE title LIKE %(term)s",
{"term": term + "%"},
)
@ -218,7 +215,6 @@ def module_tag_search(context, term, REQUEST=None):
def module_tag_list(context, module_id=""):
"""les noms de tags associés à ce module"""
r = ndb.SimpleDictFetch(
context,
"""SELECT t.title
FROM notes_modules_tags mt, notes_tags t
WHERE mt.tag_id = t.tag_id
@ -229,26 +225,17 @@ def module_tag_list(context, module_id=""):
return [x["title"] for x in r]
def module_tag_set(context, module_id="", taglist=[], REQUEST=None):
def module_tag_set(context, module_id="", taglist=None):
"""taglist may either be:
a string with tag names separated by commas ("un;deux")
or a list of strings (["un", "deux"])
"""
# We check permission here to allow old Admins (with only ScoChangeFormation perm)
if REQUEST: # called from Web
authuser = REQUEST.AUTHENTICATED_USER
tag_editable = authuser.has_permission(
Permission.ScoEditFormationTags
) or authuser.has_permission(Permission.ScoChangeFormation)
if not tag_editable:
raise AccessDenied("Modification des tags impossible pour %s" % authuser)
#
if not taglist:
taglist = []
elif isinstance(taglist, str):
taglist = taglist.split(",")
taglist = [t.strip() for t in taglist]
log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist))
# log("module_tag_set: module_id=%s taglist=%s" % (module_id, taglist))
# Sanity check:
Mod = sco_edit_module.do_module_list(context, args={"module_id": module_id})
if not Mod:

View File

@ -28,10 +28,7 @@
"""Photos: trombinoscopes
"""
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
import io
from zipfile import ZipFile, BadZipfile
import reportlab
from reportlab.lib.units import cm, mm
@ -46,6 +43,7 @@ from reportlab.lib.colors import Color
from reportlab.lib import colors
from PIL import Image as PILImage
import flask
from flask import url_for, g
from app.scodoc.notes_log import log
@ -100,11 +98,11 @@ def trombino(
return _listeappel_photos_pdf(context, groups_infos, REQUEST)
else:
raise Exception("invalid format")
# return _trombino_html_header(context, REQUEST) + trombino_html(context, group, members, REQUEST=REQUEST) + html_sco_header.sco_footer(context, REQUEST)
# return _trombino_html_header(context, REQUEST) + trombino_html(context, group, members, REQUEST=REQUEST) + html_sco_header.sco_footer( REQUEST)
def _trombino_html_header(context, REQUEST):
return html_sco_header.sco_header(context, REQUEST, javascripts=["js/trombino.js"])
return html_sco_header.sco_header(javascripts=["js/trombino.js"])
def trombino_html(context, groups_infos, REQUEST=None):
@ -204,7 +202,6 @@ def check_local_photos_availability(context, groups_infos, REQUEST, format=""):
return (
False,
scu.confirm_dialog(
context,
"""<p>Attention: %d photos ne sont pas disponibles et ne peuvent pas être exportées.</p><p>Vous pouvez <a class="stdlink" href="%s">exporter seulement les photos existantes</a>"""
% (
nb_missing,
@ -214,7 +211,6 @@ def check_local_photos_availability(context, groups_infos, REQUEST, format=""):
OK="Exporter seulement les photos existantes",
cancel_url="groups_view?curtab=tab-photos&"
+ groups_infos.groups_query_args,
REQUEST=REQUEST,
parameters=parameters,
),
)
@ -224,7 +220,7 @@ def check_local_photos_availability(context, groups_infos, REQUEST, format=""):
def _trombino_zip(context, groups_infos, REQUEST):
"Send photos as zip archive"
data = StringIO()
data = io.BytesIO()
Z = ZipFile(data, "w")
# assume we have the photos (or the user acknowledged the fact)
# Archive originals (not reduced) images, in JPEG
@ -232,7 +228,7 @@ def _trombino_zip(context, groups_infos, REQUEST):
im_path = sco_photos.photo_pathname(context, t, size="orig")
if not im_path:
continue
img = open(im_path).read()
img = open(im_path, "rb").read()
code_nip = t["code_nip"]
if code_nip:
filename = code_nip + ".jpg"
@ -260,10 +256,8 @@ def trombino_copy_photos(context, group_ids=[], REQUEST=None, dialog_confirmed=F
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
portal_url = sco_portal_apogee.get_portal_url(context)
header = html_sco_header.sco_header(
context, REQUEST, page_title="Chargement des photos"
)
footer = html_sco_header.sco_footer(context, REQUEST)
header = html_sco_header.sco_header(page_title="Chargement des photos")
footer = html_sco_header.sco_footer()
if not portal_url:
return (
header
@ -273,14 +267,12 @@ def trombino_copy_photos(context, group_ids=[], REQUEST=None, dialog_confirmed=F
)
if not dialog_confirmed:
return scu.confirm_dialog(
context,
"""<h2>Copier les photos du portail vers ScoDoc ?</h2>
<p>Les photos du groupe %s présentes dans ScoDoc seront remplacées par celles du portail (si elles existent).</p>
<p>(les photos sont normalement automatiquement copiées lors de leur première utilisation, l'usage de cette fonction n'est nécessaire que si les photos du portail ont été modifiées)</p>
"""
% (groups_infos.groups_titles),
dest_url="",
REQUEST=REQUEST,
cancel_url=back_url,
parameters={"group_ids": group_ids},
)
@ -340,7 +332,7 @@ def _trombino_pdf(context, groups_infos, REQUEST):
N_PER_ROW = 5 # XXX should be in ScoDoc preferences
StyleSheet = styles.getSampleStyleSheet()
report = StringIO() # in-memory document, no disk file
report = io.BytesIO() # in-memory document, no disk file
objects = [
Paragraph(
SU("Trombinoscope " + sem["titreannee"] + " " + groups_infos.groups_titles),
@ -394,7 +386,7 @@ def _trombino_pdf(context, groups_infos, REQUEST):
sco_pdf.ScolarsPageTemplate(
document,
context=context,
preferences=sco_preferences.SemPreferences(context, sem["formsemestre_id"]),
preferences=sco_preferences.SemPreferences(sem["formsemestre_id"]),
)
)
document.build(objects)
@ -414,7 +406,7 @@ def _listeappel_photos_pdf(context, groups_infos, REQUEST):
# ROWS_PER_PAGE = 26 # XXX should be in ScoDoc preferences
StyleSheet = styles.getSampleStyleSheet()
report = StringIO() # in-memory document, no disk file
report = io.BytesIO() # in-memory document, no disk file
objects = [
Paragraph(
SU(
@ -471,7 +463,7 @@ def _listeappel_photos_pdf(context, groups_infos, REQUEST):
sco_pdf.ScolarsPageTemplate(
document,
context,
preferences=sco_preferences.SemPreferences(context, sem["formsemestre_id"]),
preferences=sco_preferences.SemPreferences(sem["formsemestre_id"]),
)
)
document.build(objects)
@ -499,7 +491,7 @@ def photos_generate_excel_sample(context, group_ids=[], REQUEST=None):
extra_cols=["fichier_photo"],
REQUEST=REQUEST,
)
return sco_excel.sendExcelFile(REQUEST, data, "ImportPhotos.xls")
return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
def photos_import_files_form(context, group_ids=[], REQUEST=None):
@ -510,9 +502,7 @@ def photos_import_files_form(context, group_ids=[], REQUEST=None):
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
H = [
html_sco_header.sco_header(
context, REQUEST, page_title="Import des photos des étudiants"
),
html_sco_header.sco_header(page_title="Import des photos des étudiants"),
"""<h2 class="formsemestre">Téléchargement des photos des étudiants</h2>
<p><b>Vous pouvez aussi charger les photos individuellement via la fiche de chaque étudiant (menu "Etudiant" / "Changer la photo").</b></p>
<p class="help">Cette page permet de charger en une seule fois les photos de plusieurs étudiants.<br/>
@ -530,7 +520,7 @@ def photos_import_files_form(context, group_ids=[], REQUEST=None):
"""
% groups_infos.groups_query_args,
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
REQUEST.form["group_ids"] = groups_infos.group_ids
tf = TrivialFormulator(
REQUEST.URL0,
@ -545,7 +535,7 @@ def photos_import_files_form(context, group_ids=[], REQUEST=None):
if tf[0] == 0:
return "\n".join(H) + tf[1] + "</li></ol>" + F
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(back_url)
return flask.redirect(back_url)
else:
return photos_import_files(
context,
@ -573,7 +563,7 @@ def photos_import_files(
zip_excel_import_files(
context, xlsfile, zipfile, REQUEST, callback, filename_title, page_title
)
return REQUEST.RESPONSE.redirect(back_url + "&head_message=photos%20 importees")
return flask.redirect(back_url + "&head_message=photos%20 importees")
def zip_excel_import_files(

View File

@ -30,10 +30,7 @@
Modification Jérome Billoue,Vincent Grimaud, IUT de Tours, 2017
"""
try:
from io import StringIO # for Python 3
except ImportError:
from cStringIO import StringIO # for Python 2
import io
from reportlab.lib import colors
from reportlab.lib import pagesizes
@ -68,10 +65,10 @@ def pdf_trombino_tours(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
)
DeptName = sco_preferences.get_preference(context, "DeptName")
DeptFullName = sco_preferences.get_preference(context, "DeptFullName")
UnivName = sco_preferences.get_preference(context, "UnivName")
InstituteName = sco_preferences.get_preference(context, "InstituteName")
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
UnivName = sco_preferences.get_preference("UnivName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = []
@ -268,16 +265,14 @@ def pdf_trombino_tours(
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = StringIO() # in-memory document, no disk file
report = io.BytesIO() # in-memory document, no disk file
filename = "trombino-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
document = BaseDocTemplate(report)
document.addPageTemplates(
ScolarsPageTemplate(
document,
context=context,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
)
document.build(objects)
@ -297,10 +292,10 @@ def pdf_feuille_releve_absences(
):
"""Generation de la feuille d'absence en fichier PDF, avec photos"""
NB_CELL_AM = sco_preferences.get_preference(context, "feuille_releve_abs_AM")
NB_CELL_PM = sco_preferences.get_preference(context, "feuille_releve_abs_PM")
NB_CELL_AM = sco_preferences.get_preference("feuille_releve_abs_AM")
NB_CELL_PM = sco_preferences.get_preference("feuille_releve_abs_PM")
COLWIDTH = 0.85 * cm
if sco_preferences.get_preference(context, "feuille_releve_abs_samedi"):
if sco_preferences.get_preference("feuille_releve_abs_samedi"):
days = sco_abs.DAYNAMES[:6] # Lundi, ..., Samedi
else:
days = sco_abs.DAYNAMES[:5] # Lundi, ..., Vendredi
@ -311,10 +306,10 @@ def pdf_feuille_releve_absences(
context, group_ids, formsemestre_id=formsemestre_id, REQUEST=REQUEST
)
DeptName = sco_preferences.get_preference(context, "DeptName")
DeptFullName = sco_preferences.get_preference(context, "DeptFullName")
UnivName = sco_preferences.get_preference(context, "UnivName")
InstituteName = sco_preferences.get_preference(context, "InstituteName")
DeptName = sco_preferences.get_preference("DeptName")
DeptFullName = sco_preferences.get_preference("DeptFullName")
UnivName = sco_preferences.get_preference("UnivName")
InstituteName = sco_preferences.get_preference("InstituteName")
# Generate PDF page
StyleSheet = styles.getSampleStyleSheet()
objects = [
@ -459,16 +454,13 @@ def pdf_feuille_releve_absences(
# Réduit sur une page
objects = [KeepInFrame(0, 0, objects, mode="shrink")]
# Build document
report = StringIO() # in-memory document, no disk file
report = io.BytesIO() # in-memory document, no disk file
filename = "absences-%s-%s.pdf" % (DeptName, groups_infos.groups_filename)
if sco_preferences.get_preference(context, "feuille_releve_abs_taille") == "A3":
if sco_preferences.get_preference("feuille_releve_abs_taille") == "A3":
taille = A3
elif sco_preferences.get_preference(context, "feuille_releve_abs_taille") == "A4":
elif sco_preferences.get_preference("feuille_releve_abs_taille") == "A4":
taille = A4
if (
sco_preferences.get_preference(context, "feuille_releve_abs_format")
== "Paysage"
):
if sco_preferences.get_preference("feuille_releve_abs_format") == "Paysage":
document = BaseDocTemplate(report, pagesize=landscape(taille))
else:
document = BaseDocTemplate(report, pagesize=taille)
@ -476,9 +468,7 @@ def pdf_feuille_releve_absences(
ScolarsPageTemplate(
document,
context=context,
preferences=sco_preferences.SemPreferences(
context,
),
preferences=sco_preferences.SemPreferences(),
)
)
document.build(objects)

View File

@ -53,6 +53,7 @@ Solution proposée (nov 2014):
- seront aussi présentées (à part) sur la page "Voir les inscriptions aux modules"
"""
import flask
import app.scodoc.notesdb as ndb
import app.scodoc.sco_utils as scu
@ -108,11 +109,10 @@ def external_ue_create(
"ects": ects,
"is_external": 1,
},
REQUEST,
)
matiere_id = sco_edit_matiere.do_matiere_create(
context, {"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}, REQUEST
context, {"ue_id": ue_id, "titre": titre or acronyme, "numero": 1}
)
module_id = sco_edit_module.do_module_create(
@ -126,7 +126,6 @@ def external_ue_create(
"formation_id": formation_id,
"semestre_id": sem["semestre_id"],
},
REQUEST,
)
moduleimpl_id = sco_moduleimpl.do_moduleimpl_create(
@ -152,19 +151,21 @@ def external_ue_inscrit_et_note(
)
# Inscription des étudiants
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
context, moduleimpl_id, formsemestre_id, list(notes_etuds.keys()), REQUEST=REQUEST
context,
moduleimpl_id,
formsemestre_id,
list(notes_etuds.keys()),
REQUEST=REQUEST,
)
# Création d'une évaluation si il n'y en a pas déjà:
ModEvals = sco_evaluations.do_evaluation_list(
context, args={"moduleimpl_id": moduleimpl_id}
)
ModEvals = sco_evaluations.do_evaluation_list(args={"moduleimpl_id": moduleimpl_id})
if len(ModEvals):
# met la note dans le première évaluation existante:
evaluation_id = ModEvals[0]["evaluation_id"]
else:
# crée une évaluation:
evaluation_id = sco_evaluations.do_evaluation_create(context,
evaluation_id = sco_evaluations.do_evaluation_create(
REQUEST=REQUEST,
moduleimpl_id=moduleimpl_id,
note_max=20.0,
@ -194,7 +195,6 @@ def get_existing_external_ue(context, formation_id):
def get_external_moduleimpl_id(context, formsemestre_id, ue_id):
"moduleimpl correspondant à l'UE externe indiquée de ce formsemestre"
r = ndb.SimpleDictFetch(
context,
"""
SELECT moduleimpl_id FROM notes_moduleimpl mi, notes_modules mo
WHERE mi.formsemestre_id = %(formsemestre_id)s
@ -246,7 +246,7 @@ def external_ue_create_form(context, formsemestre_id, etudid, REQUEST=None):
</p>
""",
]
html_footer = html_sco_header.sco_footer(context, REQUEST)
html_footer = html_sco_header.sco_footer()
Fo = sco_formations.formation_list(
context, args={"formation_id": sem["formation_id"]}
)[0]
@ -342,7 +342,7 @@ def external_ue_create_form(context, formsemestre_id, etudid, REQUEST=None):
if tf[0] == 0:
return "\n".join(H) + "\n" + tf[1] + html_footer
elif tf[0] == -1:
return REQUEST.RESPONSE.redirect(bull_url)
return flask.redirect(bull_url)
else:
note = tf[2]["note"].strip().upper()
note_value, invalid = sco_saisie_notes.convert_note_from_string(note, 20.0)
@ -384,4 +384,4 @@ def external_ue_create_form(context, formsemestre_id, etudid, REQUEST=None):
{etudid: note_value},
REQUEST=REQUEST,
)
return REQUEST.RESPONSE.redirect(bull_url + "&head_message=Ajout%20effectué")
return flask.redirect(bull_url + "&head_message=Ajout%20effectué")

View File

@ -104,12 +104,12 @@ def list_operations(context, evaluation_id):
"""returns list of NotesOperation for this evaluation"""
notes = list(
sco_evaluations.do_evaluation_get_all_notes(
context, evaluation_id, filter_suppressed=False
evaluation_id, filter_suppressed=False
).values()
)
notes_log = list(
sco_evaluations.do_evaluation_get_all_notes(
context, evaluation_id, filter_suppressed=False, table="notes_notes_log"
evaluation_id, filter_suppressed=False, table="notes_notes_log"
).values()
)
dt = OPERATION_DATE_TOLERANCE
@ -146,7 +146,7 @@ def list_operations(context, evaluation_id):
def evaluation_list_operations(context, REQUEST, evaluation_id):
"""Page listing operations on evaluation"""
E = sco_evaluations.do_evaluation_list(context, {"evaluation_id": evaluation_id})[0]
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
M = sco_moduleimpl.do_moduleimpl_list(context, moduleimpl_id=E["moduleimpl_id"])[0]
Ops = list_operations(context, evaluation_id)
@ -165,7 +165,7 @@ def evaluation_list_operations(context, REQUEST, evaluation_id):
html_sortable=False,
html_title="<h2>Opérations sur l'évaluation %s du %s</h2>"
% (E["description"], E["jour"]),
preferences=sco_preferences.SemPreferences(context, M["formsemestre_id"]),
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
)
return tab.make_page(context, REQUEST=REQUEST)
@ -176,7 +176,6 @@ def formsemestre_list_saisies_notes(
"""Table listant toutes les operations de saisies de notes, dans toutes les evaluations du semestre."""
sem = sco_formsemestre.get_formsemestre(context, formsemestre_id)
r = ndb.SimpleDictFetch(
context,
"""select i.nom, n.*, mod.titre, e.description, e.jour from notes_notes n, notes_evaluation e, notes_moduleimpl m, notes_modules mod, identite i where m.moduleimpl_id = e.moduleimpl_id and m.module_id = mod.module_id and e.evaluation_id=n.evaluation_id and i.etudid=n.etudid and m.formsemestre_id=%(formsemestre_id)s order by date desc""",
{"formsemestre_id": formsemestre_id},
)
@ -208,7 +207,7 @@ def formsemestre_list_saisies_notes(
html_class="table_leftalign table_coldate",
html_sortable=True,
caption="Saisies de notes dans %s" % sem["titreannee"],
preferences=sco_preferences.SemPreferences(context, formsemestre_id),
preferences=sco_preferences.SemPreferences(formsemestre_id),
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
origin="Généré par %s le " % VERSION.SCONAME + scu.timedate_human_repr() + "",
)

View File

@ -87,21 +87,16 @@ def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="
with_inactives = int(with_inactives)
H = [html_sco_header.html_sem_header(context, REQUEST, "Gestion des utilisateurs")]
H.append("<h2>Gestion des utilisateurs</h2>")
if current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept):
H.append(
'<p><a href="{}" class="stdlink">Ajouter un utilisateur</a>'.format(
url_for("users.create_user_form", scodoc_dept=g.scodoc_dept).encode(
scu.SCO_ENCODING
) # sco8
url_for("users.create_user_form", scodoc_dept=g.scodoc_dept)
)
)
H.append(
'&nbsp;&nbsp; <a href="{}" class="stdlink">Importer des utilisateurs</a></p>'.format(
url_for("users.import_users_form", scodoc_dept=g.scodoc_dept).encode(
scu.SCO_ENCODING
) # sco8
url_for("users.import_users_form", scodoc_dept=g.scodoc_dept)
)
)
if all_depts:
@ -133,7 +128,7 @@ def index_html(context, REQUEST, all_depts=False, with_inactives=False, format="
return L
H.append(L)
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
return "\n".join(H) + F
@ -151,7 +146,7 @@ def list_users(
if dept and not all_depts:
users = get_user_list(dept=dept, with_inactives=with_inactives)
comm = "dept. %s" % dept.encode(scu.SCO_ENCODING) # sco8
comm = "dept. %s" % dept
else:
users = get_user_list(with_inactives=with_inactives)
comm = "tous"
@ -170,9 +165,7 @@ def list_users(
if with_links and can_modify:
target = url_for(
"users.user_info_page", scodoc_dept=dept, user_name=u.user_name
).encode(
scu.SCO_ENCODING
) # sco8
)
d["_user_name_target"] = target
d["_nom_target"] = target
d["_prenom_target"] = target
@ -305,12 +298,10 @@ def user_info_page(context, user_name=None, REQUEST=None):
raise ScoValueError("invalid user_name")
H = [
html_sco_header.sco_header(
context,
REQUEST,
page_title="Utilisateur %s" % user.user_name.encode("utf-8"), # sco8
page_title="Utilisateur %s" % user.user_name,
)
]
F = html_sco_header.sco_footer(context, REQUEST)
F = html_sco_header.sco_footer()
H.append("<h2>Utilisateur: %s" % user.user_name)
info = user.to_dict()
if info:

View File

@ -36,6 +36,7 @@ import json
from hashlib import md5
import numbers
import os
import pydot
import re
import six
import six.moves._thread
@ -51,7 +52,7 @@ STRING_TYPES = six.string_types
from PIL import Image as PILImage
from flask import g, url_for
from flask import g, url_for, request
from scodoc_manager import sco_mgr
@ -300,6 +301,8 @@ CSV_FIELDSEP = ";"
CSV_LINESEP = "\n"
CSV_MIMETYPE = "text/comma-separated-values"
XLS_MIMETYPE = "application/vnd.ms-excel"
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
XLSX_SUFFIX = ".xlsx"
PDF_MIMETYPE = "application/pdf"
XML_MIMETYPE = "text/xml"
JSON_MIMETYPE = "application/json"
@ -321,8 +324,8 @@ def get_dept_id():
raise sco_exceptions.ScoInvalidDept("département invalide: %s" % g.scodoc_dept)
def get_db_cnx_string():
return "dbname=SCO" + g.scodoc_dept
def get_db_cnx_string(scodoc_dept=None):
return "dbname=SCO" + (scodoc_dept or g.scodoc_dept)
def ScoURL():
@ -372,15 +375,6 @@ def UsersURL():
return url_for("users.index_html", scodoc_dept=g.scodoc_dept)[: -len("/index_html")]
def get_current_user_name(REQUEST):
"""return a displayable string identifying the current user.
XXX For now, the login, but will be the name. #sco8
"""
authuser = REQUEST.AUTHENTICATED_USER
uid = str(authuser)
return uid
# ---- Simple python utilities
@ -673,39 +667,27 @@ def sem_decale_str(sem):
return ""
# Graphes (optionnel pour ne pas accroitre les dependances de ScoDoc)
try:
import pydot
WITH_PYDOT = True
except:
WITH_PYDOT = False
if WITH_PYDOT:
# check API (incompatible change after pydot version 0.9.10: scodoc install may use old or new version)
junk_graph = pydot.Dot("junk")
junk_graph.add_node(pydot.Node("a"))
n = junk_graph.get_node("a")
if type(n) == type([]): # "modern" pydot
def pydot_get_node(g, name):
r = g.get_node(name)
if not r:
return r
else:
return r[0]
else: # very old pydot
def pydot_get_node(g, name):
return g.get_node(name)
def is_valid_mail(email):
"""True if well-formed email address"""
return re.match(r"^.+@.+\..{2,3}$", email)
def graph_from_edges(edges, graph_name="mygraph"):
"""Crée un graph pydot
à partir d'une liste d'arêtes [ (n1, n2), (n2, n3), ... ]
n1, n2, ... sont des chaînes donnant l'id des nœuds.
Fonction remplaçant celle de pydot qui est buggée.
"""
nodes = set([it for tup in edges for it in tup])
graph = pydot.Dot(graph_name)
for n in nodes:
graph.add_node(pydot.Node(n))
for e in edges:
graph.add_edge(pydot.Edge(src=e[0], dst=e[1]))
return graph
ICONSIZES = {} # name : (width, height) cache image sizes
@ -782,16 +764,16 @@ def query_portal(req, msg="Portail Apogee", timeout=3):
return data
def AnneeScolaire(REQUEST=None): # TODO remplacer REQUEST #sco8
def AnneeScolaire(sco_year=None):
"annee de debut de l'annee scolaire courante"
if REQUEST and "sco_year" in REQUEST.form:
year = REQUEST.form["sco_year"]
if sco_year:
year = sco_year
try:
year = int(year)
if year > 1900 and year < 2999:
return year
except:
pass
raise sco_exceptions.ScoValueError("invalid sco_year")
t = time.localtime()
year, month = t[0], t[1]
if month < 8: # le "pivot" est le 1er aout
@ -799,7 +781,7 @@ def AnneeScolaire(REQUEST=None): # TODO remplacer REQUEST #sco8
return year
def log_unknown_etud(context, REQUEST=None, format="html"):
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", "?")
@ -808,13 +790,11 @@ def log_unknown_etud(context, REQUEST=None, format="html"):
"unknown student: etudid=%s code_nip=%s code_ine=%s"
% (etudid, code_nip, code_ine)
)
return _sco_error_response(
context, "unknown student", format=format, REQUEST=REQUEST
)
return _sco_error_response("unknown student", format=format, REQUEST=REQUEST)
# XXX #sco8 à tester ou ré-écrire
def _sco_error_response(context, msg, format="html", REQUEST=None):
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":
@ -841,7 +821,6 @@ def return_text_if_published(val, REQUEST):
def confirm_dialog(
context,
message="<p>Confirmer ?</p>",
OK="OK",
Cancel="Annuler",
@ -850,7 +829,6 @@ def confirm_dialog(
target_variable="dialog_confirmed",
parameters={},
add_headers=True, # complete page
REQUEST=None, # required
helpmsg=None,
):
from app.scodoc import html_sco_header
@ -860,7 +838,7 @@ def confirm_dialog(
# Attention: la page a pu etre servie en GET avec des parametres
# si on laisse l'url "action" vide, les parametres restent alors que l'on passe en POST...
if not dest_url:
dest_url = REQUEST.URL
dest_url = request.base_url
# strip remaining parameters from destination url:
dest_url = six.moves.urllib.parse.splitquery(dest_url)[0]
H = [
@ -888,11 +866,9 @@ def confirm_dialog(
H.append("</form>")
if helpmsg:
H.append('<p class="help">' + helpmsg + "</p>")
if add_headers and REQUEST:
if add_headers:
return (
html_sco_header.sco_header(context, REQUEST)
+ "\n".join(H)
+ html_sco_header.sco_footer(context, REQUEST)
html_sco_header.sco_header() + "\n".join(H) + html_sco_header.sco_footer()
)
else:
return "\n".join(H)

View File

@ -31,6 +31,7 @@
from xml.etree import ElementTree
import xml.sax.saxutils
from xml.dom import minidom
from app.scodoc import sco_utils as scu
from app.scodoc.sco_vdi import ApoEtapeVDI
@ -44,7 +45,7 @@ def quote_xml_attr(data):
# ScoDoc7 legacy function:
def simple_dictlist2xml(dictlist, doc=None, tagname=None, quote=False):
def simple_dictlist2xml(dictlist, tagname=None, quote=False, pretty=True):
"""Represent a dict as XML data.
All keys with string or numeric values are attributes (numbers converted to strings).
All list values converted to list of childs (recursively).
@ -64,9 +65,21 @@ def simple_dictlist2xml(dictlist, doc=None, tagname=None, quote=False):
if not tagname:
raise ValueError("invalid empty tagname !")
elements = _dictlist2xml(dictlist, root=[], tagname=tagname, quote=quote)
return XML_HEADER + b"\n".join([ElementTree.tostring(x) for x in elements]).decode(
ans = XML_HEADER + b"\n".join([ElementTree.tostring(x) for x in elements]).decode(
scu.SCO_ENCODING
)
if pretty:
# solution peu satisfaisante car on doit reparser le XML
# de plus, on encode/decode pour avoir le tag <?xml version="1.0" encoding="utf-8"?>
try:
ans = (
minidom.parseString(ans)
.toprettyxml(indent="\t", encoding="utf-8")
.decode("utf-8")
)
except xml.parsers.expat.ExpatError:
pass
return ans
def _dictlist2xml(dictlist, root=None, tagname=None, quote=False):

View File

@ -28,23 +28,21 @@
"""Logging des opérations en base de données
"""
from flask import request
from flask_login import current_user
import app.scodoc.notesdb as ndb
from app.scodoc.notes_log import retreive_request
def logdb(REQUEST=None, cnx=None, method=None, etudid=None, msg=None, commit=True):
def logdb(cnx=None, method=None, etudid=None, msg=None, commit=True):
"Add entry"
if not cnx:
raise ValueError("logdb: cnx is None")
if not REQUEST:
REQUEST = retreive_request(skip=1)
if REQUEST:
args = {
"authenticated_user": str(REQUEST.AUTHENTICATED_USER),
"remote_addr": REQUEST.REMOTE_ADDR,
}
else:
args = {"authenticated_user": None, "remote_addr": None}
args = {
"authenticated_user": current_user.user_name,
"remote_addr": request.remote_addr,
}
args.update({"method": method, "etudid": etudid, "msg": msg})
ndb.quote_dict(args)
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)

View File

@ -0,0 +1,18 @@
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h2>Erreur !</h2>
<p>{{ exc }}</p>
<p>
{% if g.scodoc_dept %}
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
{% else %}
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
{% endif %}
</p>
{% endblock %}

View File

@ -20,12 +20,14 @@
{% endfor %}
</ul>
{% if current_user.is_authenticated %}
<form action="table_etud_in_accessible_depts" method="POST">
<b>Chercher étudiant:</b>
<input type="text" name="expnom" width="12" spellcheck="false" value="">
<input type="submit" value="Chercher">
<br />(entrer une partie du nom ou le code NIP, cherche dans tous les départements autorisés)
</form>
{% endif %}
<div style="margin-top: 1cm; font-size: 120%;">
<p><a href="/ScoDoc/static/mobile">Version mobile (expérimentale, à vos risques et périls)</a></p>

View File

@ -41,14 +41,22 @@ def import_scodoc7_user_db(scodoc7_db="dbname=SCOUSERS"):
)
# Set roles:
# ScoDoc7 roles are stored as 'AdminRT,EnsRT'
for role_dept in u7["roles"].split(","):
m = re.match(r"^([A-Za-z0-9]+?)([A-Z][A-Za-z0-9]*?)$", role_dept)
if u7["roles"]:
roles7 = u7["roles"].split(",")
else:
roles7 = []
for role_dept in roles7:
m = re.match(r"^-?([A-Za-z0-9]+?)([A-Z][A-Za-z0-9]*?)$", role_dept)
if not m:
current_app.logger.warning(
"User {}: ignoring role {}".format(u7["user_name"], role_dept)
)
else:
role_name = m.group(1)
if role_name.startswith("-"):
# disabled users in ScoDoc7
role_name = role_name[1:]
assert not u.active
dept = m.group(2)
role = Role.query.filter_by(name=role_name).first()
if not role:

Some files were not shown because too many files have changed in this diff Show More