forked from ScoDoc/ScoDoc
placement_group_size_control #2
1
.gitignore
vendored
1
.gitignore
vendored
@ -170,3 +170,4 @@ Thumbs.db
|
||||
*.code-workspace
|
||||
|
||||
|
||||
copy
|
||||
|
10
README.md
10
README.md
@ -3,16 +3,12 @@
|
||||
|
||||
(c) Emmanuel Viennet 1999 - 2021 (voir LICENCE.txt)
|
||||
|
||||
VERSION EXPERIMENTALE - NE PAS DEPLOYER - TESTS EN COURS
|
||||
|
||||
Installation: voir instructions à jour sur <https://scodoc.org/GuideInstallDebian11>
|
||||
|
||||
Documentation utilisateur: <https://scodoc.org>
|
||||
|
||||
## Version ScoDoc 9
|
||||
|
||||
N'utiliser que pour les développements et tests.
|
||||
|
||||
La version ScoDoc 9 est basée sur Flask (au lieu de Zope) et sur
|
||||
**python 3.9+**.
|
||||
|
||||
@ -22,13 +18,13 @@ Flask, SQLAlchemy, au lien de Python2/Zope dans les versions précédentes).
|
||||
|
||||
|
||||
|
||||
### État actuel (27 août 21)
|
||||
### État actuel (26 sept 21)
|
||||
|
||||
- Tests en cours, notamment système d'installation et de migration.
|
||||
- 9.0 reproduit l'ensemble des fonctions de ScoDoc 7 (donc pas de BUT), sauf:
|
||||
|
||||
**Fonctionnalités non intégrées:**
|
||||
|
||||
- feuille "placement" (en cours)
|
||||
- génération LaTeX des avis de poursuite d'études
|
||||
|
||||
- ancien module "Entreprises" (obsolete)
|
||||
|
||||
|
@ -17,14 +17,14 @@ from flask import render_template
|
||||
from flask.logging import default_handler
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager
|
||||
from flask_login import LoginManager, current_user
|
||||
from flask_mail import Mail
|
||||
from flask_bootstrap import Bootstrap
|
||||
from flask_moment import Moment
|
||||
from flask_caching import Cache
|
||||
import sqlalchemy
|
||||
|
||||
from app.scodoc.sco_exceptions import ScoValueError, APIInvalidParams
|
||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError, APIInvalidParams
|
||||
from config import DevConfig
|
||||
import sco_version
|
||||
|
||||
@ -82,7 +82,7 @@ def postgresql_server_error(e):
|
||||
return render_raw_html("error_503.html", SCOVERSION=sco_version.SCOVERSION), 503
|
||||
|
||||
|
||||
class RequestFormatter(logging.Formatter):
|
||||
class LogRequestFormatter(logging.Formatter):
|
||||
"""Ajoute URL et remote_addr for logging"""
|
||||
|
||||
def format(self, record):
|
||||
@ -92,6 +92,34 @@ class RequestFormatter(logging.Formatter):
|
||||
else:
|
||||
record.url = None
|
||||
record.remote_addr = None
|
||||
record.sco_user = current_user
|
||||
|
||||
return super().format(record)
|
||||
|
||||
|
||||
class LogExceptionFormatter(logging.Formatter):
|
||||
"""Formatteur pour les exceptions: ajoute détails"""
|
||||
|
||||
def format(self, record):
|
||||
if has_request_context():
|
||||
record.url = request.url
|
||||
record.remote_addr = request.environ.get(
|
||||
"HTTP_X_FORWARDED_FOR", request.remote_addr
|
||||
)
|
||||
record.http_referrer = request.referrer
|
||||
record.http_method = request.method
|
||||
if request.method == "GET":
|
||||
record.http_params = str(request.args)
|
||||
else:
|
||||
# rep = reprlib.Repr() # abbrège
|
||||
record.http_params = str(request.form)[:2048]
|
||||
else:
|
||||
record.url = None
|
||||
record.remote_addr = None
|
||||
record.http_referrer = None
|
||||
record.http_method = None
|
||||
record.http_params = None
|
||||
record.sco_user = current_user
|
||||
|
||||
return super().format(record)
|
||||
|
||||
@ -105,8 +133,24 @@ class ScoSMTPHandler(SMTPHandler):
|
||||
return subject
|
||||
|
||||
|
||||
class ReverseProxied(object):
|
||||
"""Adaptateur wsgi qui nous permet d'avoir toutes les URL calculées en https
|
||||
sauf quand on est en dev.
|
||||
La variable HTTP_X_FORWARDED_PROTO est positionnée par notre config nginx"""
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
scheme = environ.get("HTTP_X_FORWARDED_PROTO")
|
||||
if scheme:
|
||||
environ["wsgi.url_scheme"] = scheme # ou forcer à https ici ?
|
||||
return self.app(environ, start_response)
|
||||
|
||||
|
||||
def create_app(config_class=DevConfig):
|
||||
app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static")
|
||||
app.wsgi_app = ReverseProxied(app.wsgi_app)
|
||||
app.logger.setLevel(logging.DEBUG)
|
||||
app.config.from_object(config_class)
|
||||
|
||||
@ -119,6 +163,7 @@ def create_app(config_class=DevConfig):
|
||||
cache.init_app(app)
|
||||
sco_cache.CACHE = cache
|
||||
|
||||
app.register_error_handler(ScoGenError, handle_sco_value_error)
|
||||
app.register_error_handler(ScoValueError, handle_sco_value_error)
|
||||
app.register_error_handler(500, internal_server_error)
|
||||
app.register_error_handler(503, postgresql_server_error)
|
||||
@ -148,9 +193,16 @@ def create_app(config_class=DevConfig):
|
||||
absences_bp, url_prefix="/ScoDoc/<scodoc_dept>/Scolarite/Absences"
|
||||
)
|
||||
app.register_blueprint(api_bp, url_prefix="/ScoDoc/api")
|
||||
scodoc_exc_formatter = RequestFormatter(
|
||||
"[%(asctime)s] %(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s in %(module)s: %(message)s"
|
||||
scodoc_log_formatter = LogRequestFormatter(
|
||||
"[%(asctime)s] %(sco_user)s@%(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s: %(message)s"
|
||||
)
|
||||
scodoc_exc_formatter = LogExceptionFormatter(
|
||||
"[%(asctime)s] %(sco_user)s@%(remote_addr)s requested %(url)s\n"
|
||||
"%(levelname)s: %(message)s\n"
|
||||
"Referrer: %(http_referrer)s\n"
|
||||
"Method: %(http_method)s\n"
|
||||
"Params: %(http_params)s\n"
|
||||
)
|
||||
if not app.testing:
|
||||
if not app.debug:
|
||||
@ -179,7 +231,7 @@ def create_app(config_class=DevConfig):
|
||||
app.logger.addHandler(mail_handler)
|
||||
else:
|
||||
# Pour logs en DEV uniquement:
|
||||
default_handler.setFormatter(scodoc_exc_formatter)
|
||||
default_handler.setFormatter(scodoc_log_formatter)
|
||||
|
||||
# Config logs pour DEV et PRODUCTION
|
||||
# Configuration des logs (actifs aussi en mode development)
|
||||
@ -188,9 +240,17 @@ def create_app(config_class=DevConfig):
|
||||
file_handler = WatchedFileHandler(
|
||||
app.config["SCODOC_LOG_FILE"], encoding="utf-8"
|
||||
)
|
||||
file_handler.setFormatter(scodoc_exc_formatter)
|
||||
file_handler.setFormatter(scodoc_log_formatter)
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
# Log pour les erreurs (exceptions) uniquement:
|
||||
# usually /opt/scodoc-data/log/scodoc_exc.log
|
||||
file_handler = WatchedFileHandler(
|
||||
app.config["SCODOC_ERR_FILE"], encoding="utf-8"
|
||||
)
|
||||
file_handler.setFormatter(scodoc_exc_formatter)
|
||||
file_handler.setLevel(logging.ERROR)
|
||||
app.logger.addHandler(file_handler)
|
||||
|
||||
# app.logger.setLevel(logging.INFO)
|
||||
app.logger.info(f"{sco_version.SCONAME} {sco_version.SCOVERSION} startup")
|
||||
|
@ -20,7 +20,9 @@
|
||||
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.from flask import jsonify
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from flask import jsonify
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_etud # a deplacer dans scu
|
||||
|
||||
VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\\\.]+$")
|
||||
VALID_LOGIN_EXP = re.compile(r"^[a-zA-Z0-9@\\\-_\.]+$")
|
||||
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
|
@ -20,6 +20,7 @@ import flask_login
|
||||
|
||||
import app
|
||||
from app.auth.models import User
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
|
||||
class ZUser(object):
|
||||
@ -39,69 +40,6 @@ class ZUser(object):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class ZRequest(object):
|
||||
"Emulating Zope 2 REQUEST"
|
||||
|
||||
def __init__(self):
|
||||
if current_app.config["DEBUG"]:
|
||||
self.URL = request.base_url
|
||||
self.BASE0 = request.url_root
|
||||
else:
|
||||
self.URL = request.base_url.replace("http://", "https://")
|
||||
self.BASE0 = request.url_root.replace("http://", "https://")
|
||||
self.URL0 = self.URL
|
||||
# query_string is bytes:
|
||||
self.QUERY_STRING = request.query_string.decode("utf-8")
|
||||
self.REQUEST_METHOD = request.method
|
||||
self.AUTHENTICATED_USER = current_user
|
||||
self.REMOTE_ADDR = request.remote_addr
|
||||
if request.method == "POST":
|
||||
# request.form is a werkzeug.datastructures.ImmutableMultiDict
|
||||
# must copy to get a mutable version (needed by TrivialFormulator)
|
||||
self.form = request.form.copy()
|
||||
if request.files:
|
||||
# Add files in form:
|
||||
self.form.update(request.files)
|
||||
for k in request.form:
|
||||
if k.endswith(":list"):
|
||||
self.form[k[:-5]] = request.form.getlist(k)
|
||||
elif request.method == "GET":
|
||||
self.form = {}
|
||||
for k in request.args:
|
||||
# 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))
|
||||
self.RESPONSE = ZResponse()
|
||||
|
||||
def __str__(self):
|
||||
return """REQUEST
|
||||
URL={r.URL}
|
||||
QUERY_STRING={r.QUERY_STRING}
|
||||
REQUEST_METHOD={r.REQUEST_METHOD}
|
||||
AUTHENTICATED_USER={r.AUTHENTICATED_USER}
|
||||
form={r.form}
|
||||
""".format(
|
||||
r=self
|
||||
)
|
||||
|
||||
|
||||
class ZResponse(object):
|
||||
"Emulating Zope 2 RESPONSE"
|
||||
|
||||
def __init__(self):
|
||||
self.headers = {}
|
||||
|
||||
def redirect(self, url):
|
||||
# current_app.logger.debug("ZResponse redirect to:" + str(url))
|
||||
return flask.redirect(url) # http 302
|
||||
|
||||
def setHeader(self, header, value):
|
||||
self.headers[header.lower()] = value
|
||||
|
||||
|
||||
def scodoc(func):
|
||||
"""Décorateur pour toutes les fonctions ScoDoc
|
||||
Affecte le département à g
|
||||
@ -132,7 +70,6 @@ def permission_required(permission):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
# current_app.logger.info("PERMISSION; kwargs=%s" % str(kwargs))
|
||||
scodoc_dept = getattr(g, "scodoc_dept", None)
|
||||
if not current_user.has_permission(permission, scodoc_dept):
|
||||
abort(403)
|
||||
@ -193,7 +130,6 @@ def admin_required(f):
|
||||
|
||||
def scodoc7func(func):
|
||||
"""Décorateur pour intégrer les fonctions Zope 2 de ScoDoc 7.
|
||||
Ajoute l'argument REQUEST s'il est dans la signature de la fonction.
|
||||
Les paramètres de la query string deviennent des (keywords) paramètres de la fonction.
|
||||
"""
|
||||
|
||||
@ -206,19 +142,20 @@ def scodoc7func(func):
|
||||
1. via a Flask route ("top level call")
|
||||
2. or be called directly from Python.
|
||||
|
||||
If called via a route, this decorator setups a REQUEST object (emulating Zope2 REQUEST)
|
||||
"""
|
||||
# Détermine si on est appelé via une route ("toplevel")
|
||||
# ou par un appel de fonction python normal.
|
||||
top_level = not hasattr(g, "zrequest")
|
||||
top_level = not hasattr(g, "scodoc7_decorated")
|
||||
if not top_level:
|
||||
# ne "redécore" pas
|
||||
return func(*args, **kwargs)
|
||||
g.scodoc7_decorated = True
|
||||
# --- Emulate Zope's REQUEST
|
||||
REQUEST = ZRequest()
|
||||
g.zrequest = REQUEST
|
||||
req_args = REQUEST.form # args from query string (get) or form (post)
|
||||
# --- Add positional arguments
|
||||
# REQUEST = ZRequest()
|
||||
# g.zrequest = REQUEST
|
||||
# args from query string (get) or form (post)
|
||||
req_args = scu.get_request_args()
|
||||
## --- Add positional arguments
|
||||
pos_arg_values = []
|
||||
argspec = inspect.getfullargspec(func)
|
||||
# current_app.logger.info("argspec=%s" % str(argspec))
|
||||
@ -227,10 +164,12 @@ def scodoc7func(func):
|
||||
arg_names = argspec.args[:-nb_default_args]
|
||||
else:
|
||||
arg_names = argspec.args
|
||||
for arg_name in arg_names:
|
||||
if arg_name == "REQUEST": # special case
|
||||
pos_arg_values.append(REQUEST)
|
||||
for arg_name in arg_names: # pour chaque arg de la fonction vue
|
||||
if arg_name == "REQUEST": # ne devrait plus arriver !
|
||||
# debug check, TODO remove after tests
|
||||
raise ValueError("invalid REQUEST parameter !")
|
||||
else:
|
||||
# peut produire une KeyError s'il manque un argument attendu:
|
||||
v = req_args[arg_name]
|
||||
# try to convert all arguments to INTEGERS
|
||||
# necessary for db ids and boolean values
|
||||
@ -244,9 +183,9 @@ def scodoc7func(func):
|
||||
# Add keyword arguments
|
||||
if nb_default_args:
|
||||
for arg_name in argspec.args[-nb_default_args:]:
|
||||
if arg_name == "REQUEST": # special case
|
||||
kwargs[arg_name] = REQUEST
|
||||
elif arg_name in req_args:
|
||||
# if arg_name == "REQUEST": # special case
|
||||
# kwargs[arg_name] = REQUEST
|
||||
if arg_name in req_args:
|
||||
# set argument kw optionnel
|
||||
v = req_args[arg_name]
|
||||
# try to convert all arguments to INTEGERS
|
||||
@ -270,13 +209,13 @@ def scodoc7func(func):
|
||||
# Build response, adding collected http headers:
|
||||
headers = []
|
||||
kw = {"response": value, "status": 200}
|
||||
if g.zrequest:
|
||||
headers = g.zrequest.RESPONSE.headers
|
||||
if not headers:
|
||||
# no customized header, speedup:
|
||||
return value
|
||||
if "content-type" in headers:
|
||||
kw["mimetype"] = headers["content-type"]
|
||||
# if g.zrequest:
|
||||
# headers = g.zrequest.RESPONSE.headers
|
||||
# if not headers:
|
||||
# # no customized header, speedup:
|
||||
# return value
|
||||
# if "content-type" in headers:
|
||||
# kw["mimetype"] = headers["content-type"]
|
||||
r = flask.Response(**kw)
|
||||
for h in headers:
|
||||
r.headers[h] = headers[h]
|
||||
|
@ -73,3 +73,17 @@ class BilletAbsence(db.Model):
|
||||
entry_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now())
|
||||
# true si l'absence _pourrait_ etre justifiée
|
||||
justified = db.Column(db.Boolean(), default=False, server_default="false")
|
||||
|
||||
def to_dict(self):
|
||||
data = {
|
||||
"id": self.id,
|
||||
"billet_id": self.id,
|
||||
"etudid": self.etudid,
|
||||
"abs_begin": self.abs_begin,
|
||||
"abs_end": self.abs_begin,
|
||||
"description": self.description,
|
||||
"etat": self.etat,
|
||||
"entry_date": self.entry_date,
|
||||
"justified": self.justified,
|
||||
}
|
||||
return data
|
||||
|
@ -33,7 +33,7 @@ class Departement(db.Model):
|
||||
semsets = db.relationship("NotesSemSet", lazy="dynamic", backref="departement")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Departement {self.acronym}>"
|
||||
return f"<{self.__class__.__name__}(id={self.id}, acronym='{self.acronym}')>"
|
||||
|
||||
def to_dict(self):
|
||||
data = {
|
||||
|
@ -41,7 +41,10 @@ class Identite(db.Model):
|
||||
code_nip = db.Column(db.Text())
|
||||
code_ine = db.Column(db.Text())
|
||||
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||
# ne pas utiliser après migrate_scodoc7_dept_archive
|
||||
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||
#
|
||||
billets = db.relationship("BilletAbsence", backref="etudiant", lazy="dynamic")
|
||||
|
||||
|
||||
class Adresse(db.Model):
|
||||
|
@ -11,7 +11,7 @@ class NotesFormation(db.Model):
|
||||
"""Programme pédagogique d'une formation"""
|
||||
|
||||
__tablename__ = "notes_formations"
|
||||
__table_args__ = (db.UniqueConstraint("acronyme", "titre", "version"),)
|
||||
__table_args__ = (db.UniqueConstraint("dept_id", "acronyme", "titre", "version"),)
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
formation_id = db.synonym("id")
|
||||
@ -30,8 +30,12 @@ class NotesFormation(db.Model):
|
||||
type_parcours = db.Column(db.Integer, default=0, server_default="0")
|
||||
code_specialite = db.Column(db.String(SHORT_STR_LEN))
|
||||
|
||||
ues = db.relationship("NotesUE", backref="formation", lazy="dynamic")
|
||||
formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}(id={self.id}, dept_id={self.dept_id}, acronyme='{self.acronyme}')>"
|
||||
|
||||
|
||||
class NotesUE(db.Model):
|
||||
"""Unité d'Enseignement"""
|
||||
@ -61,6 +65,9 @@ class NotesUE(db.Model):
|
||||
# coef UE, utilise seulement si l'option use_ue_coefs est activée:
|
||||
coefficient = db.Column(db.Float)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__name__}(id={self.id}, formation_id={self.formation_id}, acronyme='{self.acronyme}')>"
|
||||
|
||||
|
||||
class NotesMatiere(db.Model):
|
||||
"""Matières: regroupe les modules d'une UE
|
||||
|
@ -41,6 +41,10 @@ class FormSemestre(db.Model):
|
||||
bul_hide_xml = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# Bloque le calcul des moyennes (générale et d'UE)
|
||||
block_moyennes = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
)
|
||||
# semestres decales (pour gestion jurys):
|
||||
gestion_semestrielle = db.Column(
|
||||
db.Boolean(), nullable=False, default=False, server_default="false"
|
||||
@ -70,6 +74,7 @@ class FormSemestre(db.Model):
|
||||
"NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre"
|
||||
)
|
||||
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||
# ne pas utiliser après migrate_scodoc7_dept_archive
|
||||
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
8
app/pe/README.md
Normal file
8
app/pe/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# Module "Avis de poursuite d'étude"
|
||||
|
||||
Conçu et développé sur ScoDoc 7 par Cléo Baras (IUT de Grenoble) pour le DUT.
|
||||
|
||||
Actuellement non opérationnel dans ScoDoc 9.
|
||||
|
||||
|
||||
|
@ -33,9 +33,9 @@
|
||||
import os
|
||||
import codecs
|
||||
import re
|
||||
from app.scodoc import pe_jurype
|
||||
from app.scodoc import pe_tagtable
|
||||
from app.scodoc import pe_tools
|
||||
from app.pe import pe_tagtable
|
||||
from app.pe import pe_jurype
|
||||
from app.pe import pe_tools
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -48,7 +48,7 @@ from app.scodoc import sco_etud
|
||||
DEBUG = False # Pour debug et repérage des prints à changer en Log
|
||||
|
||||
DONNEE_MANQUANTE = (
|
||||
u"" # Caractère de remplacement des données manquantes dans un avis PE
|
||||
"" # Caractère de remplacement des données manquantes dans un avis PE
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
@ -102,17 +102,17 @@ def comp_latex_parcourstimeline(etudiant, promo, taille=17):
|
||||
result: chaine unicode (EV:)
|
||||
"""
|
||||
codelatexDebut = (
|
||||
u"""
|
||||
""""
|
||||
\\begin{parcourstimeline}{**debut**}{**fin**}{**nbreSemestres**}{%d}
|
||||
"""
|
||||
% taille
|
||||
)
|
||||
|
||||
modeleEvent = u"""
|
||||
modeleEvent = """
|
||||
\\parcoursevent{**nosem**}{**nomsem**}{**descr**}
|
||||
"""
|
||||
|
||||
codelatexFin = u"""
|
||||
codelatexFin = """
|
||||
\\end{parcourstimeline}
|
||||
"""
|
||||
reslatex = codelatexDebut
|
||||
@ -125,13 +125,13 @@ def comp_latex_parcourstimeline(etudiant, promo, taille=17):
|
||||
for no_sem in range(etudiant["nbSemestres"]):
|
||||
descr = modeleEvent
|
||||
nom_semestre_dans_parcours = parcours[no_sem]["nom_semestre_dans_parcours"]
|
||||
descr = descr.replace(u"**nosem**", str(no_sem + 1))
|
||||
descr = descr.replace("**nosem**", str(no_sem + 1))
|
||||
if no_sem % 2 == 0:
|
||||
descr = descr.replace(u"**nomsem**", nom_semestre_dans_parcours)
|
||||
descr = descr.replace(u"**descr**", u"")
|
||||
descr = descr.replace("**nomsem**", nom_semestre_dans_parcours)
|
||||
descr = descr.replace("**descr**", "")
|
||||
else:
|
||||
descr = descr.replace(u"**nomsem**", u"")
|
||||
descr = descr.replace(u"**descr**", nom_semestre_dans_parcours)
|
||||
descr = descr.replace("**nomsem**", "")
|
||||
descr = descr.replace("**descr**", nom_semestre_dans_parcours)
|
||||
reslatex += descr
|
||||
reslatex += codelatexFin
|
||||
return reslatex
|
||||
@ -166,7 +166,7 @@ def get_code_latex_avis_etudiant(
|
||||
result: chaine unicode
|
||||
"""
|
||||
if not donnees_etudiant or not un_avis_latex: # Cas d'un template vide
|
||||
return annotationPE if annotationPE else u""
|
||||
return annotationPE if annotationPE else ""
|
||||
|
||||
# Le template latex (corps + footer)
|
||||
code = un_avis_latex + "\n\n" + footer_latex
|
||||
@ -189,17 +189,17 @@ def get_code_latex_avis_etudiant(
|
||||
)
|
||||
|
||||
# La macro parcourstimeline
|
||||
elif tag_latex == u"parcourstimeline":
|
||||
elif tag_latex == "parcourstimeline":
|
||||
valeur = comp_latex_parcourstimeline(
|
||||
donnees_etudiant, donnees_etudiant["promo"]
|
||||
)
|
||||
|
||||
# Le tag annotationPE
|
||||
elif tag_latex == u"annotation":
|
||||
elif tag_latex == "annotation":
|
||||
valeur = annotationPE
|
||||
|
||||
# Le tag bilanParTag
|
||||
elif tag_latex == u"bilanParTag":
|
||||
elif tag_latex == "bilanParTag":
|
||||
valeur = get_bilanParTag(donnees_etudiant)
|
||||
|
||||
# Les tags "simples": par ex. nom, prenom, civilite, ...
|
||||
@ -249,14 +249,14 @@ def get_annotation_PE(etudid, tag_annotation_pe):
|
||||
]["comment_u"]
|
||||
|
||||
annotationPE = exp.sub(
|
||||
u"", annotationPE
|
||||
"", annotationPE
|
||||
) # Suppression du tag d'annotation PE
|
||||
annotationPE = annotationPE.replace(u"\r", u"") # Suppression des \r
|
||||
annotationPE = annotationPE.replace("\r", "") # Suppression des \r
|
||||
annotationPE = annotationPE.replace(
|
||||
u"<br/>", u"\n\n"
|
||||
"<br/>", "\n\n"
|
||||
) # Interprète les retours chariots html
|
||||
return annotationPE
|
||||
return u"" # pas d'annotations
|
||||
return "" # pas d'annotations
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
@ -282,7 +282,7 @@ def str_from_syntheseJury(donnees_etudiant, aggregat, groupe, tag_scodoc, champ)
|
||||
):
|
||||
donnees_numeriques = donnees_etudiant[aggregat][groupe][tag_scodoc]
|
||||
if champ == "rang":
|
||||
valeur = u"%s/%d" % (
|
||||
valeur = "%s/%d" % (
|
||||
donnees_numeriques[
|
||||
pe_tagtable.TableTag.FORMAT_DONNEES_ETUDIANTS.index("rang")
|
||||
],
|
||||
@ -303,9 +303,9 @@ def str_from_syntheseJury(donnees_etudiant, aggregat, groupe, tag_scodoc, champ)
|
||||
if isinstance(
|
||||
donnees_numeriques[indice_champ], float
|
||||
): # valeur numérique avec formattage unicode
|
||||
valeur = u"%2.2f" % donnees_numeriques[indice_champ]
|
||||
valeur = "%2.2f" % donnees_numeriques[indice_champ]
|
||||
else:
|
||||
valeur = u"%s" % donnees_numeriques[indice_champ]
|
||||
valeur = "%s" % donnees_numeriques[indice_champ]
|
||||
|
||||
return valeur
|
||||
|
||||
@ -356,29 +356,27 @@ def get_bilanParTag(donnees_etudiant, groupe="groupe"):
|
||||
("\\textit{" + rang + "}") if note else ""
|
||||
) # rang masqué si pas de notes
|
||||
|
||||
code_latex = u"\\begin{tabular}{|c|" + "|c" * (len(entete)) + "|}\n"
|
||||
code_latex += u"\\hline \n"
|
||||
code_latex = "\\begin{tabular}{|c|" + "|c" * (len(entete)) + "|}\n"
|
||||
code_latex += "\\hline \n"
|
||||
code_latex += (
|
||||
u" & "
|
||||
" & "
|
||||
+ " & ".join(["\\textbf{" + intitule + "}" for (agg, intitule, _) in entete])
|
||||
+ " \\\\ \n"
|
||||
)
|
||||
code_latex += u"\\hline"
|
||||
code_latex += u"\\hline \n"
|
||||
code_latex += "\\hline"
|
||||
code_latex += "\\hline \n"
|
||||
for (i, ligne_val) in enumerate(valeurs["note"]):
|
||||
titre = lignes[i] # règle le pb d'encodage
|
||||
code_latex += "\\textbf{" + titre + "} & " + " & ".join(ligne_val) + "\\\\ \n"
|
||||
code_latex += (
|
||||
u"\\textbf{" + titre + u"} & " + " & ".join(ligne_val) + u"\\\\ \n"
|
||||
)
|
||||
code_latex += (
|
||||
u" & "
|
||||
+ u" & ".join(
|
||||
[u"{\\scriptsize " + clsmt + u"}" for clsmt in valeurs["rang"][i]]
|
||||
" & "
|
||||
+ " & ".join(
|
||||
["{\\scriptsize " + clsmt + "}" for clsmt in valeurs["rang"][i]]
|
||||
)
|
||||
+ u"\\\\ \n"
|
||||
+ "\\\\ \n"
|
||||
)
|
||||
code_latex += u"\\hline \n"
|
||||
code_latex += u"\\end{tabular}"
|
||||
code_latex += "\\hline \n"
|
||||
code_latex += "\\end{tabular}"
|
||||
|
||||
return code_latex
|
||||
|
||||
@ -397,21 +395,15 @@ def get_avis_poursuite_par_etudiant(
|
||||
nom = jury.syntheseJury[etudid]["nom"].replace(" ", "-")
|
||||
prenom = jury.syntheseJury[etudid]["prenom"].replace(" ", "-")
|
||||
|
||||
nom_fichier = (
|
||||
u"avis_poursuite_"
|
||||
+ pe_tools.remove_accents(nom)
|
||||
+ "_"
|
||||
+ pe_tools.remove_accents(prenom)
|
||||
+ "_"
|
||||
+ str(etudid)
|
||||
nom_fichier = scu.sanitize_filename(
|
||||
"avis_poursuite_%s_%s_%s" % (nom, prenom, etudid)
|
||||
)
|
||||
if pe_tools.PE_DEBUG:
|
||||
pe_tools.pe_print("fichier latex =" + nom_fichier, type(nom_fichier))
|
||||
|
||||
# Entete (commentaire)
|
||||
|
||||
contenu_latex = (
|
||||
u"%% ---- Etudiant: " + civilite_str + " " + nom + " " + prenom + u"\n"
|
||||
"%% ---- Etudiant: " + civilite_str + " " + nom + " " + prenom + "\n"
|
||||
)
|
||||
|
||||
# les annnotations
|
@ -52,10 +52,10 @@ from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_codes_parcours # sco_codes_parcours.NEXT -> sem suivant
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import pe_tagtable
|
||||
from app.scodoc import pe_tools
|
||||
from app.scodoc import pe_semestretag
|
||||
from app.scodoc import pe_settag
|
||||
from app.pe import pe_tagtable
|
||||
from app.pe import pe_tools
|
||||
from app.pe import pe_semestretag
|
||||
from app.pe import pe_settag
|
||||
|
||||
# ----------------------------------------------------------------------------------------
|
||||
def comp_nom_semestre_dans_parcours(sem):
|
||||
@ -946,7 +946,7 @@ class JuryPE(object):
|
||||
return list(taglist)
|
||||
|
||||
def get_allTagInSyntheseJury(self):
|
||||
"""Extrait tous les tags du dictionnaire syntheseJury trié par ordre alphabétique. [] si aucun tag """
|
||||
"""Extrait tous les tags du dictionnaire syntheseJury trié par ordre alphabétique. [] si aucun tag"""
|
||||
allTags = set()
|
||||
for nom in JuryPE.PARCOURS.keys():
|
||||
allTags = allTags.union(set(self.get_allTagForAggregat(nom)))
|
@ -40,7 +40,7 @@ from app import log
|
||||
from app.scodoc import sco_codes_parcours
|
||||
from app.scodoc import sco_cache
|
||||
from app.scodoc import sco_tag_module
|
||||
from app.scodoc import pe_tagtable
|
||||
from app.pe import pe_tagtable
|
||||
|
||||
|
||||
class SemestreTag(pe_tagtable.TableTag):
|
@ -36,8 +36,8 @@ Created on Fri Sep 9 09:15:05 2016
|
||||
@author: barasc
|
||||
"""
|
||||
|
||||
from app.scodoc.pe_tools import pe_print, PE_DEBUG
|
||||
from app.scodoc import pe_tagtable
|
||||
from app.pe.pe_tools import pe_print, PE_DEBUG
|
||||
from app.pe import pe_tagtable
|
||||
|
||||
|
||||
class SetTag(pe_tagtable.TableTag):
|
@ -167,8 +167,19 @@ def list_directory_filenames(path):
|
||||
def add_local_file_to_zip(zipfile, ziproot, pathname, path_in_zip):
|
||||
"""Read pathname server file and add content to zip under path_in_zip"""
|
||||
rooted_path_in_zip = os.path.join(ziproot, path_in_zip)
|
||||
data = open(pathname).read()
|
||||
zipfile.writestr(rooted_path_in_zip, data)
|
||||
zipfile.write(filename=pathname, arcname=rooted_path_in_zip)
|
||||
# data = open(pathname).read()
|
||||
# zipfile.writestr(rooted_path_in_zip, data)
|
||||
|
||||
|
||||
def add_refs_to_register(register, directory):
|
||||
"""Ajoute les fichiers trouvés dans directory au registre (dictionaire) sous la forme
|
||||
filename => pathname
|
||||
"""
|
||||
length = len(directory)
|
||||
for pathname in list_directory_filenames(directory):
|
||||
filename = pathname[length + 1 :]
|
||||
register[filename] = pathname
|
||||
|
||||
|
||||
def add_pe_stuff_to_zip(zipfile, ziproot):
|
||||
@ -179,30 +190,16 @@ def add_pe_stuff_to_zip(zipfile, ziproot):
|
||||
|
||||
Also copy logos
|
||||
"""
|
||||
register = {}
|
||||
# first add standard (distrib references)
|
||||
distrib_dir = os.path.join(REP_DEFAULT_AVIS, "distrib")
|
||||
distrib_pathnames = list_directory_filenames(
|
||||
distrib_dir
|
||||
) # eg /opt/scodoc/tools/doc_poursuites_etudes/distrib/modeles/toto.tex
|
||||
l = len(distrib_dir)
|
||||
distrib_filenames = {x[l + 1 :] for x in distrib_pathnames} # eg modeles/toto.tex
|
||||
|
||||
add_refs_to_register(register=register, directory=distrib_dir)
|
||||
# then add local references (some oh them may overwrite distrib refs)
|
||||
local_dir = os.path.join(REP_LOCAL_AVIS, "local")
|
||||
local_pathnames = list_directory_filenames(local_dir)
|
||||
l = len(local_dir)
|
||||
local_filenames = {x[l + 1 :] for x in local_pathnames}
|
||||
|
||||
for filename in distrib_filenames | local_filenames:
|
||||
if filename in local_filenames:
|
||||
add_local_file_to_zip(
|
||||
zipfile, ziproot, os.path.join(local_dir, filename), "avis/" + filename
|
||||
)
|
||||
else:
|
||||
add_local_file_to_zip(
|
||||
zipfile,
|
||||
ziproot,
|
||||
os.path.join(distrib_dir, filename),
|
||||
"avis/" + filename,
|
||||
)
|
||||
add_refs_to_register(register=register, directory=local_dir)
|
||||
# at this point register contains all refs (filename, pathname) to be saved
|
||||
for filename, pathname in register.items():
|
||||
add_local_file_to_zip(zipfile, ziproot, pathname, "avis/" + filename)
|
||||
|
||||
# Logos: (add to logos/ directory in zip)
|
||||
logos_names = ["logo_header.jpg", "logo_footer.jpg"]
|
@ -42,10 +42,9 @@ from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_preferences
|
||||
|
||||
from app.scodoc import pe_tools
|
||||
from app.scodoc.pe_tools import PE_LATEX_ENCODING
|
||||
from app.scodoc import pe_jurype
|
||||
from app.scodoc import pe_avislatex
|
||||
from app.pe import pe_tools
|
||||
from app.pe import pe_jurype
|
||||
from app.pe import pe_avislatex
|
||||
|
||||
|
||||
def _pe_view_sem_recap_form(formsemestre_id):
|
||||
@ -90,7 +89,6 @@ def pe_view_sem_recap(
|
||||
semBase = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
jury = pe_jurype.JuryPE(semBase)
|
||||
|
||||
# Ajout avis LaTeX au même zip:
|
||||
etudids = list(jury.syntheseJury.keys())
|
||||
|
||||
@ -150,18 +148,14 @@ def pe_view_sem_recap(
|
||||
footer_latex,
|
||||
prefs,
|
||||
)
|
||||
|
||||
jury.add_file_to_zip(
|
||||
("avis/" + nom_fichier + ".tex").encode(PE_LATEX_ENCODING),
|
||||
contenu_latex.encode(PE_LATEX_ENCODING),
|
||||
)
|
||||
jury.add_file_to_zip("avis/" + nom_fichier + ".tex", contenu_latex)
|
||||
latex_pages[nom_fichier] = contenu_latex # Sauvegarde dans un dico
|
||||
|
||||
# Nouvelle version : 1 fichier par étudiant avec 1 fichier appelant créée ci-dessous
|
||||
doc_latex = "\n% -----\n".join(
|
||||
["\\include{" + nom + "}" for nom in sorted(latex_pages.keys())]
|
||||
)
|
||||
jury.add_file_to_zip("avis/avis_poursuite.tex", doc_latex.encode(PE_LATEX_ENCODING))
|
||||
jury.add_file_to_zip("avis/avis_poursuite.tex", doc_latex)
|
||||
|
||||
# Ajoute image, LaTeX class file(s) and modeles
|
||||
pe_tools.add_pe_stuff_to_zip(jury.zipfile, jury.NOM_EXPORT_ZIP)
|
@ -8,6 +8,7 @@
|
||||
|
||||
v 1.3 (python3)
|
||||
"""
|
||||
import html
|
||||
|
||||
|
||||
def TrivialFormulator(
|
||||
@ -134,7 +135,7 @@ class TF(object):
|
||||
is_submitted=False,
|
||||
):
|
||||
self.form_url = form_url
|
||||
self.values = values
|
||||
self.values = values.copy()
|
||||
self.formdescription = list(formdescription)
|
||||
self.initvalues = initvalues
|
||||
self.method = method
|
||||
@ -722,7 +723,9 @@ var {field}_as = new bsn.AutoSuggest('{field}', {field}_opts);
|
||||
if str(descr["allowed_values"][i]) == str(self.values[field]):
|
||||
R.append('<span class="tf-ro-value">%s</span>' % labels[i])
|
||||
elif input_type == "textarea":
|
||||
R.append('<div class="tf-ro-textarea">%s</div>' % self.values[field])
|
||||
R.append(
|
||||
'<div class="tf-ro-textarea">%s</div>' % html.escape(self.values[field])
|
||||
)
|
||||
elif input_type == "separator" or input_type == "hidden":
|
||||
pass
|
||||
elif input_type == "file":
|
||||
|
@ -379,6 +379,25 @@ def bonus_iutbethune(notes_sport, coefs, infos=None):
|
||||
return bonus
|
||||
|
||||
|
||||
def bonus_iutbeziers(notes_sport, coefs, infos=None):
|
||||
"""Calcul bonus modules optionels (sport, culture), regle IUT BEZIERS
|
||||
|
||||
Les étudiants de l'IUT peuvent suivre des enseignements optionnels
|
||||
sport , etc) non rattaches à une unité d'enseignement. Les points
|
||||
au-dessus de 10 sur 20 obtenus dans chacune des matières
|
||||
optionnelles sont cumulés et 3% de ces points cumulés s'ajoutent à
|
||||
la moyenne générale du semestre déjà obtenue par l'étudiant.
|
||||
"""
|
||||
sumc = sum(coefs) # assumes sum. coefs > 0
|
||||
# note_sport = sum(map(mul, notes_sport, coefs)) / sumc # moyenne pondérée
|
||||
bonus = sum([(x - 10) * 0.03 for x in notes_sport if x > 10])
|
||||
# le total du bonus ne doit pas dépasser 0.3 - Fred, 28/01/2020
|
||||
|
||||
if bonus > 0.3:
|
||||
bonus = 0.3
|
||||
return bonus
|
||||
|
||||
|
||||
def bonus_demo(notes_sport, coefs, infos=None):
|
||||
"""Fausse fonction "bonus" pour afficher les informations disponibles
|
||||
et aider les développeurs.
|
||||
@ -386,8 +405,8 @@ def bonus_demo(notes_sport, coefs, infos=None):
|
||||
qui est ECRASE à chaque appel.
|
||||
*** Ne pas utiliser en production !!! ***
|
||||
"""
|
||||
f = open("/tmp/scodoc_bonus.log", "w") # mettre 'a' pour ajouter en fin
|
||||
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
|
||||
with open("/tmp/scodoc_bonus.log", "w") as f: # mettre 'a' pour ajouter en fin
|
||||
f.write("\n---------------\n" + pprint.pformat(infos) + "\n")
|
||||
# Statut de chaque UE
|
||||
# for ue_id in infos['moy_ues']:
|
||||
# ue_status = infos['moy_ues'][ue_id]
|
||||
|
@ -185,6 +185,9 @@ class GenTable(object):
|
||||
else:
|
||||
self.preferences = DEFAULT_TABLE_PREFERENCES()
|
||||
|
||||
def __repr__(self):
|
||||
return f"<gen_table( nrows={self.get_nb_rows()}, ncols={self.get_nb_cols()} )>"
|
||||
|
||||
def get_nb_cols(self):
|
||||
return len(self.columns_ids)
|
||||
|
||||
@ -468,7 +471,10 @@ class GenTable(object):
|
||||
|
||||
def excel(self, wb=None):
|
||||
"""Simple Excel representation of the table"""
|
||||
ses = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
|
||||
if wb is None:
|
||||
ses = sco_excel.ScoExcelSheet(sheet_name=self.xls_sheet_name, wb=wb)
|
||||
else:
|
||||
ses = wb.create_sheet(sheet_name=self.xls_sheet_name)
|
||||
ses.rows += self.xls_before_table
|
||||
style_bold = sco_excel.excel_make_style(bold=True)
|
||||
style_base = sco_excel.excel_make_style()
|
||||
@ -482,9 +488,7 @@ class GenTable(object):
|
||||
ses.append_blank_row() # empty line
|
||||
ses.append_single_cell_row(self.origin, style_base)
|
||||
if wb is None:
|
||||
return ses.generate_standalone()
|
||||
else:
|
||||
ses.generate_embeded()
|
||||
return ses.generate()
|
||||
|
||||
def text(self):
|
||||
"raw text representation of the table"
|
||||
@ -573,7 +577,7 @@ class GenTable(object):
|
||||
"""
|
||||
doc = ElementTree.Element(
|
||||
self.xml_outer_tag,
|
||||
id=self.table_id,
|
||||
id=str(self.table_id),
|
||||
origin=self.origin or "",
|
||||
caption=self.caption or "",
|
||||
)
|
||||
@ -587,7 +591,7 @@ class GenTable(object):
|
||||
v = row.get(cid, "")
|
||||
if v is None:
|
||||
v = ""
|
||||
x_cell = ElementTree.Element(cid, value=str(v))
|
||||
x_cell = ElementTree.Element(str(cid), value=str(v))
|
||||
x_row.append(x_cell)
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
|
||||
@ -610,7 +614,6 @@ class GenTable(object):
|
||||
format="html",
|
||||
page_title="",
|
||||
filename=None,
|
||||
REQUEST=None,
|
||||
javascripts=[],
|
||||
with_html_headers=True,
|
||||
publish=True,
|
||||
@ -643,35 +646,53 @@ class GenTable(object):
|
||||
H.append(html_sco_header.sco_footer())
|
||||
return "\n".join(H)
|
||||
elif format == "pdf":
|
||||
objects = self.pdf()
|
||||
doc = sco_pdf.pdf_basic_page(
|
||||
objects, title=title, preferences=self.preferences
|
||||
pdf_objs = self.pdf()
|
||||
pdf_doc = sco_pdf.pdf_basic_page(
|
||||
pdf_objs, title=title, preferences=self.preferences
|
||||
)
|
||||
if publish:
|
||||
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
|
||||
return scu.send_file(
|
||||
pdf_doc,
|
||||
filename,
|
||||
suffix=".pdf",
|
||||
mime=scu.PDF_MIMETYPE,
|
||||
)
|
||||
else:
|
||||
return doc
|
||||
elif format == "xls" or format == "xlsx":
|
||||
return pdf_doc
|
||||
elif format == "xls" or format == "xlsx": # dans les 2 cas retourne du xlsx
|
||||
xls = self.excel()
|
||||
if publish:
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, xls, filename + scu.XLSX_SUFFIX
|
||||
return scu.send_file(
|
||||
xls,
|
||||
filename,
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
else:
|
||||
return xls
|
||||
elif format == "text":
|
||||
return self.text()
|
||||
elif format == "csv":
|
||||
return scu.sendCSVFile(REQUEST, self.text(), filename + ".csv")
|
||||
return scu.send_file(
|
||||
self.text(),
|
||||
filename,
|
||||
suffix=".csv",
|
||||
mime=scu.CSV_MIMETYPE,
|
||||
attached=True,
|
||||
)
|
||||
elif format == "xml":
|
||||
xml = self.xml()
|
||||
if REQUEST and publish:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
if publish:
|
||||
return scu.send_file(
|
||||
xml, filename, suffix=".xml", mime=scu.XML_MIMETYPE
|
||||
)
|
||||
return xml
|
||||
elif format == "json":
|
||||
js = self.json()
|
||||
if REQUEST and publish:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
|
||||
if publish:
|
||||
return scu.send_file(
|
||||
js, filename, suffix=".json", mime=scu.JSON_MIMETYPE
|
||||
)
|
||||
return js
|
||||
else:
|
||||
log("make_page: format=%s" % format)
|
||||
@ -732,5 +753,5 @@ if __name__ == "__main__":
|
||||
document.build(objects)
|
||||
data = doc.getvalue()
|
||||
open("/tmp/gen_table.pdf", "wb").write(data)
|
||||
p = T.make_page(format="pdf", REQUEST=None)
|
||||
p = T.make_page(format="pdf")
|
||||
open("toto.pdf", "wb").write(p)
|
||||
|
@ -87,10 +87,6 @@ Problème de connexion (identifiant, mot de passe): <em>contacter votre responsa
|
||||
)
|
||||
|
||||
|
||||
_TOP_LEVEL_CSS = """
|
||||
<style type="text/css">
|
||||
</style>"""
|
||||
|
||||
_HTML_BEGIN = """<?xml version="1.0" encoding="%(encoding)s"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
@ -105,31 +101,30 @@ _HTML_BEGIN = """<?xml version="1.0" encoding="%(encoding)s"?>
|
||||
|
||||
<link href="/ScoDoc/static/css/scodoc.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/ScoDoc/static/css/menu.css" rel="stylesheet" type="text/css" />
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/sorttable.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script type="text/javascript">
|
||||
<script src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/sorttable.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script>
|
||||
window.onload=function(){enableTooltips("gtrcontent")};
|
||||
</script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery.field.min.js"></script>
|
||||
<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
<script src="/ScoDoc/static/jQuery/jquery-migrate-1.2.0.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/jquery.field.min.js"></script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css" />
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/scodoc.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/etud_info.js"></script>
|
||||
<script src="/ScoDoc/static/js/scodoc.js"></script>
|
||||
<script src="/ScoDoc/static/js/etud_info.js"></script>
|
||||
"""
|
||||
|
||||
|
||||
def scodoc_top_html_header(page_title="ScoDoc: bienvenue"):
|
||||
H = [
|
||||
_HTML_BEGIN % {"page_title": page_title, "encoding": scu.SCO_ENCODING},
|
||||
_TOP_LEVEL_CSS,
|
||||
"""</head><body class="gtrcontent" id="gtrcontent">""",
|
||||
scu.CUSTOM_HTML_HEADER_CNX,
|
||||
]
|
||||
@ -185,13 +180,10 @@ def sco_header(
|
||||
init_jquery = True
|
||||
|
||||
H = [
|
||||
"""<?xml version="1.0" encoding="%(encoding)s"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
"""<!DOCTYPE html><html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>%(page_title)s</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=%(encoding)s" />
|
||||
<meta http-equiv="Content-Style-Type" content="text/css" />
|
||||
<meta name="LANG" content="fr" />
|
||||
<meta name="DESCRIPTION" content="ScoDoc" />
|
||||
|
||||
@ -206,9 +198,7 @@ def sco_header(
|
||||
)
|
||||
if init_google_maps:
|
||||
# It may be necessary to add an API key:
|
||||
H.append(
|
||||
'<script type="text/javascript" src="https://maps.google.com/maps/api/js"></script>'
|
||||
)
|
||||
H.append('<script src="https://maps.google.com/maps/api/js"></script>')
|
||||
|
||||
# Feuilles de style additionnelles:
|
||||
for cssstyle in cssstyles:
|
||||
@ -223,9 +213,9 @@ def sco_header(
|
||||
<link href="/ScoDoc/static/css/menu.css" rel="stylesheet" type="text/css" />
|
||||
<link href="/ScoDoc/static/css/gt_table.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script type="text/javascript">
|
||||
<script src="/ScoDoc/static/libjs/menu.js"></script>
|
||||
<script src="/ScoDoc/static/libjs/bubble.js"></script>
|
||||
<script>
|
||||
window.onload=function(){enableTooltips("gtrcontent")};
|
||||
|
||||
var SCO_URL="%(ScoURL)s";
|
||||
@ -236,16 +226,14 @@ def sco_header(
|
||||
# jQuery
|
||||
if init_jquery:
|
||||
H.append(
|
||||
"""<script language="javascript" type="text/javascript" src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
"""<script src="/ScoDoc/static/jQuery/jquery.js"></script>
|
||||
"""
|
||||
)
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery.field.min.js"></script>'
|
||||
)
|
||||
H.append('<script src="/ScoDoc/static/libjs/jquery.field.min.js"></script>')
|
||||
# qTip
|
||||
if init_qtip:
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.js"></script>'
|
||||
)
|
||||
H.append(
|
||||
'<link type="text/css" rel="stylesheet" href="/ScoDoc/static/libjs/qtip/jquery.qtip-3.0.3.min.css" />'
|
||||
@ -253,32 +241,25 @@ def sco_header(
|
||||
|
||||
if init_jquery_ui:
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>'
|
||||
)
|
||||
# H.append('<script language="javascript" type="text/javascript" src="/ScoDoc/static/libjs/jquery-ui/js/jquery-ui-i18n.js"></script>')
|
||||
H.append(
|
||||
'<script language="javascript" type="text/javascript" src="/ScoDoc/static/js/scodoc.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min.js"></script>'
|
||||
)
|
||||
# H.append('<script src="/ScoDoc/static/libjs/jquery-ui/js/jquery-ui-i18n.js"></script>')
|
||||
H.append('<script src="/ScoDoc/static/js/scodoc.js"></script>')
|
||||
if init_google_maps:
|
||||
H.append(
|
||||
'<script type="text/javascript" src="/ScoDoc/static/libjs/jquery.ui.map.full.min.js"></script>'
|
||||
'<script src="/ScoDoc/static/libjs/jquery.ui.map.full.min.js"></script>'
|
||||
)
|
||||
if init_datatables:
|
||||
H.append(
|
||||
'<link rel="stylesheet" type="text/css" href="/ScoDoc/static/DataTables/datatables.min.css"/>'
|
||||
)
|
||||
H.append(
|
||||
'<script type="text/javascript" src="/ScoDoc/static/DataTables/datatables.min.js"></script>'
|
||||
)
|
||||
H.append('<script src="/ScoDoc/static/DataTables/datatables.min.js"></script>')
|
||||
# JS additionels
|
||||
for js in javascripts:
|
||||
H.append(
|
||||
"""<script language="javascript" type="text/javascript" src="/ScoDoc/static/%s"></script>\n"""
|
||||
% js
|
||||
)
|
||||
H.append("""<script src="/ScoDoc/static/%s"></script>\n""" % js)
|
||||
|
||||
H.append(
|
||||
"""<style type="text/css">
|
||||
"""<style>
|
||||
.gtrcontent {
|
||||
margin-left: %(margin_left)s;
|
||||
height: 100%%;
|
||||
@ -290,7 +271,7 @@ def sco_header(
|
||||
)
|
||||
# Scripts de la page:
|
||||
if scripts:
|
||||
H.append("""<script language="javascript" type="text/javascript">""")
|
||||
H.append("""<script>""")
|
||||
for script in scripts:
|
||||
H.append(script)
|
||||
H.append("""</script>""")
|
||||
@ -337,13 +318,7 @@ def sco_footer():
|
||||
|
||||
|
||||
def html_sem_header(
|
||||
REQUEST,
|
||||
title,
|
||||
sem=None,
|
||||
with_page_header=True,
|
||||
with_h2=True,
|
||||
page_title=None,
|
||||
**args
|
||||
title, sem=None, with_page_header=True, with_h2=True, page_title=None, **args
|
||||
):
|
||||
"Titre d'une page semestre avec lien vers tableau de bord"
|
||||
# sem now unused and thus optional...
|
||||
|
@ -28,9 +28,8 @@
|
||||
"""
|
||||
Génération de la "sidebar" (marge gauche des pages HTML)
|
||||
"""
|
||||
from flask import url_for
|
||||
from flask import g
|
||||
from flask import request
|
||||
from flask import render_template, url_for
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -40,17 +39,11 @@ from app.scodoc.sco_permissions import Permission
|
||||
|
||||
def sidebar_common():
|
||||
"partie commune à toutes les sidebar"
|
||||
params = {
|
||||
"ScoURL": scu.ScoURL(),
|
||||
"UsersURL": scu.UsersURL(),
|
||||
"NotesURL": scu.NotesURL(),
|
||||
"AbsencesURL": scu.AbsencesURL(),
|
||||
"authuser": current_user.user_name,
|
||||
}
|
||||
H = [
|
||||
f"""<a class="scodoc_title" href="about">ScoDoc 9</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)
|
||||
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>
|
||||
@ -71,7 +64,8 @@ def sidebar_common():
|
||||
|
||||
if current_user.has_permission(Permission.ScoChangePreferences):
|
||||
H.append(
|
||||
f"""<a href="{url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept)}" class="sidebar">Paramétrage</a> <br/>"""
|
||||
f"""<a href="{url_for("scolar.edit_preferences", scodoc_dept=g.scodoc_dept)}"
|
||||
class="sidebar">Paramétrage</a> <br/>"""
|
||||
)
|
||||
|
||||
return "".join(H)
|
||||
@ -97,11 +91,12 @@ def sidebar():
|
||||
"""
|
||||
]
|
||||
# ---- 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)
|
||||
etudid = g.get("etudid", None)
|
||||
if not etudid:
|
||||
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=True, etudid=etudid)[0]
|
||||
@ -155,8 +150,9 @@ def sidebar():
|
||||
<div class="sidebar-bottom"><a href="{ url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }" class="sidebar">À propos</a><br/>
|
||||
<a href="{ scu.SCO_USER_MANUAL }" target="_blank" class="sidebar">Aide</a>
|
||||
</div></div>
|
||||
<div class="logo-logo"><a href= { url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }
|
||||
">{ scu.icontag("scologo_img", no_size=True) }</a>
|
||||
<div class="logo-logo">
|
||||
<a href="{ url_for( 'scodoc.about', scodoc_dept=g.scodoc_dept ) }">
|
||||
{ scu.icontag("scologo_img", no_size=True) }</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end of sidebar -->
|
||||
@ -167,19 +163,7 @@ def sidebar():
|
||||
|
||||
def sidebar_dept():
|
||||
"""Partie supérieure de la marge de gauche"""
|
||||
H = [
|
||||
f"""<h2 class="insidebar">Dépt. {sco_preferences.get_preference("DeptName")}</h2>
|
||||
<a href="{url_for("scodoc.index")}" class="sidebar">Accueil</a> <br/> """
|
||||
]
|
||||
dept_intranet_url = sco_preferences.get_preference("DeptIntranetURL")
|
||||
if dept_intranet_url:
|
||||
H.append(
|
||||
f"""<a href="{dept_intranet_url}" class="sidebar">{
|
||||
sco_preferences.get_preference("DeptIntranetTitle")}</a> <br/>"""
|
||||
)
|
||||
# Entreprises pas encore supporté en ScoDoc8
|
||||
# H.append(
|
||||
# """<br/><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br/>"""
|
||||
# % infos
|
||||
# )
|
||||
return "\n".join(H)
|
||||
return render_template(
|
||||
"sidebar_dept.html",
|
||||
prefs=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
@ -186,6 +186,8 @@ class NotesTable(object):
|
||||
self.use_ue_coefs = sco_preferences.get_preference(
|
||||
"use_ue_coefs", formsemestre_id
|
||||
)
|
||||
# si vrai, bloque calcul des moy gen. et d'UE.:
|
||||
self.block_moyennes = self.sem["block_moyennes"]
|
||||
# Infos sur les etudiants
|
||||
self.inscrlist = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||
args={"formsemestre_id": formsemestre_id}
|
||||
@ -738,6 +740,7 @@ class NotesTable(object):
|
||||
block_computation = (
|
||||
self.inscrdict[etudid]["etat"] == "D"
|
||||
or self.inscrdict[etudid]["etat"] == DEF
|
||||
or self.block_moyennes
|
||||
)
|
||||
|
||||
moy_ues = {}
|
||||
|
@ -88,7 +88,15 @@ def SimpleDictFetch(query, args, cursor=None):
|
||||
return cursor.dictfetchall()
|
||||
|
||||
|
||||
def DBInsertDict(cnx, table, vals, commit=0, convert_empty_to_nulls=1, return_id=True):
|
||||
def DBInsertDict(
|
||||
cnx,
|
||||
table,
|
||||
vals,
|
||||
commit=0,
|
||||
convert_empty_to_nulls=1,
|
||||
return_id=True,
|
||||
ignore_conflicts=False,
|
||||
):
|
||||
"""insert into table values in dict 'vals'
|
||||
Return: id de l'object créé
|
||||
"""
|
||||
@ -103,13 +111,18 @@ def DBInsertDict(cnx, table, vals, commit=0, convert_empty_to_nulls=1, return_id
|
||||
fmt = ",".join(["%%(%s)s" % col for col in cols])
|
||||
# print 'insert into %s (%s) values (%s)' % (table,colnames,fmt)
|
||||
oid = None
|
||||
if ignore_conflicts:
|
||||
ignore = " ON CONFLICT DO NOTHING"
|
||||
else:
|
||||
ignore = ""
|
||||
try:
|
||||
if vals:
|
||||
cursor.execute(
|
||||
"insert into %s (%s) values (%s)" % (table, colnames, fmt), vals
|
||||
"insert into %s (%s) values (%s)%s" % (table, colnames, fmt, ignore),
|
||||
vals,
|
||||
)
|
||||
else:
|
||||
cursor.execute("insert into %s default values" % table)
|
||||
cursor.execute("insert into %s default values%s" % (table, ignore))
|
||||
if return_id:
|
||||
cursor.execute(f"SELECT CURRVAL('{table}_id_seq')") # id créé
|
||||
oid = cursor.fetchone()[0]
|
||||
@ -291,6 +304,7 @@ class EditableTable(object):
|
||||
fields_creators={}, # { field : [ sql_command_to_create_it ] }
|
||||
filter_nulls=True, # dont allow to set fields to null
|
||||
filter_dept=False, # ajoute selection sur g.scodoc_dept_id
|
||||
insert_ignore_conflicts=False,
|
||||
):
|
||||
self.table_name = table_name
|
||||
self.id_name = id_name
|
||||
@ -311,6 +325,7 @@ class EditableTable(object):
|
||||
self.filter_nulls = filter_nulls
|
||||
self.filter_dept = filter_dept
|
||||
self.sql_default_values = None
|
||||
self.insert_ignore_conflicts = insert_ignore_conflicts
|
||||
|
||||
def create(self, cnx, args):
|
||||
"create object in table"
|
||||
@ -336,6 +351,7 @@ class EditableTable(object):
|
||||
vals,
|
||||
commit=True,
|
||||
return_id=(self.id_name is not None),
|
||||
ignore_conflicts=self.insert_ignore_conflicts,
|
||||
)
|
||||
return new_id
|
||||
|
||||
|
@ -626,7 +626,6 @@ def add_absence(
|
||||
jour,
|
||||
matin,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=None,
|
||||
moduleimpl_id=None,
|
||||
):
|
||||
@ -656,7 +655,7 @@ def add_absence(
|
||||
sco_abs_notification.abs_notify(etudid, jour)
|
||||
|
||||
|
||||
def add_justif(etudid, jour, matin, REQUEST, description=None):
|
||||
def add_justif(etudid, jour, matin, description=None):
|
||||
"Ajoute un justificatif dans la base"
|
||||
# unpublished
|
||||
if _isFarFutur(jour):
|
||||
@ -665,7 +664,9 @@ def add_justif(etudid, jour, matin, REQUEST, description=None):
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
cursor.execute(
|
||||
"insert into absences (etudid,jour,estabs,estjust,matin, description) values (%(etudid)s,%(jour)s, FALSE, TRUE, %(matin)s, %(description)s )",
|
||||
"""INSERT INTO absences (etudid, jour, estabs, estjust, matin, description)
|
||||
VALUES (%(etudid)s, %(jour)s, FALSE, TRUE, %(matin)s, %(description)s)
|
||||
""",
|
||||
vars(),
|
||||
)
|
||||
logdb(
|
||||
@ -678,7 +679,7 @@ def add_justif(etudid, jour, matin, REQUEST, description=None):
|
||||
invalidate_abs_etud_date(etudid, jour)
|
||||
|
||||
|
||||
def _add_abslist(abslist, REQUEST, moduleimpl_id=None):
|
||||
def add_abslist(abslist, moduleimpl_id=None):
|
||||
for a in abslist:
|
||||
etudid, jour, ampm = a.split(":")
|
||||
if ampm == "am":
|
||||
@ -689,7 +690,7 @@ def _add_abslist(abslist, REQUEST, moduleimpl_id=None):
|
||||
raise ValueError("invalid ampm !")
|
||||
# ajoute abs si pas deja absent
|
||||
if count_abs(etudid, jour, jour, matin, moduleimpl_id) == 0:
|
||||
add_absence(etudid, jour, matin, 0, REQUEST, "", moduleimpl_id)
|
||||
add_absence(etudid, jour, matin, 0, "", moduleimpl_id)
|
||||
|
||||
|
||||
def annule_absence(etudid, jour, matin, moduleimpl_id=None):
|
||||
@ -721,7 +722,7 @@ def annule_absence(etudid, jour, matin, moduleimpl_id=None):
|
||||
invalidate_abs_etud_date(etudid, jour)
|
||||
|
||||
|
||||
def annule_justif(etudid, jour, matin, REQUEST=None):
|
||||
def annule_justif(etudid, jour, matin):
|
||||
"Annule un justificatif"
|
||||
# unpublished
|
||||
matin = _toboolean(matin)
|
||||
|
@ -30,7 +30,7 @@
|
||||
"""
|
||||
import datetime
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
@ -58,7 +58,6 @@ def doSignaleAbsence(
|
||||
estjust=False,
|
||||
description=None,
|
||||
etudid=False,
|
||||
REQUEST=None,
|
||||
): # etudid implied
|
||||
"""Signalement d'une absence.
|
||||
|
||||
@ -69,7 +68,8 @@ def doSignaleAbsence(
|
||||
demijournee: 2 si journée complète, 1 matin, 0 après-midi
|
||||
estjust: absence justifiée
|
||||
description: str
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans REQUEST.form
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans
|
||||
les paramètres de la requête courante.
|
||||
"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
@ -86,7 +86,6 @@ def doSignaleAbsence(
|
||||
jour,
|
||||
False,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
@ -95,7 +94,6 @@ def doSignaleAbsence(
|
||||
jour,
|
||||
True,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
@ -106,7 +104,6 @@ def doSignaleAbsence(
|
||||
jour,
|
||||
demijournee,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description_abs,
|
||||
moduleimpl_id,
|
||||
)
|
||||
@ -156,7 +153,7 @@ def doSignaleAbsence(
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def SignaleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def SignaleAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple de signalement d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
@ -228,7 +225,6 @@ def SignaleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""
|
||||
@ -281,7 +277,6 @@ def doJustifAbsence(
|
||||
demijournee,
|
||||
description=None,
|
||||
etudid=False,
|
||||
REQUEST=None,
|
||||
): # etudid implied
|
||||
"""Justification d'une absence
|
||||
|
||||
@ -291,7 +286,8 @@ def doJustifAbsence(
|
||||
demijournee: 2 si journée complète, 1 matin, 0 après-midi
|
||||
estjust: absence justifiée
|
||||
description: str
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans REQUEST.form
|
||||
etudid: etudiant concerné. Si non spécifié, cherche dans les
|
||||
paramètres de la requête.
|
||||
"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
@ -305,14 +301,12 @@ def doJustifAbsence(
|
||||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=False,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
sco_abs.add_justif(
|
||||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=True,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
nbadded += 2
|
||||
@ -321,7 +315,6 @@ def doJustifAbsence(
|
||||
etudid=etudid,
|
||||
jour=jour,
|
||||
matin=demijournee,
|
||||
REQUEST=REQUEST,
|
||||
description=description_abs,
|
||||
)
|
||||
nbadded += 1
|
||||
@ -357,7 +350,7 @@ def doJustifAbsence(
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def JustifAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def JustifAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple de justification d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
@ -376,7 +369,6 @@ def JustifAbsenceEtud(REQUEST=None): # etudid implied
|
||||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""
|
||||
@ -412,9 +404,7 @@ Raison: <input type="text" name="description" size="42"/> (optionnel)
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def doAnnuleAbsence(
|
||||
datedebut, datefin, demijournee, etudid=False, REQUEST=None
|
||||
): # etudid implied
|
||||
def doAnnuleAbsence(datedebut, datefin, demijournee, etudid=False): # etudid implied
|
||||
"""Annulation des absences pour une demi journée"""
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
@ -462,7 +452,7 @@ autre absence pour <b>%(nomprenom)s</b></a></li>
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
def AnnuleAbsenceEtud(): # etudid implied
|
||||
"""Formulaire individuel simple d'annulation d'une absence"""
|
||||
# brute-force portage from very old dtml code ...
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
@ -482,7 +472,6 @@ def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
"""</a></td></tr></table>""",
|
||||
"""<p>A n'utiliser que suite à une erreur de saisie ou lorsqu'il s'avère que l'étudiant était en fait présent. </p>
|
||||
@ -548,7 +537,7 @@ def AnnuleAbsenceEtud(REQUEST=None): # etudid implied
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def doAnnuleJustif(datedebut0, datefin0, demijournee, REQUEST=None): # etudid implied
|
||||
def doAnnuleJustif(datedebut0, datefin0, demijournee): # etudid implied
|
||||
"""Annulation d'une justification"""
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
etudid = etud["etudid"]
|
||||
@ -558,11 +547,11 @@ def doAnnuleJustif(datedebut0, datefin0, demijournee, REQUEST=None): # etudid i
|
||||
for jour in dates:
|
||||
# Attention: supprime matin et après-midi
|
||||
if demijournee == 2:
|
||||
sco_abs.annule_justif(etudid, jour, False, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, True, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, False)
|
||||
sco_abs.annule_justif(etudid, jour, True)
|
||||
nbadded += 2
|
||||
else:
|
||||
sco_abs.annule_justif(etudid, jour, demijournee, REQUEST=REQUEST)
|
||||
sco_abs.annule_justif(etudid, jour, demijournee)
|
||||
nbadded += 1
|
||||
#
|
||||
H = [
|
||||
@ -716,7 +705,6 @@ def formChoixSemestreGroupe(all=False):
|
||||
def CalAbs(etudid, sco_year=None):
|
||||
"""Calendrier des absences d'un etudiant"""
|
||||
# crude portage from 1999 DTML
|
||||
REQUEST = None # XXX
|
||||
etud = sco_etud.get_etud_info(filled=True, etudid=etudid)[0]
|
||||
etudid = etud["etudid"]
|
||||
anneescolaire = int(scu.AnneeScolaire(sco_year))
|
||||
@ -766,7 +754,6 @@ def CalAbs(etudid, sco_year=None):
|
||||
sco_photos.etud_photo_html(
|
||||
etudid=etudid,
|
||||
title="fiche de " + etud["nomprenom"],
|
||||
REQUEST=REQUEST,
|
||||
),
|
||||
),
|
||||
CalHTML,
|
||||
@ -791,7 +778,6 @@ def ListeAbsEtud(
|
||||
format="html",
|
||||
absjust_only=0,
|
||||
sco_year=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Liste des absences d'un étudiant sur l'année en cours
|
||||
En format 'html': page avec deux tableaux (non justifiées et justifiées).
|
||||
@ -810,12 +796,12 @@ def ListeAbsEtud(
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
|
||||
# Liste des absences et titres colonnes tables:
|
||||
titles, columns_ids, absnonjust, absjust = _TablesAbsEtud(
|
||||
titles, columns_ids, absnonjust, absjust = _tables_abs_etud(
|
||||
etudid, datedebut, with_evals=with_evals, format=format
|
||||
)
|
||||
if REQUEST:
|
||||
base_url_nj = "%s?etudid=%s&absjust_only=0" % (REQUEST.URL0, etudid)
|
||||
base_url_j = "%s?etudid=%s&absjust_only=1" % (REQUEST.URL0, etudid)
|
||||
if request.base_url:
|
||||
base_url_nj = "%s?etudid=%s&absjust_only=0" % (request.base_url, etudid)
|
||||
base_url_j = "%s?etudid=%s&absjust_only=1" % (request.base_url, etudid)
|
||||
else:
|
||||
base_url_nj = base_url_j = ""
|
||||
tab_absnonjust = GenTable(
|
||||
@ -844,9 +830,9 @@ def ListeAbsEtud(
|
||||
# Formats non HTML et demande d'une seule table:
|
||||
if format != "html" and format != "text":
|
||||
if absjust_only == 1:
|
||||
return tab_absjust.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab_absjust.make_page(format=format)
|
||||
else:
|
||||
return tab_absnonjust.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab_absnonjust.make_page(format=format)
|
||||
|
||||
if format == "html":
|
||||
# Mise en forme HTML:
|
||||
@ -896,13 +882,12 @@ def ListeAbsEtud(
|
||||
raise ValueError("Invalid format !")
|
||||
|
||||
|
||||
def _TablesAbsEtud(
|
||||
def _tables_abs_etud(
|
||||
etudid,
|
||||
datedebut,
|
||||
with_evals=True,
|
||||
format="html",
|
||||
absjust_only=0,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Tables des absences justifiees et non justifiees d'un étudiant
|
||||
sur l'année en cours
|
||||
@ -928,11 +913,11 @@ def _TablesAbsEtud(
|
||||
cursor.execute(
|
||||
"""SELECT mi.moduleimpl_id
|
||||
FROM absences abs, notes_moduleimpl_inscription mi, notes_moduleimpl m
|
||||
WHERE abs.matin = %(matin)s
|
||||
and abs.jour = %(jour)s
|
||||
and abs.etudid = %(etudid)s
|
||||
and abs.moduleimpl_id = mi.moduleimpl_id
|
||||
and mi.moduleimpl_id = m.id
|
||||
WHERE abs.matin = %(matin)s
|
||||
and abs.jour = %(jour)s
|
||||
and abs.etudid = %(etudid)s
|
||||
and abs.moduleimpl_id = mi.moduleimpl_id
|
||||
and mi.moduleimpl_id = m.id
|
||||
and mi.etudid = %(etudid)s
|
||||
""",
|
||||
{
|
||||
@ -959,8 +944,9 @@ def _TablesAbsEtud(
|
||||
)[0]
|
||||
if format == "html":
|
||||
ex.append(
|
||||
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
|
||||
% (mod["moduleimpl_id"], mod["module"]["code"])
|
||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"]}</a>"""
|
||||
)
|
||||
else:
|
||||
ex.append(mod["module"]["code"])
|
||||
@ -976,8 +962,9 @@ def _TablesAbsEtud(
|
||||
)[0]
|
||||
if format == "html":
|
||||
ex.append(
|
||||
'<a href="Notes/moduleimpl_status?moduleimpl_id=%s">%s</a>'
|
||||
% (mod["moduleimpl_id"], mod["module"]["code"])
|
||||
f"""<a href="{url_for('notes.moduleimpl_status',
|
||||
scodoc_dept=g.scodoc_dept, moduleimpl_id=mod["moduleimpl_id"])}
|
||||
">{mod["module"]["code"]}</a>"""
|
||||
)
|
||||
else:
|
||||
ex.append(mod["module"]["code"])
|
||||
|
@ -29,37 +29,40 @@
|
||||
|
||||
|
||||
Archives are plain files, stored in
|
||||
<SCODOC_VAR_DIR>/archives/<deptid>
|
||||
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <deptid> a departement id)
|
||||
<SCODOC_VAR_DIR>/archives/<dept_id>
|
||||
(where <SCODOC_VAR_DIR> is usually /opt/scodoc-data, and <dept_id> a departement id (int))
|
||||
|
||||
Les PV de jurys et documents associés sont stockées dans un sous-repertoire de la forme
|
||||
<archivedir>/<dept>/<formsemestre_id>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(formsemestre_id est ici FormSemestre.scodoc7_id ou à défaut FormSemestre.id)
|
||||
(formsemestre_id est ici FormSemestre.id)
|
||||
|
||||
Les documents liés à l'étudiant sont dans
|
||||
<archivedir>/docetuds/<dept>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(etudid est ici soit Identite.scodoc7id, soit à défaut Identite.id)
|
||||
<archivedir>/docetuds/<dept_id>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
||||
(etudid est ici Identite.id)
|
||||
|
||||
Les maquettes Apogée pour l'export des notes sont dans
|
||||
<archivedir>/apo_csv/<dept>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
||||
<archivedir>/apo_csv/<dept_id>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
||||
|
||||
Un répertoire d'archive contient des fichiers quelconques, et un fichier texte nommé _description.txt
|
||||
qui est une description (humaine, format libre) de l'archive.
|
||||
|
||||
"""
|
||||
import os
|
||||
import time
|
||||
import datetime
|
||||
import glob
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import glob
|
||||
import time
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from config import Config
|
||||
from app import log
|
||||
from app.models import Departement
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc.sco_exceptions import (
|
||||
AccessDenied,
|
||||
@ -108,7 +111,8 @@ class BaseArchiver(object):
|
||||
If directory does not yet exist, create it.
|
||||
"""
|
||||
self.initialize()
|
||||
dept_dir = os.path.join(self.root, g.scodoc_dept)
|
||||
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
|
||||
dept_dir = os.path.join(self.root, str(dept.id))
|
||||
try:
|
||||
scu.GSL.acquire()
|
||||
if not os.path.isdir(dept_dir):
|
||||
@ -127,7 +131,8 @@ class BaseArchiver(object):
|
||||
:return: list of archive oids
|
||||
"""
|
||||
self.initialize()
|
||||
base = os.path.join(self.root, g.scodoc_dept) + os.path.sep
|
||||
dept = Departement.query.filter_by(acronym=g.scodoc_dept).first()
|
||||
base = os.path.join(self.root, str(dept.id)) + os.path.sep
|
||||
dirs = glob.glob(base + "*")
|
||||
return [os.path.split(x)[1] for x in dirs]
|
||||
|
||||
@ -244,31 +249,15 @@ class BaseArchiver(object):
|
||||
log("reading archive file %s" % fname)
|
||||
return open(fname, "rb").read()
|
||||
|
||||
def get_archived_file(self, REQUEST, oid, archive_name, filename):
|
||||
def get_archived_file(self, oid, archive_name, filename):
|
||||
"""Recupere donnees du fichier indiqué et envoie au client"""
|
||||
# XXX très incomplet: devrait inférer et assigner un type MIME
|
||||
archive_id = self.get_id_from_name(oid, archive_name)
|
||||
data = self.get(archive_id, filename)
|
||||
ext = os.path.splitext(filename.lower())[1]
|
||||
if ext == ".html" or ext == ".htm":
|
||||
return data
|
||||
elif ext == ".xml":
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
return data
|
||||
elif ext == ".xls":
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, filename, mime=scu.XLS_MIMETYPE
|
||||
)
|
||||
elif ext == ".xlsx":
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, filename, mime=scu.XLSX_MIMETYPE
|
||||
)
|
||||
elif ext == ".csv":
|
||||
return scu.sendCSVFile(REQUEST, data, filename)
|
||||
elif ext == ".pdf":
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
REQUEST.RESPONSE.setHeader("content-type", "application/octet-stream")
|
||||
return data # should set mimetype for known files like images
|
||||
mime = mimetypes.guess_type(filename)[0]
|
||||
if mime is None:
|
||||
mime = "application/octet-stream"
|
||||
|
||||
return scu.send_file(data, filename, mime=mime)
|
||||
|
||||
|
||||
class SemsArchiver(BaseArchiver):
|
||||
@ -283,7 +272,6 @@ PVArchive = SemsArchiver()
|
||||
|
||||
|
||||
def do_formsemestre_archive(
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
group_ids=[], # si indiqué, ne prend que ces groupes
|
||||
description="",
|
||||
@ -305,7 +293,7 @@ def do_formsemestre_archive(
|
||||
from app.scodoc.sco_recapcomplet import make_formsemestre_recapcomplet
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
archive_id = PVArchive.create_obj_archive(sem_archive_id, description)
|
||||
date = PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
|
||||
|
||||
@ -351,14 +339,12 @@ def do_formsemestre_archive(
|
||||
data = data.encode(scu.SCO_ENCODING)
|
||||
PVArchive.store(archive_id, "Bulletins.xml", data)
|
||||
# Decisions de jury, en XLS
|
||||
data = sco_pvjury.formsemestre_pvjury(
|
||||
formsemestre_id, format="xls", REQUEST=REQUEST, publish=False
|
||||
)
|
||||
data = sco_pvjury.formsemestre_pvjury(formsemestre_id, format="xls", publish=False)
|
||||
if data:
|
||||
PVArchive.store(archive_id, "Decisions_Jury" + scu.XLSX_SUFFIX, data)
|
||||
# Classeur bulletins (PDF)
|
||||
data, _ = sco_bulletins_pdf.get_formsemestre_bulletins_pdf(
|
||||
formsemestre_id, REQUEST, version=bulVersion
|
||||
formsemestre_id, version=bulVersion
|
||||
)
|
||||
if data:
|
||||
PVArchive.store(archive_id, "Bulletins.pdf", data)
|
||||
@ -389,14 +375,12 @@ def do_formsemestre_archive(
|
||||
PVArchive.store(archive_id, "PV_Jury%s.pdf" % groups_filename, data)
|
||||
|
||||
|
||||
def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
|
||||
def formsemestre_archive(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(formsemestre_id):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not group_ids:
|
||||
@ -408,7 +392,6 @@ def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Archiver les PV et résultats du semestre",
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
@ -469,8 +452,8 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
method="POST",
|
||||
@ -492,7 +475,6 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
||||
else:
|
||||
tf[2]["anonymous"] = False
|
||||
do_formsemestre_archive(
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
group_ids=group_ids,
|
||||
description=tf[2]["description"],
|
||||
@ -516,10 +498,10 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
||||
)
|
||||
|
||||
|
||||
def formsemestre_list_archives(REQUEST, formsemestre_id):
|
||||
def formsemestre_list_archives(formsemestre_id):
|
||||
"""Page listing archives"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
L = []
|
||||
for archive_id in PVArchive.list_obj_archives(sem_archive_id):
|
||||
a = {
|
||||
@ -530,7 +512,7 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
||||
}
|
||||
L.append(a)
|
||||
|
||||
H = [html_sco_header.html_sem_header(REQUEST, "Archive des PV et résultats ", sem)]
|
||||
H = [html_sco_header.html_sem_header("Archive des PV et résultats ", sem)]
|
||||
if not L:
|
||||
H.append("<p>aucune archive enregistrée</p>")
|
||||
else:
|
||||
@ -559,23 +541,19 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def formsemestre_get_archived_file(REQUEST, formsemestre_id, archive_name, filename):
|
||||
def formsemestre_get_archived_file(formsemestre_id, archive_name, filename):
|
||||
"""Send file to client."""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
return PVArchive.get_archived_file(REQUEST, sem_archive_id, archive_name, filename)
|
||||
sem_archive_id = formsemestre_id
|
||||
return PVArchive.get_archived_file(sem_archive_id, archive_name, filename)
|
||||
|
||||
|
||||
def formsemestre_delete_archive(
|
||||
REQUEST, formsemestre_id, archive_name, dialog_confirmed=False
|
||||
):
|
||||
def formsemestre_delete_archive(formsemestre_id, archive_name, dialog_confirmed=False):
|
||||
"""Delete an archive"""
|
||||
if not sco_permissions_check.can_edit_pv(formsemestre_id):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||
sem_archive_id = formsemestre_id
|
||||
archive_id = PVArchive.get_id_from_name(sem_archive_id, archive_name)
|
||||
|
||||
dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id)
|
||||
|
@ -30,7 +30,8 @@
|
||||
les dossiers d'admission et autres pièces utiles.
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_import_etuds
|
||||
@ -58,14 +59,14 @@ def can_edit_etud_archive(authuser):
|
||||
return authuser.has_permission(Permission.ScoEtudAddAnnotations)
|
||||
|
||||
|
||||
def etud_list_archives_html(REQUEST, etudid):
|
||||
def etud_list_archives_html(etudid):
|
||||
"""HTML snippet listing archives"""
|
||||
can_edit = can_edit_etud_archive(REQUEST.AUTHENTICATED_USER)
|
||||
can_edit = can_edit_etud_archive(current_user)
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etudid
|
||||
etud_archive_id = etudid
|
||||
L = []
|
||||
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||
a = {
|
||||
@ -118,7 +119,7 @@ def add_archives_info_to_etud_list(etuds):
|
||||
"""
|
||||
for etud in etuds:
|
||||
l = []
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||
l.append(
|
||||
"%s (%s)"
|
||||
@ -130,13 +131,11 @@ def add_archives_info_to_etud_list(etuds):
|
||||
etud["etudarchive"] = ", ".join(l)
|
||||
|
||||
|
||||
def etud_upload_file_form(REQUEST, etudid):
|
||||
def etud_upload_file_form(etudid):
|
||||
"""Page with a form to choose and upload a file, with a description."""
|
||||
# check permission
|
||||
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % REQUEST.AUTHENTICATED_USER
|
||||
)
|
||||
if not can_edit_etud_archive(current_user):
|
||||
raise AccessDenied("opération non autorisée pour %s" % current_user)
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
@ -153,8 +152,8 @@ def etud_upload_file_form(REQUEST, etudid):
|
||||
% (scu.CONFIG.ETUD_MAX_FILE_SIZE // (1024 * 1024)),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("etudid", {"default": etudid, "input_type": "hidden"}),
|
||||
("datafile", {"input_type": "file", "title": "Fichier", "size": 30}),
|
||||
@ -181,7 +180,7 @@ def etud_upload_file_form(REQUEST, etudid):
|
||||
data = tf[2]["datafile"].read()
|
||||
descr = tf[2]["description"]
|
||||
filename = tf[2]["datafile"].filename
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
_store_etud_file_to_new_archive(
|
||||
etud_archive_id, data, filename, description=descr
|
||||
)
|
||||
@ -199,18 +198,16 @@ def _store_etud_file_to_new_archive(etud_archive_id, data, filename, description
|
||||
EtudsArchive.store(archive_id, filename, data)
|
||||
|
||||
|
||||
def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
||||
def etud_delete_archive(etudid, archive_name, dialog_confirmed=False):
|
||||
"""Delete an archive"""
|
||||
# check permission
|
||||
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
||||
raise AccessDenied(
|
||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||
)
|
||||
if not can_edit_etud_archive(current_user):
|
||||
raise AccessDenied("opération non autorisée pour %s" % str(current_user))
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
etud_archive_id = etud["etudid"]
|
||||
archive_id = EtudsArchive.get_id_from_name(etud_archive_id, archive_name)
|
||||
if not dialog_confirmed:
|
||||
return scu.confirm_dialog(
|
||||
@ -242,20 +239,18 @@ def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
||||
)
|
||||
|
||||
|
||||
def etud_get_archived_file(REQUEST, etudid, archive_name, filename):
|
||||
def etud_get_archived_file(etudid, archive_name, filename):
|
||||
"""Send file to client."""
|
||||
etuds = sco_etud.get_etud_info(filled=True)
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid, filled=True)
|
||||
if not etuds:
|
||||
raise ScoValueError("étudiant inexistant")
|
||||
etud = etuds[0]
|
||||
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||
return EtudsArchive.get_archived_file(
|
||||
REQUEST, etud_archive_id, archive_name, filename
|
||||
)
|
||||
etud_archive_id = etud["etudid"]
|
||||
return EtudsArchive.get_archived_file(etud_archive_id, archive_name, filename)
|
||||
|
||||
|
||||
# --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants)
|
||||
def etudarchive_generate_excel_sample(group_id=None, REQUEST=None):
|
||||
def etudarchive_generate_excel_sample(group_id=None):
|
||||
"""Feuille excel pour import fichiers etudiants (utilisé pour admissions)"""
|
||||
fmt = sco_import_etuds.sco_import_format()
|
||||
data = sco_import_etuds.sco_import_generate_excel_sample(
|
||||
@ -271,12 +266,15 @@ def etudarchive_generate_excel_sample(group_id=None, REQUEST=None):
|
||||
],
|
||||
extra_cols=["fichier_a_charger"],
|
||||
)
|
||||
return sco_excel.send_excel_file(
|
||||
REQUEST, data, "ImportFichiersEtudiants" + scu.XLSX_SUFFIX
|
||||
return scu.send_file(
|
||||
data,
|
||||
"ImportFichiersEtudiants",
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
|
||||
|
||||
def etudarchive_import_files_form(group_id, REQUEST=None):
|
||||
def etudarchive_import_files_form(group_id):
|
||||
"""Formulaire pour importation fichiers d'un groupe"""
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
@ -310,8 +308,8 @@ def etudarchive_import_files_form(group_id, REQUEST=None):
|
||||
]
|
||||
F = html_sco_header.sco_footer()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),
|
||||
("zipfile", {"title": "Fichier zip:", "input_type": "file", "size": 40}),
|
||||
|
@ -28,6 +28,7 @@
|
||||
"""Génération des bulletins de notes
|
||||
|
||||
"""
|
||||
from app.models import formsemestre
|
||||
import time
|
||||
import pprint
|
||||
import email
|
||||
@ -35,11 +36,10 @@ from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email.header import Header
|
||||
|
||||
from reportlab.lib.colors import Color
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask import url_for
|
||||
from flask_login import current_user
|
||||
from flask_mail import Message
|
||||
@ -48,7 +48,7 @@ import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app import log
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_exceptions import AccessDenied
|
||||
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import htmlutils
|
||||
from app.scodoc import sco_abs
|
||||
@ -121,9 +121,7 @@ def make_context_dict(sem, etud):
|
||||
return C
|
||||
|
||||
|
||||
def formsemestre_bulletinetud_dict(
|
||||
formsemestre_id, etudid, version="long", REQUEST=None
|
||||
):
|
||||
def formsemestre_bulletinetud_dict(formsemestre_id, etudid, version="long"):
|
||||
"""Collecte informations pour bulletin de notes
|
||||
Retourne un dictionnaire (avec valeur par défaut chaine vide).
|
||||
Le contenu du dictionnaire dépend des options (rangs, ...)
|
||||
@ -143,10 +141,7 @@ def formsemestre_bulletinetud_dict(
|
||||
I["etudid"] = etudid
|
||||
I["formsemestre_id"] = formsemestre_id
|
||||
I["sem"] = nt.sem
|
||||
if REQUEST:
|
||||
I["server_name"] = REQUEST.BASE0
|
||||
else:
|
||||
I["server_name"] = ""
|
||||
I["server_name"] = request.url_root
|
||||
|
||||
# Formation et parcours
|
||||
I["formation"] = sco_formations.formation_list(
|
||||
@ -771,14 +766,16 @@ def formsemestre_bulletinetud(
|
||||
xml_with_decisions=False,
|
||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
||||
prefer_mail_perso=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"page bulletin de notes"
|
||||
try:
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
etudid = etud["etudid"]
|
||||
except:
|
||||
return scu.log_unknown_etud(REQUEST, format=format)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
|
||||
bulletin = do_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
@ -788,15 +785,15 @@ def formsemestre_bulletinetud(
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
prefer_mail_perso=prefer_mail_perso,
|
||||
REQUEST=REQUEST,
|
||||
)[0]
|
||||
if format not in {"html", "pdfmail"}:
|
||||
return bulletin
|
||||
filename = scu.bul_filename(sem, etud, format)
|
||||
return scu.send_file(bulletin, filename, mime=scu.get_mime_suffix(format)[0])
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
_formsemestre_bulletinetud_header_html(
|
||||
etud, etudid, sem, formsemestre_id, format, version, REQUEST
|
||||
etud, etudid, sem, formsemestre_id, format, version
|
||||
),
|
||||
bulletin,
|
||||
]
|
||||
@ -854,7 +851,6 @@ def do_formsemestre_bulletinetud(
|
||||
etudid,
|
||||
version="long", # short, long, selectedevals
|
||||
format="html",
|
||||
REQUEST=None,
|
||||
nohtml=False,
|
||||
xml_with_decisions=False, # force decisions dans XML
|
||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
||||
@ -862,14 +858,13 @@ def do_formsemestre_bulletinetud(
|
||||
):
|
||||
"""Génère le bulletin au format demandé.
|
||||
Retourne: (bul, filigranne)
|
||||
où bul est au format demandé (html, pdf, pdfmail, pdfpart, xml)
|
||||
où bul est str ou bytes au format demandé (html, pdf, pdfmail, pdfpart, xml, json)
|
||||
et filigranne est un message à placer en "filigranne" (eg "Provisoire").
|
||||
"""
|
||||
if format == "xml":
|
||||
bul = sco_bulletins_xml.make_xml_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
version=version,
|
||||
@ -881,19 +876,18 @@ def do_formsemestre_bulletinetud(
|
||||
bul = sco_bulletins_json.make_json_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
force_publishing=force_publishing,
|
||||
version=version,
|
||||
)
|
||||
return bul, ""
|
||||
|
||||
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid, REQUEST=REQUEST)
|
||||
I = formsemestre_bulletinetud_dict(formsemestre_id, etudid)
|
||||
etud = I["etud"]
|
||||
|
||||
if format == "html":
|
||||
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="html", REQUEST=REQUEST
|
||||
I, version=version, format="html"
|
||||
)
|
||||
return htm, I["filigranne"]
|
||||
|
||||
@ -903,11 +897,10 @@ def do_formsemestre_bulletinetud(
|
||||
version=version,
|
||||
format="pdf",
|
||||
stand_alone=(format != "pdfpart"),
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
if format == "pdf":
|
||||
return (
|
||||
scu.sendPDFFile(REQUEST, bul, filename),
|
||||
scu.sendPDFFile(bul, filename),
|
||||
I["filigranne"],
|
||||
) # unused ret. value
|
||||
else:
|
||||
@ -923,11 +916,11 @@ def do_formsemestre_bulletinetud(
|
||||
htm = "" # speed up if html version not needed
|
||||
else:
|
||||
htm, _ = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="html", REQUEST=REQUEST
|
||||
I, version=version, format="html"
|
||||
)
|
||||
|
||||
pdfdata, filename = sco_bulletins_generator.make_formsemestre_bulletinetud(
|
||||
I, version=version, format="pdf", REQUEST=REQUEST
|
||||
I, version=version, format="pdf"
|
||||
)
|
||||
|
||||
if prefer_mail_perso:
|
||||
@ -998,7 +991,6 @@ def mail_bulletin(formsemestre_id, I, pdfdata, filename, recipient_addr):
|
||||
|
||||
# Attach pdf
|
||||
msg.attach(filename, scu.PDF_MIMETYPE, pdfdata)
|
||||
|
||||
log("mail bulletin a %s" % recipient_addr)
|
||||
email.send_message(msg)
|
||||
|
||||
@ -1010,7 +1002,6 @@ def _formsemestre_bulletinetud_header_html(
|
||||
formsemestre_id=None,
|
||||
format=None,
|
||||
version=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
@ -1033,7 +1024,7 @@ def _formsemestre_bulletinetud_header_html(
|
||||
),
|
||||
"""
|
||||
<form name="f" method="GET" action="%s">"""
|
||||
% REQUEST.URL0,
|
||||
% request.base_url,
|
||||
f"""Bulletin <span class="bull_liensemestre"><a href="{
|
||||
url_for("notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
@ -1063,14 +1054,20 @@ def _formsemestre_bulletinetud_header_html(
|
||||
H.append("""</select></td>""")
|
||||
# Menu
|
||||
endpoint = "notes.formsemestre_bulletinetud"
|
||||
url = REQUEST.URL0
|
||||
qurl = six.moves.urllib.parse.quote_plus(url + "?" + REQUEST.QUERY_STRING)
|
||||
|
||||
menuBul = [
|
||||
{
|
||||
"title": "Réglages bulletins",
|
||||
"endpoint": "notes.formsemestre_edit_options",
|
||||
"args": {"formsemestre_id": formsemestre_id, "target_url": qurl},
|
||||
"args": {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
# "target_url": url_for(
|
||||
# "notes.formsemestre_bulletinetud",
|
||||
# scodoc_dept=g.scodoc_dept,
|
||||
# formsemestre_id=formsemestre_id,
|
||||
# etudid=etudid,
|
||||
# ),
|
||||
},
|
||||
"enabled": (current_user.id in sem["responsables"])
|
||||
or current_user.has_permission(Permission.ScoImplement),
|
||||
},
|
||||
@ -1113,6 +1110,16 @@ def _formsemestre_bulletinetud_header_html(
|
||||
"enabled": etud["emailperso"]
|
||||
and can_send_bulletin_by_mail(formsemestre_id),
|
||||
},
|
||||
{
|
||||
"title": "Version json",
|
||||
"endpoint": endpoint,
|
||||
"args": {
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"etudid": etudid,
|
||||
"version": version,
|
||||
"format": "json",
|
||||
},
|
||||
},
|
||||
{
|
||||
"title": "Version XML",
|
||||
"endpoint": endpoint,
|
||||
@ -1188,9 +1195,14 @@ def _formsemestre_bulletinetud_header_html(
|
||||
H.append(
|
||||
'<td> <a href="%s">%s</a></td>'
|
||||
% (
|
||||
url
|
||||
+ "?formsemestre_id=%s&etudid=%s&format=pdf&version=%s"
|
||||
% (formsemestre_id, etudid, version),
|
||||
url_for(
|
||||
"notes.formsemestre_bulletinetud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
etudid=etudid,
|
||||
format="pdf",
|
||||
version=version,
|
||||
),
|
||||
scu.ICON_PDF,
|
||||
)
|
||||
)
|
||||
@ -1201,9 +1213,7 @@ def _formsemestre_bulletinetud_header_html(
|
||||
"""
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de " + etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de " + etud["nom"]),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
|
@ -52,6 +52,9 @@ import reportlab
|
||||
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Frame, PageBreak
|
||||
from reportlab.platypus import Table, TableStyle, Image, KeepInFrame
|
||||
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.sco_exceptions import NoteProcessError
|
||||
from app import log
|
||||
@ -148,14 +151,7 @@ class BulletinGenerator(object):
|
||||
def get_filename(self):
|
||||
"""Build a filename to be proposed to the web client"""
|
||||
sem = sco_formsemestre.get_formsemestre(self.infos["formsemestre_id"])
|
||||
dt = time.strftime("%Y-%m-%d")
|
||||
filename = "bul-%s-%s-%s.pdf" % (
|
||||
sem["titre_num"],
|
||||
dt,
|
||||
self.infos["etud"]["nom"],
|
||||
)
|
||||
filename = scu.unescape_html(filename).replace(" ", "_").replace("&", "")
|
||||
return filename
|
||||
return scu.bul_filename(sem, self.infos["etud"], "pdf")
|
||||
|
||||
def generate(self, format="", stand_alone=True):
|
||||
"""Return bulletin in specified format"""
|
||||
@ -260,7 +256,6 @@ def make_formsemestre_bulletinetud(
|
||||
version="long", # short, long, selectedevals
|
||||
format="pdf", # html, pdf
|
||||
stand_alone=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Bulletin de notes
|
||||
|
||||
@ -286,10 +281,10 @@ def make_formsemestre_bulletinetud(
|
||||
PDFLOCK.acquire()
|
||||
bul_generator = gen_class(
|
||||
infos,
|
||||
authuser=REQUEST.AUTHENTICATED_USER,
|
||||
authuser=current_user,
|
||||
version=version,
|
||||
filigranne=infos["filigranne"],
|
||||
server_name=REQUEST.BASE0,
|
||||
server_name=request.url_root,
|
||||
)
|
||||
if format not in bul_generator.supported_formats:
|
||||
# use standard generator
|
||||
@ -301,10 +296,10 @@ def make_formsemestre_bulletinetud(
|
||||
gen_class = bulletin_get_class(bul_class_name)
|
||||
bul_generator = gen_class(
|
||||
infos,
|
||||
authuser=REQUEST.AUTHENTICATED_USER,
|
||||
authuser=current_user,
|
||||
version=version,
|
||||
filigranne=infos["filigranne"],
|
||||
server_name=REQUEST.BASE0,
|
||||
server_name=request.url_root,
|
||||
)
|
||||
|
||||
data = bul_generator.generate(format=format, stand_alone=stand_alone)
|
||||
|
@ -47,27 +47,22 @@ from app.scodoc import sco_etud
|
||||
|
||||
|
||||
def make_json_formsemestre_bulletinetud(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
REQUEST=None,
|
||||
formsemestre_id: int,
|
||||
etudid: int,
|
||||
xml_with_decisions=False,
|
||||
version="long",
|
||||
force_publishing=False, # force publication meme si semestre non publie sur "portail"
|
||||
):
|
||||
) -> str:
|
||||
"""Renvoie bulletin en chaine JSON"""
|
||||
|
||||
d = formsemestre_bulletinetud_published_dict(
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
force_publishing=force_publishing,
|
||||
REQUEST=REQUEST,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
version=version,
|
||||
)
|
||||
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.JSON_MIMETYPE)
|
||||
|
||||
return json.dumps(d, cls=scu.ScoDocJSONEncoder)
|
||||
|
||||
|
||||
@ -79,7 +74,6 @@ def formsemestre_bulletinetud_published_dict(
|
||||
etudid,
|
||||
force_publishing=False,
|
||||
xml_nodate=False,
|
||||
REQUEST=None,
|
||||
xml_with_decisions=False, # inclue les decisions même si non publiées
|
||||
version="long",
|
||||
):
|
||||
|
@ -58,7 +58,7 @@ import traceback
|
||||
|
||||
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
|
||||
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
@ -164,7 +164,7 @@ def process_field(field, cdict, style, suppress_empty_pars=False, format="pdf"):
|
||||
return sco_pdf.makeParas(text, style, suppress_empty=suppress_empty_pars)
|
||||
|
||||
|
||||
def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedevals"):
|
||||
def get_formsemestre_bulletins_pdf(formsemestre_id, version="selectedevals"):
|
||||
"document pdf et filename"
|
||||
from app.scodoc import sco_bulletins
|
||||
|
||||
@ -184,7 +184,6 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedev
|
||||
etudid,
|
||||
format="pdfpart",
|
||||
version=version,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
fragments += frag
|
||||
filigrannes[i] = filigranne
|
||||
@ -192,8 +191,8 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedev
|
||||
i = i + 1
|
||||
#
|
||||
infos = {"DeptName": sco_preferences.get_preference("DeptName", formsemestre_id)}
|
||||
if REQUEST:
|
||||
server_name = REQUEST.BASE0
|
||||
if request:
|
||||
server_name = request.url_root
|
||||
else:
|
||||
server_name = ""
|
||||
try:
|
||||
@ -220,7 +219,7 @@ def get_formsemestre_bulletins_pdf(formsemestre_id, REQUEST, version="selectedev
|
||||
return pdfdoc, filename
|
||||
|
||||
|
||||
def get_etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
|
||||
def get_etud_bulletins_pdf(etudid, version="selectedevals"):
|
||||
"Bulletins pdf de tous les semestres de l'étudiant, et filename"
|
||||
from app.scodoc import sco_bulletins
|
||||
|
||||
@ -235,15 +234,14 @@ def get_etud_bulletins_pdf(etudid, REQUEST, version="selectedevals"):
|
||||
etudid,
|
||||
format="pdfpart",
|
||||
version=version,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
fragments += frag
|
||||
filigrannes[i] = filigranne
|
||||
bookmarks[i] = sem["session_id"] # eg RT-DUT-FI-S1-2015
|
||||
i = i + 1
|
||||
infos = {"DeptName": sco_preferences.get_preference("DeptName")}
|
||||
if REQUEST:
|
||||
server_name = REQUEST.BASE0
|
||||
if request:
|
||||
server_name = request.url_root
|
||||
else:
|
||||
server_name = ""
|
||||
try:
|
||||
|
@ -56,7 +56,7 @@ et sur page "réglages bulletin" (avec formsemestre_id)
|
||||
# import os
|
||||
|
||||
|
||||
# def form_change_bul_sig(side, formsemestre_id=None, REQUEST=None):
|
||||
# def form_change_bul_sig(side, formsemestre_id=None):
|
||||
# """Change pdf signature"""
|
||||
# filename = _get_sig_existing_filename(
|
||||
# side, formsemestre_id=formsemestre_id
|
||||
@ -69,7 +69,7 @@ et sur page "réglages bulletin" (avec formsemestre_id)
|
||||
# raise ValueError("invalid value for 'side' parameter")
|
||||
# signatureloc = get_bul_sig_img()
|
||||
# H = [
|
||||
# self.sco_header(REQUEST, page_title="Changement de signature"),
|
||||
# self.sco_header(page_title="Changement de signature"),
|
||||
# """<h2>Changement de la signature bulletin de %(sidetxt)s</h2>
|
||||
# """
|
||||
# % (sidetxt,),
|
||||
|
@ -69,16 +69,13 @@ def make_xml_formsemestre_bulletinetud(
|
||||
doc=None, # XML document
|
||||
force_publishing=False,
|
||||
xml_nodate=False,
|
||||
REQUEST=None,
|
||||
xml_with_decisions=False, # inclue les decisions même si non publiées
|
||||
version="long",
|
||||
):
|
||||
) -> str:
|
||||
"bulletin au format XML"
|
||||
from app.scodoc import sco_bulletins
|
||||
|
||||
log("xml_bulletin( formsemestre_id=%s, etudid=%s )" % (formsemestre_id, etudid))
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if (not sem["bul_hide_xml"]) or force_publishing:
|
||||
|
@ -157,7 +157,7 @@ class EvaluationCache(ScoDocCache):
|
||||
|
||||
class AbsSemEtudCache(ScoDocCache):
|
||||
"""Cache pour les comptes d'absences d'un étudiant dans un semestre.
|
||||
Ce cache étant indépendant des semestre, le compte peut être faux lorsqu'on
|
||||
Ce cache étant indépendant des semestres, le compte peut être faux lorsqu'on
|
||||
change les dates début/fin d'un semestre.
|
||||
C'est pourquoi il expire après timeout secondes.
|
||||
Le timeout evite aussi d'éliminer explicitement ces éléments cachés lors
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
(coût théorique en heures équivalent TD)
|
||||
"""
|
||||
from flask import request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc import sco_formsemestre
|
||||
@ -45,7 +47,6 @@ def formsemestre_table_estim_cost(
|
||||
n_group_tp=1,
|
||||
coef_tp=1,
|
||||
coef_cours=1.5,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""
|
||||
Rapports estimation coût de formation basé sur le programme pédagogique
|
||||
@ -156,7 +157,6 @@ def formsemestre_estim_cost(
|
||||
coef_tp=1,
|
||||
coef_cours=1.5,
|
||||
format="html",
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Page (formulaire) estimation coûts"""
|
||||
|
||||
@ -171,7 +171,6 @@ def formsemestre_estim_cost(
|
||||
n_group_tp=n_group_tp,
|
||||
coef_tp=coef_tp,
|
||||
coef_cours=coef_cours,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
h = """
|
||||
<form name="f" method="get" action="%s">
|
||||
@ -182,7 +181,7 @@ def formsemestre_estim_cost(
|
||||
<br/>
|
||||
</form>
|
||||
""" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
n_group_td,
|
||||
n_group_tp,
|
||||
@ -190,11 +189,11 @@ def formsemestre_estim_cost(
|
||||
)
|
||||
tab.html_before_table = h
|
||||
tab.base_url = "%s?formsemestre_id=%s&n_group_td=%s&n_group_tp=%s&coef_tp=%s" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
n_group_td,
|
||||
n_group_tp,
|
||||
coef_tp,
|
||||
)
|
||||
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
@ -29,7 +29,7 @@
|
||||
Rapport (table) avec dernier semestre fréquenté et débouché de chaque étudiant
|
||||
"""
|
||||
import http
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -47,10 +47,10 @@ from app.scodoc import sco_etud
|
||||
import sco_version
|
||||
|
||||
|
||||
def report_debouche_date(start_year=None, format="html", REQUEST=None):
|
||||
def report_debouche_date(start_year=None, format="html"):
|
||||
"""Rapport (table) pour les débouchés des étudiants sortis à partir de l'année indiquée."""
|
||||
if not start_year:
|
||||
return report_debouche_ask_date(REQUEST=REQUEST)
|
||||
return report_debouche_ask_date()
|
||||
if format == "xls":
|
||||
keep_numeric = True # pas de conversion des notes en strings
|
||||
else:
|
||||
@ -64,13 +64,12 @@ def report_debouche_date(start_year=None, format="html", REQUEST=None):
|
||||
"Généré par %s le " % sco_version.SCONAME + scu.timedate_human_repr() + ""
|
||||
)
|
||||
tab.caption = "Récapitulatif débouchés à partir du 1/1/%s." % start_year
|
||||
tab.base_url = "%s?start_year=%s" % (REQUEST.URL0, start_year)
|
||||
tab.base_url = "%s?start_year=%s" % (request.base_url, start_year)
|
||||
return tab.make_page(
|
||||
title="""<h2 class="formsemestre">Débouchés étudiants </h2>""",
|
||||
init_qtip=True,
|
||||
javascripts=["js/etud_info.js"],
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=True,
|
||||
)
|
||||
|
||||
@ -194,7 +193,7 @@ def table_debouche_etudids(etudids, keep_numeric=True):
|
||||
return tab
|
||||
|
||||
|
||||
def report_debouche_ask_date(REQUEST=None):
|
||||
def report_debouche_ask_date():
|
||||
"""Formulaire demande date départ"""
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
@ -249,7 +248,7 @@ def itemsuivi_get(cnx, itemsuivi_id, ignore_errors=False):
|
||||
return None
|
||||
|
||||
|
||||
def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
|
||||
def itemsuivi_suppress(itemsuivi_id):
|
||||
"""Suppression d'un item"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
@ -259,9 +258,10 @@ def itemsuivi_suppress(itemsuivi_id, REQUEST=None):
|
||||
_itemsuivi_delete(cnx, itemsuivi_id)
|
||||
logdb(cnx, method="itemsuivi_suppress", etudid=item["etudid"])
|
||||
log("suppressed itemsuivi %s" % (itemsuivi_id,))
|
||||
return ("", 204)
|
||||
|
||||
|
||||
def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=None):
|
||||
def itemsuivi_create(etudid, item_date=None, situation="", format=None):
|
||||
"""Creation d'un item"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
@ -273,11 +273,11 @@ def itemsuivi_create(etudid, item_date=None, situation="", REQUEST=None, format=
|
||||
log("created itemsuivi %s for %s" % (itemsuivi_id, etudid))
|
||||
item = itemsuivi_get(cnx, itemsuivi_id)
|
||||
if format == "json":
|
||||
return scu.sendJSON(REQUEST, item)
|
||||
return scu.sendJSON(item)
|
||||
return item
|
||||
|
||||
|
||||
def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
|
||||
def itemsuivi_set_date(itemsuivi_id, item_date):
|
||||
"""set item date
|
||||
item_date is a string dd/mm/yyyy
|
||||
"""
|
||||
@ -288,9 +288,10 @@ def itemsuivi_set_date(itemsuivi_id, item_date, REQUEST=None):
|
||||
item = itemsuivi_get(cnx, itemsuivi_id)
|
||||
item["item_date"] = item_date
|
||||
_itemsuivi_edit(cnx, item)
|
||||
return ("", 204)
|
||||
|
||||
|
||||
def itemsuivi_set_situation(object, value, REQUEST=None):
|
||||
def itemsuivi_set_situation(object, value):
|
||||
"""set situation"""
|
||||
if not sco_permissions_check.can_edit_suivi():
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
@ -304,14 +305,14 @@ def itemsuivi_set_situation(object, value, REQUEST=None):
|
||||
return situation or scu.IT_SITUATION_MISSING_STR
|
||||
|
||||
|
||||
def itemsuivi_list_etud(etudid, format=None, REQUEST=None):
|
||||
def itemsuivi_list_etud(etudid, format=None):
|
||||
"""Liste des items pour cet étudiant, avec tags"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
items = _itemsuivi_list(cnx, {"etudid": etudid})
|
||||
for it in items:
|
||||
it["tags"] = ", ".join(itemsuivi_tag_list(it["itemsuivi_id"]))
|
||||
if format == "json":
|
||||
return scu.sendJSON(REQUEST, items)
|
||||
return scu.sendJSON(items)
|
||||
return items
|
||||
|
||||
|
||||
@ -328,7 +329,7 @@ def itemsuivi_tag_list(itemsuivi_id):
|
||||
return [x["title"] for x in r]
|
||||
|
||||
|
||||
def itemsuivi_tag_search(term, REQUEST=None):
|
||||
def itemsuivi_tag_search(term):
|
||||
"""List all used tag names (for auto-completion)"""
|
||||
# restrict charset to avoid injections
|
||||
if not scu.ALPHANUM_EXP.match(term):
|
||||
@ -343,10 +344,10 @@ def itemsuivi_tag_search(term, REQUEST=None):
|
||||
)
|
||||
data = [x["title"] for x in r]
|
||||
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
return scu.sendJSON(data)
|
||||
|
||||
|
||||
def itemsuivi_tag_set(itemsuivi_id="", taglist=[], REQUEST=None):
|
||||
def itemsuivi_tag_set(itemsuivi_id="", taglist=None):
|
||||
"""taglist may either be:
|
||||
a string with tag names separated by commas ("un;deux")
|
||||
or a list of strings (["un", "deux"])
|
||||
|
@ -28,7 +28,7 @@
|
||||
"""Page accueil département (liste des semestres, etc)
|
||||
"""
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app
|
||||
@ -46,7 +46,7 @@ from app.scodoc import sco_up_to_date
|
||||
from app.scodoc import sco_users
|
||||
|
||||
|
||||
def index_html(REQUEST=None, showcodes=0, showsemtable=0):
|
||||
def index_html(showcodes=0, showsemtable=0):
|
||||
"Page accueil département (liste des semestres)"
|
||||
showsemtable = int(showsemtable)
|
||||
H = []
|
||||
@ -131,7 +131,7 @@ def index_html(REQUEST=None, showcodes=0, showsemtable=0):
|
||||
if not showsemtable:
|
||||
H.append(
|
||||
'<hr/><p><a href="%s?showsemtable=1">Voir tous les semestres</a></p>'
|
||||
% REQUEST.URL0
|
||||
% request.base_url
|
||||
)
|
||||
|
||||
H.append(
|
||||
@ -242,7 +242,7 @@ def _sem_table_gt(sems, showcodes=False):
|
||||
rows=sems,
|
||||
html_class="table_leftalign semlist",
|
||||
html_sortable=True,
|
||||
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
|
||||
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
|
||||
# caption='Maquettes enregistrées',
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
@ -51,6 +51,7 @@ import fcntl
|
||||
import subprocess
|
||||
import requests
|
||||
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -64,7 +65,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
SCO_DUMP_LOCK = "/tmp/scodump.lock"
|
||||
|
||||
|
||||
def sco_dump_and_send_db(REQUEST=None):
|
||||
def sco_dump_and_send_db():
|
||||
"""Dump base de données et l'envoie anonymisée pour debug"""
|
||||
H = [html_sco_header.sco_header(page_title="Assistance technique")]
|
||||
# get currect (dept) DB name:
|
||||
@ -93,7 +94,7 @@ def sco_dump_and_send_db(REQUEST=None):
|
||||
_anonymize_db(ano_db_name)
|
||||
|
||||
# Send
|
||||
r = _send_db(REQUEST, ano_db_name)
|
||||
r = _send_db(ano_db_name)
|
||||
if (
|
||||
r.status_code
|
||||
== requests.codes.INSUFFICIENT_STORAGE # pylint: disable=no-member
|
||||
@ -171,29 +172,27 @@ def _get_scodoc_serial():
|
||||
return 0
|
||||
|
||||
|
||||
def _send_db(REQUEST, ano_db_name):
|
||||
def _send_db(ano_db_name):
|
||||
"""Dump this (anonymized) database and send it to tech support"""
|
||||
log("dumping anonymized database {}".format(ano_db_name))
|
||||
log(f"dumping anonymized database {ano_db_name}")
|
||||
try:
|
||||
data = subprocess.check_output("pg_dump {} | gzip".format(ano_db_name), shell=1)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log("sco_dump_and_send_db: exception in anonymisation: {}".format(e))
|
||||
raise ScoValueError(
|
||||
"erreur lors de l'anonymisation de la base {}".format(ano_db_name)
|
||||
dump = subprocess.check_output(
|
||||
f"pg_dump --format=custom {ano_db_name}", shell=1
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
log(f"sco_dump_and_send_db: exception in anonymisation: {e}")
|
||||
raise ScoValueError(f"erreur lors de l'anonymisation de la base {ano_db_name}")
|
||||
|
||||
log("uploading anonymized dump...")
|
||||
files = {"file": (ano_db_name + ".gz", data)}
|
||||
files = {"file": (ano_db_name + ".dump", dump)}
|
||||
r = requests.post(
|
||||
scu.SCO_DUMP_UP_URL,
|
||||
files=files,
|
||||
data={
|
||||
"dept_name": sco_preferences.get_preference("DeptName"),
|
||||
"serial": _get_scodoc_serial(),
|
||||
"sco_user": str(REQUEST.AUTHENTICATED_USER),
|
||||
"sent_by": sco_users.user_info(str(REQUEST.AUTHENTICATED_USER))[
|
||||
"nomcomplet"
|
||||
],
|
||||
"sco_user": str(current_user),
|
||||
"sent_by": sco_users.user_info(str(current_user))["nomcomplet"],
|
||||
"sco_version": sco_version.SCOVERSION,
|
||||
"sco_fullversion": scu.get_scodoc_version(),
|
||||
},
|
||||
|
@ -29,7 +29,7 @@
|
||||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -47,7 +47,7 @@ from app.scodoc import sco_formsemestre
|
||||
from app.scodoc import sco_news
|
||||
|
||||
|
||||
def formation_delete(formation_id=None, dialog_confirmed=False, REQUEST=None):
|
||||
def formation_delete(formation_id=None, dialog_confirmed=False):
|
||||
"""Delete a formation"""
|
||||
F = sco_formations.formation_list(args={"formation_id": formation_id})
|
||||
if not F:
|
||||
@ -119,12 +119,12 @@ def do_formation_delete(oid):
|
||||
)
|
||||
|
||||
|
||||
def formation_create(REQUEST=None):
|
||||
def formation_create():
|
||||
"""Creation d'une formation"""
|
||||
return formation_edit(create=True, REQUEST=REQUEST)
|
||||
return formation_edit(create=True)
|
||||
|
||||
|
||||
def formation_edit(formation_id=None, create=False, REQUEST=None):
|
||||
def formation_edit(formation_id=None, create=False):
|
||||
"""Edit or create a formation"""
|
||||
if create:
|
||||
H = [
|
||||
@ -159,8 +159,8 @@ def formation_edit(formation_id=None, create=False, REQUEST=None):
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("formation_id", {"default": formation_id, "input_type": "hidden"}),
|
||||
(
|
||||
@ -311,7 +311,7 @@ def invalidate_sems_in_formation(formation_id):
|
||||
) # > formation modif.
|
||||
|
||||
|
||||
def module_move(module_id, after=0, REQUEST=None, redirect=1):
|
||||
def module_move(module_id, after=0, redirect=1):
|
||||
"""Move before/after previous one (decrement/increment numero)"""
|
||||
module = sco_edit_module.do_module_list({"module_id": module_id})[0]
|
||||
redirect = int(redirect)
|
||||
|
@ -29,7 +29,7 @@
|
||||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -92,7 +92,7 @@ def do_matiere_create(args):
|
||||
return r
|
||||
|
||||
|
||||
def matiere_create(ue_id=None, REQUEST=None):
|
||||
def matiere_create(ue_id=None):
|
||||
"""Creation d'une matiere"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
@ -116,8 +116,8 @@ associé.
|
||||
</p>""",
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("ue_id", {"input_type": "hidden", "default": ue_id}),
|
||||
("titre", {"size": 30, "explanation": "nom de la matière."}),
|
||||
@ -189,7 +189,7 @@ def do_matiere_delete(oid):
|
||||
)
|
||||
|
||||
|
||||
def matiere_delete(matiere_id=None, REQUEST=None):
|
||||
def matiere_delete(matiere_id=None):
|
||||
"""Delete an UE"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
@ -202,8 +202,8 @@ def matiere_delete(matiere_id=None, REQUEST=None):
|
||||
]
|
||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(UE["formation_id"])
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(("matiere_id", {"input_type": "hidden"}),),
|
||||
initvalues=M,
|
||||
submitlabel="Confirmer la suppression",
|
||||
@ -218,7 +218,7 @@ def matiere_delete(matiere_id=None, REQUEST=None):
|
||||
return flask.redirect(dest_url)
|
||||
|
||||
|
||||
def matiere_edit(matiere_id=None, REQUEST=None):
|
||||
def matiere_edit(matiere_id=None):
|
||||
"""Edit matiere"""
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_edit_ue
|
||||
@ -256,8 +256,8 @@ des notes.</em>
|
||||
associé.
|
||||
</p>"""
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("matiere_id", {"input_type": "hidden"}),
|
||||
(
|
||||
@ -323,4 +323,4 @@ def matiere_is_locked(matiere_id):
|
||||
""",
|
||||
{"matiere_id": matiere_id},
|
||||
)
|
||||
return len(r) > 0
|
||||
return len(r) > 0
|
||||
|
@ -29,7 +29,8 @@
|
||||
(portage from DTML)
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -118,7 +119,7 @@ def do_module_create(args) -> int:
|
||||
return r
|
||||
|
||||
|
||||
def module_create(matiere_id=None, REQUEST=None):
|
||||
def module_create(matiere_id=None):
|
||||
"""Creation d'un module"""
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_edit_ue
|
||||
@ -143,8 +144,8 @@ def module_create(matiere_id=None, REQUEST=None):
|
||||
else:
|
||||
default_num = 10
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
(
|
||||
"code",
|
||||
@ -258,12 +259,13 @@ def do_module_delete(oid):
|
||||
# S'il y a des moduleimpls, on ne peut pas detruire le module !
|
||||
mods = sco_moduleimpl.do_moduleimpl_list(module_id=oid)
|
||||
if mods:
|
||||
err_page = scu.confirm_dialog(
|
||||
message="""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>""",
|
||||
helpmsg="""Il faut d'abord supprimer le semestre. Mais il est peut être préférable de laisser ce programme intact et d'en créer une nouvelle version pour la modifier.""",
|
||||
dest_url="ue_list",
|
||||
parameters={"formation_id": mod["formation_id"]},
|
||||
)
|
||||
err_page = f"""<h3>Destruction du module impossible car il est utilisé dans des semestres existants !</h3>
|
||||
<p class="help">Il faut d'abord supprimer le semestre. Mais il est peut être préférable de
|
||||
laisser ce programme intact et d'en créer une nouvelle version pour la modifier.
|
||||
</p>
|
||||
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept,
|
||||
formation_id=mod["formation_id"])}">reprendre</a>
|
||||
"""
|
||||
raise ScoGenError(err_page)
|
||||
# delete
|
||||
cnx = ndb.GetDBConnexion()
|
||||
@ -279,7 +281,7 @@ def do_module_delete(oid):
|
||||
)
|
||||
|
||||
|
||||
def module_delete(module_id=None, REQUEST=None):
|
||||
def module_delete(module_id=None):
|
||||
"""Delete a module"""
|
||||
if not module_id:
|
||||
raise ScoValueError("invalid module !")
|
||||
@ -294,8 +296,8 @@ def module_delete(module_id=None, REQUEST=None):
|
||||
|
||||
dest_url = scu.NotesURL() + "/ue_list?formation_id=" + str(Mod["formation_id"])
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(("module_id", {"input_type": "hidden"}),),
|
||||
initvalues=Mod,
|
||||
submitlabel="Confirmer la suppression",
|
||||
@ -337,7 +339,7 @@ def check_module_code_unicity(code, field, formation_id, module_id=None):
|
||||
return len(Mods) == 0
|
||||
|
||||
|
||||
def module_edit(module_id=None, REQUEST=None):
|
||||
def module_edit(module_id=None):
|
||||
"""Edit a module"""
|
||||
from app.scodoc import sco_formations
|
||||
from app.scodoc import sco_tag_module
|
||||
@ -388,8 +390,8 @@ def module_edit(module_id=None, REQUEST=None):
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
(
|
||||
"code",
|
||||
@ -513,7 +515,7 @@ def module_edit(module_id=None, REQUEST=None):
|
||||
|
||||
|
||||
# Edition en ligne du code Apogee
|
||||
def edit_module_set_code_apogee(id=None, value=None, REQUEST=None):
|
||||
def edit_module_set_code_apogee(id=None, value=None):
|
||||
"Set UE code apogee"
|
||||
module_id = id
|
||||
value = value.strip("-_ \t")
|
||||
@ -529,7 +531,7 @@ def edit_module_set_code_apogee(id=None, value=None, REQUEST=None):
|
||||
return value
|
||||
|
||||
|
||||
def module_list(formation_id, REQUEST=None):
|
||||
def module_list(formation_id):
|
||||
"""Liste des modules de la formation
|
||||
(XXX inutile ou a revoir)
|
||||
"""
|
||||
@ -544,7 +546,7 @@ def module_list(formation_id, REQUEST=None):
|
||||
% F,
|
||||
'<ul class="notes_module_list">',
|
||||
]
|
||||
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
|
||||
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
||||
|
||||
for Mod in do_module_list(args={"formation_id": formation_id}):
|
||||
H.append('<li class="notes_module_list">%s' % Mod)
|
||||
@ -582,7 +584,7 @@ def module_count_moduleimpls(module_id):
|
||||
return len(mods)
|
||||
|
||||
|
||||
def formation_add_malus_modules(formation_id, titre=None, REQUEST=None):
|
||||
def formation_add_malus_modules(formation_id, titre=None, redirect=True):
|
||||
"""Création d'un module de "malus" dans chaque UE d'une formation"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
@ -598,13 +600,13 @@ def formation_add_malus_modules(formation_id, titre=None, REQUEST=None):
|
||||
]
|
||||
)
|
||||
if nb_mod_malus == 0:
|
||||
ue_add_malus_module(ue["ue_id"], titre=titre, REQUEST=REQUEST)
|
||||
ue_add_malus_module(ue["ue_id"], titre=titre)
|
||||
|
||||
if REQUEST:
|
||||
if redirect:
|
||||
return flask.redirect("ue_list?formation_id=" + str(formation_id))
|
||||
|
||||
|
||||
def ue_add_malus_module(ue_id, titre=None, code=None, REQUEST=None):
|
||||
def ue_add_malus_module(ue_id, titre=None, code=None):
|
||||
"""Add a malus module in this ue"""
|
||||
from app.scodoc import sco_edit_ue
|
||||
|
||||
|
@ -29,9 +29,10 @@
|
||||
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.models.formations import NotesUE
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
@ -185,12 +186,12 @@ def do_ue_delete(ue_id, delete_validations=False, force=False):
|
||||
return None
|
||||
|
||||
|
||||
def ue_create(formation_id=None, REQUEST=None):
|
||||
def ue_create(formation_id=None):
|
||||
"""Creation d'une UE"""
|
||||
return ue_edit(create=True, formation_id=formation_id, REQUEST=REQUEST)
|
||||
return ue_edit(create=True, formation_id=formation_id)
|
||||
|
||||
|
||||
def ue_edit(ue_id=None, create=False, formation_id=None, REQUEST=None):
|
||||
def ue_edit(ue_id=None, create=False, formation_id=None):
|
||||
"""Modification ou creation d'une UE"""
|
||||
from app.scodoc import sco_formations
|
||||
|
||||
@ -326,7 +327,11 @@ def ue_edit(ue_id=None, create=False, formation_id=None, REQUEST=None):
|
||||
)
|
||||
)
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0, REQUEST.form, fw, initvalues=initvalues, submitlabel=submitlabel
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
fw,
|
||||
initvalues=initvalues,
|
||||
submitlabel=submitlabel,
|
||||
)
|
||||
if tf[0] == 0:
|
||||
X = """<div id="ue_list_code"></div>
|
||||
@ -846,25 +851,25 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
|
||||
ue_code = ue["ue_code"]
|
||||
F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0]
|
||||
formation_code = F["formation_code"]
|
||||
|
||||
ue_list_all = do_ue_list(args={"ue_code": ue_code})
|
||||
if ue_id:
|
||||
# retire les UE d'autres formations:
|
||||
# log('checking ucode %s formation %s' % (ue_code, formation_code))
|
||||
ue_list = []
|
||||
for ue in ue_list_all:
|
||||
F = sco_formations.formation_list(
|
||||
args={"formation_id": ue["formation_id"]}
|
||||
)[0]
|
||||
if formation_code == F["formation_code"]:
|
||||
ue_list.append(ue)
|
||||
# UE du même code, code formation et departement:
|
||||
q_ues = (
|
||||
NotesUE.query.filter_by(ue_code=ue_code)
|
||||
.join(NotesUE.formation, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id, formation_code=formation_code)
|
||||
)
|
||||
else:
|
||||
ue_list = ue_list_all
|
||||
# Toutes les UE du departement avec ce code:
|
||||
q_ues = (
|
||||
NotesUE.query.filter_by(ue_code=ue_code)
|
||||
.join(NotesUE.formation, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id)
|
||||
)
|
||||
|
||||
if hide_ue_id: # enlève l'ue de depart
|
||||
ue_list = [ue for ue in ue_list if ue["ue_id"] != hide_ue_id]
|
||||
q_ues = q_ues.filter(NotesUE.id != hide_ue_id)
|
||||
|
||||
if not ue_list:
|
||||
ues = q_ues.all()
|
||||
if not ues:
|
||||
if ue_id:
|
||||
return """<span class="ue_share">Seule UE avec code %s</span>""" % ue_code
|
||||
else:
|
||||
@ -875,18 +880,13 @@ def ue_sharing_code(ue_code=None, ue_id=None, hide_ue_id=None):
|
||||
else:
|
||||
H.append('<span class="ue_share">UE avec le code %s:</span>' % ue_code)
|
||||
H.append("<ul>")
|
||||
for ue in ue_list:
|
||||
F = sco_formations.formation_list(args={"formation_id": ue["formation_id"]})[0]
|
||||
for ue in ues:
|
||||
H.append(
|
||||
'<li>%s (%s) dans <a class="stdlink" href="ue_list?formation_id=%s">%s (%s)</a>, version %s</li>'
|
||||
% (
|
||||
ue["acronyme"],
|
||||
ue["titre"],
|
||||
F["formation_id"],
|
||||
F["acronyme"],
|
||||
F["titre"],
|
||||
F["version"],
|
||||
)
|
||||
f"""<li>{ue.acronyme} ({ue.titre}) dans <a class="stdlink"
|
||||
href="{url_for("notes.ue_list", scodoc_dept=g.scodoc_dept, formation_id=ue.formation.id)}"
|
||||
>{ue.formation.acronyme} ({ue.formation.titre})</a>, version {ue.formation.version}
|
||||
</li>
|
||||
"""
|
||||
)
|
||||
H.append("</ul>")
|
||||
return "\n".join(H)
|
||||
@ -956,7 +956,7 @@ def ue_is_locked(ue_id):
|
||||
|
||||
|
||||
# ---- Table recap formation
|
||||
def formation_table_recap(formation_id, format="html", REQUEST=None):
|
||||
def formation_table_recap(formation_id, format="html"):
|
||||
"""Table recapitulant formation."""
|
||||
from app.scodoc import sco_formations
|
||||
|
||||
@ -1033,13 +1033,13 @@ def formation_table_recap(formation_id, format="html", REQUEST=None):
|
||||
caption=title,
|
||||
html_caption=title,
|
||||
html_class="table_leftalign",
|
||||
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
|
||||
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
|
||||
page_title=title,
|
||||
html_title="<h2>" + title + "</h2>",
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def ue_list_semestre_ids(ue):
|
||||
|
@ -123,7 +123,7 @@ def get_edt_transcodage_groups(formsemestre_id):
|
||||
return edt2sco, sco2edt, msg
|
||||
|
||||
|
||||
def group_edt_json(group_id, start="", end="", REQUEST=None): # actuellement inutilisé
|
||||
def group_edt_json(group_id, start="", end=""): # actuellement inutilisé
|
||||
"""EDT complet du semestre, au format JSON
|
||||
TODO: indiquer un groupe
|
||||
TODO: utiliser start et end (2 dates au format ISO YYYY-MM-DD)
|
||||
@ -149,7 +149,7 @@ def group_edt_json(group_id, start="", end="", REQUEST=None): # actuellement in
|
||||
}
|
||||
J.append(d)
|
||||
|
||||
return scu.sendJSON(REQUEST, J)
|
||||
return scu.sendJSON(J)
|
||||
|
||||
|
||||
"""XXX
|
||||
@ -159,9 +159,7 @@ for e in events:
|
||||
"""
|
||||
|
||||
|
||||
def experimental_calendar(
|
||||
group_id=None, formsemestre_id=None, REQUEST=None
|
||||
): # inutilisé
|
||||
def experimental_calendar(group_id=None, formsemestre_id=None): # inutilisé
|
||||
"""experimental page"""
|
||||
return "\n".join(
|
||||
[
|
||||
|
@ -32,11 +32,11 @@
|
||||
Voir sco_apogee_csv.py pour la structure du fichier Apogée.
|
||||
|
||||
Stockage: utilise sco_archive.py
|
||||
=> /opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR.csv
|
||||
=> /opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR.csv
|
||||
pour une maquette de l'année scolaire 2016, semestre 1, etape V3ASR
|
||||
|
||||
ou bien (à partir de ScoDoc 1678) :
|
||||
/opt/scodoc/var/scodoc/archives/apo_csv/RT/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
|
||||
/opt/scodoc/var/scodoc/archives/apo_csv/<dept_id>/2016-1/2016-07-03-16-12-19/V3ASR!111.csv
|
||||
pour une maquette de l'étape V3ASR version VDI 111.
|
||||
|
||||
La version VDI sera ignorée sauf si elle est indiquée dans l'étape du semestre.
|
||||
|
@ -32,7 +32,7 @@ import io
|
||||
from zipfile import ZipFile
|
||||
|
||||
import flask
|
||||
from flask import url_for, g, send_file
|
||||
from flask import url_for, g, send_file, request
|
||||
|
||||
# from werkzeug.utils import send_file
|
||||
|
||||
@ -62,7 +62,6 @@ def apo_semset_maq_status(
|
||||
block_export_res_ues=False,
|
||||
block_export_res_modules=False,
|
||||
block_export_res_sdj=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Page statut / tableau de bord"""
|
||||
if not semset_id:
|
||||
@ -83,7 +82,7 @@ def apo_semset_maq_status(
|
||||
|
||||
prefs = sco_preferences.SemPreferences()
|
||||
|
||||
tab_archives = table_apo_csv_list(semset, REQUEST=REQUEST)
|
||||
tab_archives = table_apo_csv_list(semset)
|
||||
|
||||
(
|
||||
ok_for_export,
|
||||
@ -250,7 +249,7 @@ def apo_semset_maq_status(
|
||||
"""<form name="f" method="get" action="%s">
|
||||
<input type="hidden" name="semset_id" value="%s"></input>
|
||||
<div><input type="checkbox" name="allow_missing_apo" value="1" onchange="document.f.submit()" """
|
||||
% (REQUEST.URL0, semset_id)
|
||||
% (request.base_url, semset_id)
|
||||
)
|
||||
if allow_missing_apo:
|
||||
H.append("checked")
|
||||
@ -430,7 +429,7 @@ def apo_semset_maq_status(
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def table_apo_csv_list(semset, REQUEST=None):
|
||||
def table_apo_csv_list(semset):
|
||||
"""Table des archives (triée par date d'archivage)"""
|
||||
annee_scolaire = semset["annee_scolaire"]
|
||||
sem_id = semset["sem_id"]
|
||||
@ -476,7 +475,7 @@ def table_apo_csv_list(semset, REQUEST=None):
|
||||
rows=T,
|
||||
html_class="table_leftalign apo_maq_list",
|
||||
html_sortable=True,
|
||||
# base_url = '%s?formsemestre_id=%s' % (REQUEST.URL0, formsemestre_id),
|
||||
# base_url = '%s?formsemestre_id=%s' % (request.base_url, formsemestre_id),
|
||||
# caption='Maquettes enregistrées',
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
@ -484,7 +483,7 @@ def table_apo_csv_list(semset, REQUEST=None):
|
||||
return tab
|
||||
|
||||
|
||||
def view_apo_etuds(semset_id, title="", nip_list="", format="html", REQUEST=None):
|
||||
def view_apo_etuds(semset_id, title="", nip_list="", format="html"):
|
||||
"""Table des étudiants Apogée par nips
|
||||
nip_list est une chaine, codes nip séparés par des ,
|
||||
"""
|
||||
@ -517,11 +516,10 @@ def view_apo_etuds(semset_id, title="", nip_list="", format="html", REQUEST=None
|
||||
etuds=list(etuds.values()),
|
||||
keys=("nip", "etape_apo", "nom", "prenom", "inscriptions_scodoc"),
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
|
||||
def view_scodoc_etuds(semset_id, title="", nip_list="", format="html", REQUEST=None):
|
||||
def view_scodoc_etuds(semset_id, title="", nip_list="", format="html"):
|
||||
"""Table des étudiants ScoDoc par nips ou etudids"""
|
||||
if not isinstance(nip_list, str):
|
||||
nip_list = str(nip_list)
|
||||
@ -541,13 +539,10 @@ def view_scodoc_etuds(semset_id, title="", nip_list="", format="html", REQUEST=N
|
||||
etuds=etuds,
|
||||
keys=("code_nip", "nom", "prenom"),
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
|
||||
def _view_etuds_page(
|
||||
semset_id, title="", etuds=[], keys=(), format="html", REQUEST=None
|
||||
):
|
||||
def _view_etuds_page(semset_id, title="", etuds=[], keys=(), format="html"):
|
||||
# Tri les étudiants par nom:
|
||||
if etuds:
|
||||
etuds.sort(key=lambda x: (x["nom"], x["prenom"]))
|
||||
@ -578,7 +573,7 @@ def _view_etuds_page(
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H.append(tab.html())
|
||||
|
||||
@ -590,9 +585,7 @@ def _view_etuds_page(
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def view_apo_csv_store(
|
||||
semset_id="", csvfile=None, data="", autodetect=False, REQUEST=None
|
||||
):
|
||||
def view_apo_csv_store(semset_id="", csvfile=None, data="", autodetect=False):
|
||||
"""Store CSV data
|
||||
Le semset identifie l'annee scolaire et le semestre
|
||||
Si csvfile, lit depuis FILE, sinon utilise data
|
||||
@ -627,7 +620,7 @@ def view_apo_csv_store(
|
||||
return flask.redirect("apo_semset_maq_status?semset_id=" + semset_id)
|
||||
|
||||
|
||||
def view_apo_csv_download_and_store(etape_apo="", semset_id="", REQUEST=None):
|
||||
def view_apo_csv_download_and_store(etape_apo="", semset_id=""):
|
||||
"""Download maquette and store it"""
|
||||
if not semset_id:
|
||||
raise ValueError("invalid null semset_id")
|
||||
@ -639,12 +632,10 @@ def view_apo_csv_download_and_store(etape_apo="", semset_id="", REQUEST=None):
|
||||
# here, data is utf8
|
||||
# but we store and generate latin1 files, to ease further import in Apogée
|
||||
data = data.decode(APO_PORTAL_ENCODING).encode(APO_INPUT_ENCODING) # XXX #py3
|
||||
return view_apo_csv_store(semset_id, data=data, autodetect=False, REQUEST=REQUEST)
|
||||
return view_apo_csv_store(semset_id, data=data, autodetect=False)
|
||||
|
||||
|
||||
def view_apo_csv_delete(
|
||||
etape_apo="", semset_id="", dialog_confirmed=False, REQUEST=None
|
||||
):
|
||||
def view_apo_csv_delete(etape_apo="", semset_id="", dialog_confirmed=False):
|
||||
"""Delete CSV file"""
|
||||
if not semset_id:
|
||||
raise ValueError("invalid null semset_id")
|
||||
@ -667,7 +658,7 @@ def view_apo_csv_delete(
|
||||
return flask.redirect(dest_url + "&head_message=Archive%20supprimée")
|
||||
|
||||
|
||||
def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
||||
def view_apo_csv(etape_apo="", semset_id="", format="html"):
|
||||
"""Visualise une maquette stockée
|
||||
Si format="raw", renvoie le fichier maquette tel quel
|
||||
"""
|
||||
@ -678,7 +669,8 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
||||
sem_id = semset["sem_id"]
|
||||
csv_data = sco_etape_apogee.apo_csv_get(etape_apo, annee_scolaire, sem_id)
|
||||
if format == "raw":
|
||||
return scu.sendCSVFile(REQUEST, csv_data, etape_apo + ".txt")
|
||||
scu.send_file(csv_data, etape_apo, suffix=".txt", mime=scu.CSV_MIMETYPE)
|
||||
|
||||
apo_data = sco_apogee_csv.ApoData(csv_data, periode=semset["sem_id"])
|
||||
|
||||
(
|
||||
@ -746,14 +738,15 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
||||
rows=etuds,
|
||||
html_sortable=True,
|
||||
html_class="table_leftalign apo_maq_table",
|
||||
base_url="%s?etape_apo=%s&semset_id=%s" % (REQUEST.URL0, etape_apo, semset_id),
|
||||
base_url="%s?etape_apo=%s&semset_id=%s"
|
||||
% (request.base_url, etape_apo, semset_id),
|
||||
filename="students_" + etape_apo,
|
||||
caption="Etudiants Apogée en " + etape_apo,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
H += [
|
||||
tab.html(),
|
||||
@ -768,7 +761,7 @@ def view_apo_csv(etape_apo="", semset_id="", format="html", REQUEST=None):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
# called from Web
|
||||
# called from Web (GET)
|
||||
def apo_csv_export_results(
|
||||
semset_id,
|
||||
block_export_res_etape=False,
|
||||
|
@ -31,27 +31,21 @@
|
||||
# Ancien module "scolars"
|
||||
import os
|
||||
import time
|
||||
|
||||
from flask import url_for, g, request
|
||||
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
from email.mime.base import MIMEBase
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g, request
|
||||
from flask_mail import Message
|
||||
|
||||
from app import email
|
||||
from app import log
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc.sco_utils import SCO_ENCODING
|
||||
import app.scodoc.notesdb as ndb
|
||||
from app.scodoc.sco_exceptions import ScoGenError, ScoValueError
|
||||
|
||||
from app import log
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
from app.scodoc import safehtml
|
||||
from app.scodoc import sco_preferences
|
||||
from app.scodoc.scolog import logdb
|
||||
from flask_mail import Message
|
||||
from app import mail
|
||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||
|
||||
MONTH_NAMES_ABBREV = [
|
||||
"Jan ",
|
||||
@ -256,7 +250,6 @@ _identiteEditor = ndb.EditableTable(
|
||||
"photo_filename",
|
||||
"code_ine",
|
||||
"code_nip",
|
||||
"scodoc7_id",
|
||||
),
|
||||
filter_dept=True,
|
||||
sortkey="nom",
|
||||
@ -321,9 +314,7 @@ def check_nom_prenom(cnx, nom="", prenom="", etudid=None):
|
||||
return True, len(res)
|
||||
|
||||
|
||||
def _check_duplicate_code(
|
||||
cnx, args, code_name, disable_notify=False, edit=True, REQUEST=None
|
||||
):
|
||||
def _check_duplicate_code(cnx, args, code_name, disable_notify=False, edit=True):
|
||||
etudid = args.get("etudid", None)
|
||||
if args.get(code_name, None):
|
||||
etuds = identite_list(cnx, {code_name: str(args[code_name])})
|
||||
@ -345,31 +336,33 @@ def _check_duplicate_code(
|
||||
)
|
||||
if etudid:
|
||||
OK = "retour à la fiche étudiant"
|
||||
dest_url = "ficheEtud"
|
||||
dest_endpoint = "scolar.ficheEtud"
|
||||
parameters = {"etudid": etudid}
|
||||
else:
|
||||
if "tf_submitted" in args:
|
||||
del args["tf_submitted"]
|
||||
OK = "Continuer"
|
||||
dest_url = "etudident_create_form"
|
||||
dest_endpoint = "scolar.etudident_create_form"
|
||||
parameters = args
|
||||
else:
|
||||
OK = "Annuler"
|
||||
dest_url = ""
|
||||
dest_endpoint = "notes.index_html"
|
||||
parameters = {}
|
||||
if not disable_notify:
|
||||
err_page = scu.confirm_dialog(
|
||||
message="""<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name,
|
||||
helpmsg="""Le %s %s est déjà utilisé: un seul étudiant peut avoir ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.<p><ul><li>"""
|
||||
% (code_name, args[code_name])
|
||||
+ "</li><li>".join(listh)
|
||||
+ "</li></ul><p>",
|
||||
OK=OK,
|
||||
dest_url=dest_url,
|
||||
parameters=parameters,
|
||||
)
|
||||
err_page = f"""<h3><h3>Code étudiant ({code_name}) dupliqué !</h3>
|
||||
<p class="help">Le {code_name} {args[code_name]} est déjà utilisé: un seul étudiant peut avoir
|
||||
ce code. Vérifier votre valeur ou supprimer l'autre étudiant avec cette valeur.
|
||||
</p>
|
||||
<ul><li>
|
||||
{ '</li><li>'.join(listh) }
|
||||
</li></ul>
|
||||
<p>
|
||||
<a href="{ url_for(dest_endpoint, scodoc_dept=g.scodoc_dept, **parameters) }
|
||||
">{OK}</a>
|
||||
</p>
|
||||
"""
|
||||
else:
|
||||
err_page = """<h3>Code étudiant (%s) dupliqué !</h3>""" % code_name
|
||||
err_page = f"""<h3>Code étudiant ({code_name}) dupliqué !</h3>"""
|
||||
log("*** error: code %s duplique: %s" % (code_name, args[code_name]))
|
||||
raise ScoGenError(err_page)
|
||||
|
||||
@ -379,15 +372,15 @@ def _check_civilite(args):
|
||||
args["civilite"] = input_civilite(civilite) # TODO: A faire valider
|
||||
|
||||
|
||||
def identite_edit(cnx, args, disable_notify=False, REQUEST=None):
|
||||
def identite_edit(cnx, args, disable_notify=False):
|
||||
"""Modifie l'identite d'un étudiant.
|
||||
Si pref notification et difference, envoie message notification, sauf si disable_notify
|
||||
"""
|
||||
_check_duplicate_code(
|
||||
cnx, args, "code_nip", disable_notify=disable_notify, edit=True, REQUEST=REQUEST
|
||||
cnx, args, "code_nip", disable_notify=disable_notify, edit=True
|
||||
)
|
||||
_check_duplicate_code(
|
||||
cnx, args, "code_ine", disable_notify=disable_notify, edit=True, REQUEST=REQUEST
|
||||
cnx, args, "code_ine", disable_notify=disable_notify, edit=True
|
||||
)
|
||||
notify_to = None
|
||||
if not disable_notify:
|
||||
@ -415,10 +408,10 @@ def identite_edit(cnx, args, disable_notify=False, REQUEST=None):
|
||||
)
|
||||
|
||||
|
||||
def identite_create(cnx, args, REQUEST=None):
|
||||
def identite_create(cnx, args):
|
||||
"check unique etudid, then create"
|
||||
_check_duplicate_code(cnx, args, "code_nip", edit=False, REQUEST=REQUEST)
|
||||
_check_duplicate_code(cnx, args, "code_ine", edit=False, REQUEST=REQUEST)
|
||||
_check_duplicate_code(cnx, args, "code_nip", edit=False)
|
||||
_check_duplicate_code(cnx, args, "code_ine", edit=False)
|
||||
_check_civilite(args)
|
||||
|
||||
if "etudid" in args:
|
||||
@ -456,7 +449,7 @@ def notify_etud_change(email_addr, etud, before, after, subject):
|
||||
log("notify_etud_change: sending notification to %s" % email_addr)
|
||||
log("notify_etud_change: subject: %s" % subject)
|
||||
log(txt)
|
||||
mail.send_email(
|
||||
email.send_email(
|
||||
subject, sco_preferences.get_preference("email_from_addr"), [email_addr], txt
|
||||
)
|
||||
return txt
|
||||
@ -559,7 +552,6 @@ _admissionEditor = ndb.EditableTable(
|
||||
"villelycee",
|
||||
"codepostallycee",
|
||||
"codelycee",
|
||||
"debouche",
|
||||
"type_admission",
|
||||
"boursier_prec",
|
||||
),
|
||||
@ -583,8 +575,8 @@ admission_edit = _admissionEditor.edit
|
||||
|
||||
# Edition simultanee de identite et admission
|
||||
class EtudIdentEditor(object):
|
||||
def create(self, cnx, args, REQUEST=None):
|
||||
etudid = identite_create(cnx, args, REQUEST)
|
||||
def create(self, cnx, args):
|
||||
etudid = identite_create(cnx, args)
|
||||
args["etudid"] = etudid
|
||||
admission_create(cnx, args)
|
||||
return etudid
|
||||
@ -615,8 +607,8 @@ class EtudIdentEditor(object):
|
||||
res.sort(key=itemgetter("nom", "prenom"))
|
||||
return res
|
||||
|
||||
def edit(self, cnx, args, disable_notify=False, REQUEST=None):
|
||||
identite_edit(cnx, args, disable_notify=disable_notify, REQUEST=REQUEST)
|
||||
def edit(self, cnx, args, disable_notify=False):
|
||||
identite_edit(cnx, args, disable_notify=disable_notify)
|
||||
if "adm_id" in args: # safety net
|
||||
admission_edit(cnx, args)
|
||||
|
||||
@ -656,11 +648,17 @@ def make_etud_args(etudid=None, code_nip=None, use_request=True, raise_exc=True)
|
||||
return args
|
||||
|
||||
|
||||
def log_unknown_etud():
|
||||
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
|
||||
etud_args = make_etud_args(raise_exc=False)
|
||||
log(f"unknown student: args={etud_args}")
|
||||
|
||||
|
||||
def get_etud_info(etudid=False, code_nip=False, filled=False) -> list:
|
||||
"""infos sur un etudiant (API). If not foud, returns empty list.
|
||||
On peut specifier etudid ou code_nip
|
||||
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
|
||||
(dans cet ordre).
|
||||
ou bien cherche dans les argumenst de la requête courante:
|
||||
etudid, code_nip, code_ine (dans cet ordre).
|
||||
"""
|
||||
if etudid is None:
|
||||
return []
|
||||
@ -673,7 +671,7 @@ def get_etud_info(etudid=False, code_nip=False, filled=False) -> list:
|
||||
return etud
|
||||
|
||||
|
||||
def create_etud(cnx, args={}, REQUEST=None):
|
||||
def create_etud(cnx, args={}):
|
||||
"""Creation d'un étudiant. génère aussi évenement et "news".
|
||||
|
||||
Args:
|
||||
@ -685,7 +683,7 @@ def create_etud(cnx, args={}, REQUEST=None):
|
||||
from app.scodoc import sco_news
|
||||
|
||||
# creation d'un etudiant
|
||||
etudid = etudident_create(cnx, args, REQUEST=REQUEST)
|
||||
etudid = etudident_create(cnx, args)
|
||||
# crée une adresse vide (chaque etudiant doit etre dans la table "adresse" !)
|
||||
_ = adresse_create(
|
||||
cnx,
|
||||
|
@ -31,7 +31,7 @@ import datetime
|
||||
import operator
|
||||
import pprint
|
||||
import time
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
import flask
|
||||
from flask import url_for
|
||||
@ -250,7 +250,6 @@ def do_evaluation_create(
|
||||
publish_incomplete=None,
|
||||
evaluation_type=None,
|
||||
numero=None,
|
||||
REQUEST=None,
|
||||
**kw, # ceci pour absorber les arguments excedentaires de tf #sco8
|
||||
):
|
||||
"""Create an evaluation"""
|
||||
@ -274,13 +273,13 @@ def do_evaluation_create(
|
||||
if args["jour"]:
|
||||
next_eval = None
|
||||
t = (
|
||||
ndb.DateDMYtoISO(args["jour"]),
|
||||
ndb.TimetoISO8601(args["heure_debut"]),
|
||||
ndb.DateDMYtoISO(args["jour"], null_is_empty=True),
|
||||
ndb.TimetoISO8601(args["heure_debut"], null_is_empty=True),
|
||||
)
|
||||
for e in ModEvals:
|
||||
if (
|
||||
ndb.DateDMYtoISO(e["jour"]),
|
||||
ndb.TimetoISO8601(e["heure_debut"]),
|
||||
ndb.DateDMYtoISO(e["jour"], null_is_empty=True),
|
||||
ndb.TimetoISO8601(e["heure_debut"], null_is_empty=True),
|
||||
) > t:
|
||||
next_eval = e
|
||||
break
|
||||
@ -713,7 +712,7 @@ def do_evaluation_etat_in_mod(nt, moduleimpl_id):
|
||||
return etat
|
||||
|
||||
|
||||
def formsemestre_evaluations_cal(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_evaluations_cal(formsemestre_id):
|
||||
"""Page avec calendrier de toutes les evaluations de ce semestre"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > liste evaluations
|
||||
@ -780,7 +779,6 @@ def formsemestre_evaluations_cal(formsemestre_id, REQUEST=None):
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Evaluations du semestre",
|
||||
sem,
|
||||
cssstyles=["css/calabs.css"],
|
||||
@ -844,9 +842,7 @@ def evaluation_date_first_completion(evaluation_id):
|
||||
return max(date_premiere_note.values())
|
||||
|
||||
|
||||
def formsemestre_evaluations_delai_correction(
|
||||
formsemestre_id, format="html", REQUEST=None
|
||||
):
|
||||
def formsemestre_evaluations_delai_correction(formsemestre_id, format="html"):
|
||||
"""Experimental: un tableau indiquant pour chaque évaluation
|
||||
le nombre de jours avant la publication des notes.
|
||||
|
||||
@ -915,13 +911,13 @@ def formsemestre_evaluations_delai_correction(
|
||||
html_title="<h2>Correction des évaluations du semestre</h2>",
|
||||
caption="Correction des évaluations du semestre",
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
|
||||
origin="Généré par %s le " % sco_version.SCONAME
|
||||
+ scu.timedate_human_repr()
|
||||
+ "",
|
||||
filename=scu.make_filename("evaluations_delais_" + sem["titreannee"]),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def module_evaluation_insert_before(ModEvals, next_eval):
|
||||
@ -1089,7 +1085,7 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
||||
% (
|
||||
scu.ScoURL(),
|
||||
group_id,
|
||||
six.moves.urllib.parse.quote(E["jour"], safe=""),
|
||||
urllib.parse.quote(E["jour"], safe=""),
|
||||
)
|
||||
)
|
||||
H.append(
|
||||
@ -1110,7 +1106,6 @@ def evaluation_describe(evaluation_id="", edit_in_place=True):
|
||||
def evaluation_create_form(
|
||||
moduleimpl_id=None,
|
||||
evaluation_id=None,
|
||||
REQUEST=None,
|
||||
edit=False,
|
||||
readonly=False,
|
||||
page_title="Evaluation",
|
||||
@ -1232,11 +1227,9 @@ def evaluation_create_form(
|
||||
initvalues["visibulletinlist"] = ["X"]
|
||||
else:
|
||||
initvalues["visibulletinlist"] = []
|
||||
if (
|
||||
REQUEST.form.get("tf_submitted", False)
|
||||
and "visibulletinlist" not in REQUEST.form
|
||||
):
|
||||
REQUEST.form["visibulletinlist"] = []
|
||||
vals = scu.get_request_args()
|
||||
if vals.get("tf_submitted", False) and "visibulletinlist" not in vals:
|
||||
vals["visibulletinlist"] = []
|
||||
#
|
||||
form = [
|
||||
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
|
||||
@ -1346,8 +1339,8 @@ def evaluation_create_form(
|
||||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
vals,
|
||||
form,
|
||||
cancelbutton="Annuler",
|
||||
submitlabel=submitlabel,
|
||||
@ -1369,7 +1362,7 @@ def evaluation_create_form(
|
||||
tf[2]["visibulletin"] = False
|
||||
if not edit:
|
||||
# creation d'une evaluation
|
||||
evaluation_id = do_evaluation_create(REQUEST=REQUEST, **tf[2])
|
||||
evaluation_id = do_evaluation_create(**tf[2])
|
||||
return flask.redirect(dest_url)
|
||||
else:
|
||||
do_evaluation_edit(tf[2])
|
||||
|
@ -35,11 +35,11 @@ from enum import Enum
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
import openpyxl.utils.datetime
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||
from openpyxl.comments import Comment
|
||||
from openpyxl import Workbook, load_workbook
|
||||
from openpyxl.cell import WriteOnlyCell
|
||||
from openpyxl.styles import Font, Border, Side, Alignment, PatternFill
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00, FORMAT_GENERAL
|
||||
from openpyxl.comments import Comment
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import notesdb
|
||||
@ -59,24 +59,9 @@ class COLORS(Enum):
|
||||
LIGHT_YELLOW = "FFFFFF99"
|
||||
|
||||
|
||||
def send_excel_file(request, data, filename, mime=scu.XLSX_MIMETYPE):
|
||||
"""publication fichier.
|
||||
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
|
||||
"""
|
||||
filename = (
|
||||
scu.unescape_html(scu.suppress_accents(filename))
|
||||
.replace("&", "")
|
||||
.replace(" ", "_")
|
||||
)
|
||||
request.RESPONSE.setHeader("content-type", mime)
|
||||
request.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
# Un style est enregistré comme un dictionnaire qui précise la valeur d'un attributdans la liste suivante:
|
||||
# font, border, number_format, fill, .. (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||
# font, border, number_format, fill,...
|
||||
# (cf https://openpyxl.readthedocs.io/en/stable/styles.html#working-with-styles)
|
||||
|
||||
|
||||
def xldate_as_datetime(xldate, datemode=0):
|
||||
@ -86,6 +71,17 @@ def xldate_as_datetime(xldate, datemode=0):
|
||||
return openpyxl.utils.datetime.from_ISO8601(xldate)
|
||||
|
||||
|
||||
def adjust_sheetname(sheet_name):
|
||||
"""Renvoie un nom convenable pour une feuille excel: < 31 cars, sans caractères spéciaux
|
||||
Le / n'est pas autorisé par exemple.
|
||||
Voir https://xlsxwriter.readthedocs.io/workbook.html#add_worksheet
|
||||
"""
|
||||
sheet_name = scu.make_filename(sheet_name)
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
return sheet_name[:31]
|
||||
|
||||
|
||||
class ScoExcelBook:
|
||||
"""Permet la génération d'un classeur xlsx composé de plusieurs feuilles.
|
||||
usage:
|
||||
@ -98,13 +94,16 @@ class ScoExcelBook:
|
||||
|
||||
def __init__(self):
|
||||
self.sheets = [] # list of sheets
|
||||
self.wb = Workbook(write_only=True)
|
||||
|
||||
def create_sheet(self, sheet_name="feuille", default_style=None):
|
||||
"""Crée une nouvelle feuille dans ce classeur
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut
|
||||
"""
|
||||
sheet = ScoExcelSheet(sheet_name, default_style)
|
||||
sheet_name = adjust_sheetname(sheet_name)
|
||||
ws = self.wb.create_sheet(sheet_name)
|
||||
sheet = ScoExcelSheet(sheet_name, default_style, ws)
|
||||
self.sheets.append(sheet)
|
||||
return sheet
|
||||
|
||||
@ -112,12 +111,12 @@ class ScoExcelBook:
|
||||
"""génération d'un stream binaire représentant la totalité du classeur.
|
||||
retourne le flux
|
||||
"""
|
||||
wb = Workbook(write_only=True)
|
||||
for sheet in self.sheets:
|
||||
sheet.generate(self)
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
sheet.prepare()
|
||||
# construction d'un flux
|
||||
# (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
with NamedTemporaryFile() as tmp:
|
||||
wb.save(tmp.name)
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
@ -125,6 +124,7 @@ class ScoExcelBook:
|
||||
def excel_make_style(
|
||||
bold=False,
|
||||
italic=False,
|
||||
outline=False,
|
||||
color: COLORS = COLORS.BLACK,
|
||||
bgcolor: COLORS = None,
|
||||
halign=None,
|
||||
@ -145,7 +145,14 @@ def excel_make_style(
|
||||
size -- taille de police
|
||||
"""
|
||||
style = {}
|
||||
font = Font(name=font_name, bold=bold, italic=italic, color=color.value, size=size)
|
||||
font = Font(
|
||||
name=font_name,
|
||||
bold=bold,
|
||||
italic=italic,
|
||||
outline=outline,
|
||||
color=color.value,
|
||||
size=size,
|
||||
)
|
||||
style["font"] = font
|
||||
if bgcolor:
|
||||
style["fill"] = PatternFill(fill_type="solid", fgColor=bgcolor.value)
|
||||
@ -182,41 +189,93 @@ class ScoExcelSheet:
|
||||
"""
|
||||
|
||||
def __init__(self, sheet_name="feuille", default_style=None, wb=None):
|
||||
"""Création de la feuille.
|
||||
sheet_name -- le nom de la feuille
|
||||
default_style -- le style par défaut des cellules
|
||||
wb -- le WorkBook dans laquelle se trouve la feuille. Si wb est None (cas d'un classeur mono-feuille),
|
||||
un workbook est crée et associé à cette feuille.
|
||||
"""Création de la feuille. sheet_name
|
||||
-- le nom de la feuille default_style
|
||||
-- le style par défaut des cellules ws
|
||||
-- None si la feuille est autonome (dans ce cas ell crée son propre wb), sinon c'est la worksheet
|
||||
créée par le workbook propriétaire un workbook est crée et associé à cette feuille.
|
||||
"""
|
||||
# Le nom de la feuille ne peut faire plus de 31 caractères.
|
||||
# si la taille du nom de feuille est > 31 on tronque (on pourrait remplacer par 'feuille' ?)
|
||||
self.sheet_name = sheet_name[
|
||||
:31
|
||||
] # if len(sheet_name) > 31: sheet_name = 'Feuille' ?
|
||||
self.rows = [] # list of list of cells
|
||||
# self.cells_styles_lico = {} # { (li,co) : style }
|
||||
# self.cells_styles_li = {} # { li : style }
|
||||
# self.cells_styles_co = {} # { co : style }
|
||||
self.sheet_name = adjust_sheetname(sheet_name)
|
||||
if default_style is None:
|
||||
default_style = excel_make_style()
|
||||
self.default_style = default_style
|
||||
self.wb = wb or Workbook(write_only=True) # Création de workbook si nécessaire
|
||||
self.ws = self.wb.create_sheet(title=self.sheet_name)
|
||||
if wb is None:
|
||||
self.wb = Workbook()
|
||||
self.ws = self.wb.active
|
||||
self.ws.title = self.sheet_name
|
||||
else:
|
||||
self.wb = None
|
||||
self.ws = wb
|
||||
# internal data
|
||||
self.rows = [] # list of list of cells
|
||||
self.column_dimensions = {}
|
||||
self.row_dimensions = {}
|
||||
|
||||
def set_column_dimension_width(self, cle, value):
|
||||
"""Détermine la largeur d'une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
value -- la dimension (unité : 7 pixels comme affiché dans Excel)
|
||||
def excel_make_composite_style(
|
||||
self,
|
||||
alignment=None,
|
||||
border=None,
|
||||
fill=None,
|
||||
number_format=None,
|
||||
font=None,
|
||||
):
|
||||
style = {}
|
||||
if font is not None:
|
||||
style["font"] = font
|
||||
if alignment is not None:
|
||||
style["alignment"] = alignment
|
||||
if border is not None:
|
||||
style["border"] = border
|
||||
if fill is not None:
|
||||
style["fill"] = fill
|
||||
if number_format is None:
|
||||
style["number_format"] = FORMAT_GENERAL
|
||||
else:
|
||||
style["number_format"] = number_format
|
||||
return style
|
||||
|
||||
@staticmethod
|
||||
def i2col(idx):
|
||||
if idx < 26: # one letter key
|
||||
return chr(idx + 65)
|
||||
else: # two letters AA..ZZ
|
||||
first = (idx // 26) + 66
|
||||
second = (idx % 26) + 65
|
||||
return "" + chr(first) + chr(second)
|
||||
|
||||
def set_column_dimension_width(self, cle=None, value=21):
|
||||
"""Détermine la largeur d'une colonne. cle -- identifie la colonne ("A" "B", ... ou 0, 1, 2, ...) si None,
|
||||
value donne la liste des largeurs de colonnes depuis A, B, C, ... value -- la dimension (unité : 7 pixels
|
||||
comme affiché dans Excel)
|
||||
"""
|
||||
self.ws.column_dimensions[cle].width = value
|
||||
if cle is None:
|
||||
for i, val in enumerate(value):
|
||||
self.ws.column_dimensions[self.i2col(i)].width = val
|
||||
# No keys: value is a list of widths
|
||||
elif type(cle) == str: # accepts set_column_with("D", ...)
|
||||
self.ws.column_dimensions[cle].width = value
|
||||
else:
|
||||
self.ws.column_dimensions[self.i2col(cle)].width = value
|
||||
|
||||
def set_column_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une colonne.
|
||||
cle -- identifie la colonne ("A"n "B", ...)
|
||||
def set_row_dimension_height(self, cle=None, value=21):
|
||||
"""Détermine la hauteur d'une ligne. cle -- identifie la ligne (1, 2, ...) si None,
|
||||
value donne la liste des hauteurs de colonnes depuis 1, 2, 3, ... value -- la dimension
|
||||
"""
|
||||
if cle is None:
|
||||
for i, val in enumerate(value, start=1):
|
||||
self.ws.row_dimensions[i].height = val
|
||||
# No keys: value is a list of widths
|
||||
else:
|
||||
self.ws.row_dimensions[cle].height = value
|
||||
|
||||
def set_row_dimension_hidden(self, cle, value):
|
||||
"""Masque ou affiche une ligne.
|
||||
cle -- identifie la colonne (1...)
|
||||
value -- boolean (vrai = colonne cachée)
|
||||
"""
|
||||
self.ws.column_dimensions[cle].hidden = value
|
||||
self.ws.row_dimensions[cle].hidden = value
|
||||
|
||||
def make_cell(self, value: any = None, style=None, comment=None):
|
||||
"""Construit une cellule.
|
||||
@ -232,8 +291,12 @@ class ScoExcelSheet:
|
||||
style = self.default_style
|
||||
if "font" in style:
|
||||
cell.font = style["font"]
|
||||
if "alignment" in style:
|
||||
cell.alignment = style["alignment"]
|
||||
if "border" in style:
|
||||
cell.border = style["border"]
|
||||
if "fill" in style:
|
||||
cell.fill = style["fill"]
|
||||
if "number_format" in style:
|
||||
cell.number_format = style["number_format"]
|
||||
if "fill" in style:
|
||||
@ -272,73 +335,31 @@ class ScoExcelSheet:
|
||||
"""ajoute une ligne déjà construite à la feuille."""
|
||||
self.rows.append(row)
|
||||
|
||||
# def set_style(self, style=None, li=None, co=None):
|
||||
# if li is not None and co is not None:
|
||||
# self.cells_styles_lico[(li, co)] = style
|
||||
# elif li is None:
|
||||
# self.cells_styles_li[li] = style
|
||||
# elif co is None:
|
||||
# self.cells_styles_co[co] = style
|
||||
#
|
||||
# def get_cell_style(self, li, co):
|
||||
# """Get style for specified cell"""
|
||||
# return (
|
||||
# self.cells_styles_lico.get((li, co), None)
|
||||
# or self.cells_styles_li.get(li, None)
|
||||
# or self.cells_styles_co.get(co, None)
|
||||
# or self.default_style
|
||||
# )
|
||||
|
||||
def _generate_ws(self):
|
||||
def prepare(self):
|
||||
"""génére un flux décrivant la feuille.
|
||||
Ce flux pourra ensuite être repris dans send_excel_file (classeur mono feille)
|
||||
ou pour la génération d'un classeur multi-feuilles
|
||||
"""
|
||||
for col in self.column_dimensions.keys():
|
||||
self.ws.column_dimensions[col] = self.column_dimensions[col]
|
||||
for row in self.column_dimensions.keys():
|
||||
self.ws.column_dimensions[row] = self.column_dimensions[row]
|
||||
for row in self.row_dimensions.keys():
|
||||
self.ws.row_dimensions[row] = self.row_dimensions[row]
|
||||
for row in self.rows:
|
||||
self.ws.append(row)
|
||||
|
||||
def generate_standalone(self):
|
||||
def generate(self):
|
||||
"""génération d'un classeur mono-feuille"""
|
||||
self._generate_ws()
|
||||
# this method makes sense only if it is a standalone worksheet (else call workbook.generate()
|
||||
if self.wb is None: # embeded sheet
|
||||
raise ScoValueError("can't generate a single sheet from a ScoWorkbook")
|
||||
|
||||
# construction d'un flux (https://openpyxl.readthedocs.io/en/stable/tutorial.html#saving-as-a-stream)
|
||||
self.prepare()
|
||||
with NamedTemporaryFile() as tmp:
|
||||
self.wb.save(tmp.name)
|
||||
tmp.seek(0)
|
||||
return tmp.read()
|
||||
|
||||
def generate_embeded(self):
|
||||
"""generation d'une feuille include dans un classeur multi-feuilles"""
|
||||
self._generate_ws()
|
||||
|
||||
def gen_workbook(self, wb=None):
|
||||
"""TODO: à remplacer"""
|
||||
"""Generates and returns a workbook from stored data.
|
||||
If wb, add a sheet (tab) to the existing workbook (in this case, returns None).
|
||||
"""
|
||||
if wb is None:
|
||||
wb = Workbook() # Création du fichier
|
||||
sauvegarde = True
|
||||
else:
|
||||
sauvegarde = False
|
||||
ws0 = wb.add_sheet(self.sheet_name)
|
||||
li = 0
|
||||
for row in self.rows:
|
||||
co = 0
|
||||
for c in row:
|
||||
# safety net: allow only str, int and float
|
||||
# #py3 #sco8 A revoir lors de la ré-écriture de ce module
|
||||
# XXX if type(c) not in (IntType, FloatType):
|
||||
# c = str(c).decode(scu.SCO_ENCODING)
|
||||
ws0.write(li, co, c, self.get_cell_style(li, co))
|
||||
co += 1
|
||||
li += 1
|
||||
if sauvegarde:
|
||||
return wb.savetostr()
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def excel_simple_table(
|
||||
titles=None, lines=None, sheet_name=b"feuille", titles_styles=None, comments=None
|
||||
@ -377,7 +398,7 @@ def excel_simple_table(
|
||||
cell_style = text_style
|
||||
cells.append(ws.make_cell(it, cell_style))
|
||||
ws.append_row(cells)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_feuille_saisie(e, titreannee, description, lines):
|
||||
@ -538,19 +559,35 @@ def excel_feuille_saisie(e, titreannee, description, lines):
|
||||
ws.make_cell("cellule vide -> note non modifiée", style_expl),
|
||||
]
|
||||
)
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
||||
|
||||
def excel_bytes_to_list(bytes_content):
|
||||
filelike = io.BytesIO(bytes_content)
|
||||
return _excel_to_list(filelike)
|
||||
try:
|
||||
filelike = io.BytesIO(bytes_content)
|
||||
return _excel_to_list(filelike)
|
||||
except:
|
||||
raise ScoValueError(
|
||||
"""
|
||||
scolars_import_excel_file: un contenu xlsx semble corrompu!
|
||||
peut-être avez vous fourni un fichier au mauvais format (txt, xls, ..)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def excel_file_to_list(filename):
|
||||
return _excel_to_list(filename)
|
||||
try:
|
||||
return _excel_to_list(filename)
|
||||
except:
|
||||
raise ScoValueError(
|
||||
"""scolars_import_excel_file: un contenu xlsx
|
||||
semble corrompu !
|
||||
Peut-être avez-vous fourni un fichier au mauvais format (txt, xls, ...)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def _excel_to_list(filelike): # we may need 'encoding' argument ?
|
||||
def _excel_to_list(filelike):
|
||||
"""returns list of list
|
||||
convert_to_string is a conversion function applied to all non-string values (ie numbers)
|
||||
"""
|
||||
@ -558,7 +595,8 @@ def _excel_to_list(filelike): # we may need 'encoding' argument ?
|
||||
wb = load_workbook(filename=filelike, read_only=True, data_only=True)
|
||||
except:
|
||||
log("Excel_to_list: failure to import document")
|
||||
open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb").write(filelike)
|
||||
with open("/tmp/last_scodoc_import_failure" + scu.XLSX_SUFFIX, "wb") as f:
|
||||
f.write(filelike)
|
||||
raise ScoValueError(
|
||||
"Fichier illisible: assurez-vous qu'il s'agit bien d'un document Excel !"
|
||||
)
|
||||
@ -758,4 +796,4 @@ def excel_feuille_listeappel(
|
||||
cell_2 = ws.make_cell(("Liste éditée le " + dt), style1i)
|
||||
ws.append_row([None, cell_2])
|
||||
|
||||
return ws.generate_standalone()
|
||||
return ws.generate()
|
||||
|
@ -55,11 +55,9 @@ class InvalidNoteValue(ScoException):
|
||||
|
||||
# Exception qui stoque dest_url, utilisee dans Zope standard_error_message
|
||||
class ScoValueError(ScoException):
|
||||
def __init__(self, msg, dest_url=None, REQUEST=None):
|
||||
def __init__(self, msg, dest_url=None):
|
||||
ScoException.__init__(self, msg)
|
||||
self.dest_url = dest_url
|
||||
if REQUEST and dest_url:
|
||||
REQUEST.set("dest_url", dest_url)
|
||||
|
||||
|
||||
class FormatError(ScoValueError):
|
||||
@ -79,7 +77,7 @@ class ScoConfigurationError(ScoValueError):
|
||||
|
||||
|
||||
class ScoLockedFormError(ScoException):
|
||||
def __init__(self, msg="", REQUEST=None):
|
||||
def __init__(self, msg=""):
|
||||
msg = (
|
||||
"Cette formation est verrouillée (car il y a un semestre verrouillé qui s'y réfère). "
|
||||
+ str(msg)
|
||||
@ -90,7 +88,7 @@ class ScoLockedFormError(ScoException):
|
||||
class ScoGenError(ScoException):
|
||||
"exception avec affichage d'une page explicative ad-hoc"
|
||||
|
||||
def __init__(self, msg="", REQUEST=None):
|
||||
def __init__(self, msg=""):
|
||||
ScoException.__init__(self, msg)
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
"""Export d'une table avec les résultats de tous les étudiants
|
||||
"""
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -216,9 +216,7 @@ def get_set_formsemestre_id_dates(start_date, end_date):
|
||||
return {x["id"] for x in s}
|
||||
|
||||
|
||||
def scodoc_table_results(
|
||||
start_date="", end_date="", types_parcours=[], format="html", REQUEST=None
|
||||
):
|
||||
def scodoc_table_results(start_date="", end_date="", types_parcours=[], format="html"):
|
||||
"""Page affichant la table des résultats
|
||||
Les dates sont en dd/mm/yyyy (datepicker javascript)
|
||||
types_parcours est la liste des types de parcours à afficher
|
||||
@ -240,15 +238,13 @@ def scodoc_table_results(
|
||||
start_date_iso, end_date_iso, types_parcours
|
||||
)
|
||||
tab.base_url = "%s?start_date=%s&end_date=%s&types_parcours=%s" % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
start_date,
|
||||
end_date,
|
||||
"&types_parcours=".join([str(x) for x in types_parcours]),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(
|
||||
format=format, with_html_headers=False, REQUEST=REQUEST
|
||||
)
|
||||
return tab.make_page(format=format, with_html_headers=False)
|
||||
tab_html = tab.html()
|
||||
nb_rows = tab.get_nb_rows()
|
||||
else:
|
||||
|
@ -136,11 +136,11 @@ def search_etud_in_dept(expnom=""):
|
||||
vals = {}
|
||||
|
||||
url_args = {"scodoc_dept": g.scodoc_dept}
|
||||
if "dest_url" in request.form:
|
||||
endpoint = request.form["dest_url"]
|
||||
if "dest_url" in vals:
|
||||
endpoint = vals["dest_url"]
|
||||
else:
|
||||
endpoint = "scolar.ficheEtud"
|
||||
if "parameters_keys" in request.form:
|
||||
if "parameters_keys" in vals:
|
||||
for key in vals["parameters_keys"].split(","):
|
||||
url_args[key] = vals[key]
|
||||
|
||||
@ -362,7 +362,7 @@ def table_etud_in_accessible_depts(expnom=None):
|
||||
)
|
||||
|
||||
|
||||
def search_inscr_etud_by_nip(code_nip, REQUEST=None, format="json"):
|
||||
def search_inscr_etud_by_nip(code_nip, format="json"):
|
||||
"""Recherche multi-departement d'un étudiant par son code NIP
|
||||
Seuls les départements accessibles par l'utilisateur sont cherchés.
|
||||
|
||||
@ -404,6 +404,4 @@ def search_inscr_etud_by_nip(code_nip, REQUEST=None, format="json"):
|
||||
)
|
||||
tab = GenTable(columns_ids=columns_ids, rows=T)
|
||||
|
||||
return tab.make_page(
|
||||
format=format, with_html_headers=False, REQUEST=REQUEST, publish=True
|
||||
)
|
||||
return tab.make_page(format=format, with_html_headers=False, publish=True)
|
||||
|
@ -31,7 +31,8 @@ from operator import itemgetter
|
||||
import xml.dom.minidom
|
||||
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
|
||||
@ -92,9 +93,7 @@ def formation_has_locked_sems(formation_id):
|
||||
return sems
|
||||
|
||||
|
||||
def formation_export(
|
||||
formation_id, export_ids=False, export_tags=True, format=None, REQUEST=None
|
||||
):
|
||||
def formation_export(formation_id, export_ids=False, export_tags=True, format=None):
|
||||
"""Get a formation, with UE, matieres, modules
|
||||
in desired format
|
||||
"""
|
||||
@ -131,9 +130,7 @@ def formation_export(
|
||||
if mod["ects"] is None:
|
||||
del mod["ects"]
|
||||
|
||||
return scu.sendResult(
|
||||
REQUEST, F, name="formation", format=format, force_outer_xml_tag=False
|
||||
)
|
||||
return scu.sendResult(F, name="formation", format=format, force_outer_xml_tag=False, attached=True)
|
||||
|
||||
|
||||
def formation_import_xml(doc: str, import_tags=True):
|
||||
@ -162,20 +159,18 @@ def formation_import_xml(doc: str, import_tags=True):
|
||||
D = sco_xml.xml_to_dicts(f)
|
||||
assert D[0] == "formation"
|
||||
F = D[1]
|
||||
F_quoted = F.copy()
|
||||
log("F=%s" % F)
|
||||
ndb.quote_dict(F_quoted)
|
||||
log("F_quoted=%s" % F_quoted)
|
||||
# F_quoted = F.copy()
|
||||
# ndb.quote_dict(F_quoted)
|
||||
F["dept_id"] = g.scodoc_dept_id
|
||||
# find new version number
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
log(
|
||||
"select max(version) from notes_formations where acronyme=%(acronyme)s and titre=%(titre)s"
|
||||
% F_quoted
|
||||
)
|
||||
cursor.execute(
|
||||
"select max(version) from notes_formations where acronyme=%(acronyme)s and titre=%(titre)s",
|
||||
F_quoted,
|
||||
"""SELECT max(version)
|
||||
FROM notes_formations
|
||||
WHERE acronyme=%(acronyme)s and titre=%(titre)s and dept_id=%(dept_id)s
|
||||
""",
|
||||
F,
|
||||
)
|
||||
res = cursor.fetchall()
|
||||
try:
|
||||
@ -196,7 +191,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
||||
assert ue_info[0] == "ue"
|
||||
ue_info[1]["formation_id"] = formation_id
|
||||
if "ue_id" in ue_info[1]:
|
||||
xml_ue_id = ue_info[1]["ue_id"]
|
||||
xml_ue_id = int(ue_info[1]["ue_id"])
|
||||
del ue_info[1]["ue_id"]
|
||||
else:
|
||||
xml_ue_id = None
|
||||
@ -212,7 +207,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
||||
for mod_info in mat_info[2]:
|
||||
assert mod_info[0] == "module"
|
||||
if "module_id" in mod_info[1]:
|
||||
xml_module_id = mod_info[1]["module_id"]
|
||||
xml_module_id = int(mod_info[1]["module_id"])
|
||||
del mod_info[1]["module_id"]
|
||||
else:
|
||||
xml_module_id = None
|
||||
@ -230,7 +225,7 @@ def formation_import_xml(doc: str, import_tags=True):
|
||||
return formation_id, modules_old2new, ues_old2new
|
||||
|
||||
|
||||
def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
||||
def formation_list_table(formation_id=None, args={}):
|
||||
"""List formation, grouped by titre and sorted by versions
|
||||
and listing associated semestres
|
||||
returns a table
|
||||
@ -247,7 +242,7 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
||||
"edit_img", border="0", alt="modifier", title="Modifier titres et code"
|
||||
)
|
||||
|
||||
editable = REQUEST.AUTHENTICATED_USER.has_permission(Permission.ScoChangeFormation)
|
||||
editable = current_user.has_permission(Permission.ScoChangeFormation)
|
||||
|
||||
# Traduit/ajoute des champs à afficher:
|
||||
for f in formations:
|
||||
@ -347,17 +342,18 @@ def formation_list_table(formation_id=None, args={}, REQUEST=None):
|
||||
html_class="formation_list_table table_leftalign",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url="%s?formation_id=%s" % (REQUEST.URL0, formation_id),
|
||||
base_url="%s?formation_id=%s" % (request.base_url, formation_id),
|
||||
page_title=title,
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
|
||||
def formation_create_new_version(formation_id, redirect=True, REQUEST=None):
|
||||
def formation_create_new_version(formation_id, redirect=True):
|
||||
"duplicate formation, with new version number"
|
||||
xml = formation_export(formation_id, export_ids=True, format="xml")
|
||||
new_id, modules_old2new, ues_old2new = formation_import_xml(xml)
|
||||
resp = formation_export(formation_id, export_ids=True, format="xml")
|
||||
xml_data = resp.get_data(as_text=True)
|
||||
new_id, modules_old2new, ues_old2new = formation_import_xml(xml_data)
|
||||
# news
|
||||
F = formation_list(args={"formation_id": new_id})[0]
|
||||
sco_news.add(
|
||||
|
@ -31,7 +31,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
import time
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
|
||||
import app
|
||||
from app.models import Departement
|
||||
@ -61,6 +61,7 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
"gestion_semestrielle",
|
||||
"etat",
|
||||
"bul_hide_xml",
|
||||
"block_moyennes",
|
||||
"bul_bgcolor",
|
||||
"modalite",
|
||||
"resp_can_edit",
|
||||
@ -68,7 +69,6 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
"ens_can_edit_eval",
|
||||
"elt_sem_apo",
|
||||
"elt_annee_apo",
|
||||
"scodoc7_id",
|
||||
),
|
||||
filter_dept=True,
|
||||
sortkey="date_debut",
|
||||
@ -82,6 +82,7 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
"etat": bool,
|
||||
"gestion_compensation": bool,
|
||||
"bul_hide_xml": bool,
|
||||
"block_moyennes": bool,
|
||||
"gestion_semestrielle": bool,
|
||||
"gestion_compensation": bool,
|
||||
"gestion_semestrielle": bool,
|
||||
@ -95,9 +96,7 @@ _formsemestreEditor = ndb.EditableTable(
|
||||
def get_formsemestre(formsemestre_id):
|
||||
"list ONE formsemestre"
|
||||
if not isinstance(formsemestre_id, int):
|
||||
raise ScoValueError(
|
||||
"""Semestre invalide, reprenez l'opération au départ ou si le problème persiste signalez l'erreur sur scodoc-devel@listes.univ-paris13.fr"""
|
||||
)
|
||||
raise ValueError("formsemestre_id must be an integer !")
|
||||
try:
|
||||
sem = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})[0]
|
||||
return sem
|
||||
@ -565,7 +564,7 @@ def list_formsemestre_by_etape(etape_apo=False, annee_scolaire=False):
|
||||
return sems
|
||||
|
||||
|
||||
def view_formsemestre_by_etape(etape_apo=None, format="html", REQUEST=None):
|
||||
def view_formsemestre_by_etape(etape_apo=None, format="html"):
|
||||
"""Affiche table des semestres correspondants à l'étape"""
|
||||
if etape_apo:
|
||||
html_title = (
|
||||
@ -582,8 +581,8 @@ def view_formsemestre_by_etape(etape_apo=None, format="html", REQUEST=None):
|
||||
Etape: <input name="etape_apo" type="text" size="8"></input>
|
||||
</form>""",
|
||||
)
|
||||
tab.base_url = "%s?etape_apo=%s" % (REQUEST.URL0, etape_apo or "")
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
tab.base_url = "%s?etape_apo=%s" % (request.base_url, etape_apo or "")
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def sem_has_etape(sem, code_etape):
|
||||
|
@ -28,7 +28,7 @@
|
||||
"""Menu "custom" (défini par l'utilisateur) dans les semestres
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -77,16 +77,14 @@ def formsemestre_custommenu_html(formsemestre_id):
|
||||
return htmlutils.make_menu("Liens", menu)
|
||||
|
||||
|
||||
def formsemestre_custommenu_edit(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_custommenu_edit(formsemestre_id):
|
||||
"""Dialog to edit the custom menu"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
dest_url = (
|
||||
scu.NotesURL() + "/formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||
)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Modification du menu du semestre ", sem
|
||||
),
|
||||
html_sco_header.html_sem_header("Modification du menu du semestre ", sem),
|
||||
"""<p class="help">Ce menu, spécifique à chaque semestre, peut être utilisé pour placer des liens vers vos applications préférées.</p>
|
||||
<p class="help">Procédez en plusieurs fois si vous voulez ajouter plusieurs items.</p>""",
|
||||
]
|
||||
@ -119,8 +117,8 @@ def formsemestre_custommenu_edit(formsemestre_id, REQUEST=None):
|
||||
initvalues["title_" + str(item["custommenu_id"])] = item["title"]
|
||||
initvalues["url_" + str(item["custommenu_id"])] = item["url"]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
cancelbutton="Annuler",
|
||||
|
@ -28,7 +28,7 @@
|
||||
"""Form choix modules / responsables et creation formsemestre
|
||||
"""
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
from app.auth.models import User
|
||||
|
||||
@ -66,7 +66,7 @@ def _default_sem_title(F):
|
||||
return F["titre"]
|
||||
|
||||
|
||||
def formsemestre_createwithmodules(REQUEST=None):
|
||||
def formsemestre_createwithmodules():
|
||||
"""Page création d'un semestre"""
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
@ -77,7 +77,7 @@ def formsemestre_createwithmodules(REQUEST=None):
|
||||
),
|
||||
"""<h2>Mise en place d'un semestre de formation</h2>""",
|
||||
]
|
||||
r = do_formsemestre_createwithmodules(REQUEST=REQUEST)
|
||||
r = do_formsemestre_createwithmodules()
|
||||
if isinstance(r, str):
|
||||
H.append(r)
|
||||
else:
|
||||
@ -85,13 +85,12 @@ def formsemestre_createwithmodules(REQUEST=None):
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def formsemestre_editwithmodules(REQUEST, formsemestre_id):
|
||||
def formsemestre_editwithmodules(formsemestre_id):
|
||||
"""Page modification semestre"""
|
||||
# portage from dtml
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Modification du semestre",
|
||||
sem,
|
||||
javascripts=["libjs/AutoSuggest.js"],
|
||||
@ -105,12 +104,13 @@ def formsemestre_editwithmodules(REQUEST, formsemestre_id):
|
||||
% scu.icontag("lock_img", border="0", title="Semestre verrouillé")
|
||||
)
|
||||
else:
|
||||
r = do_formsemestre_createwithmodules(REQUEST=REQUEST, edit=1)
|
||||
r = do_formsemestre_createwithmodules(edit=1)
|
||||
if isinstance(r, str):
|
||||
H.append(r)
|
||||
else:
|
||||
return r # response redirect
|
||||
if not REQUEST.form.get("tf_submitted", False):
|
||||
vals = scu.get_request_args()
|
||||
if not vals.get("tf_submitted", False):
|
||||
H.append(
|
||||
"""<p class="help">Seuls les modules cochés font partie de ce semestre. Pour les retirer, les décocher et appuyer sur le bouton "modifier".
|
||||
</p>
|
||||
@ -121,7 +121,7 @@ def formsemestre_editwithmodules(REQUEST, formsemestre_id):
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def can_edit_sem(REQUEST, formsemestre_id="", sem=None):
|
||||
def can_edit_sem(formsemestre_id="", sem=None):
|
||||
"""Return sem if user can edit it, False otherwise"""
|
||||
sem = sem or sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not current_user.has_permission(Permission.ScoImplement): # pas chef
|
||||
@ -130,11 +130,12 @@ def can_edit_sem(REQUEST, formsemestre_id="", sem=None):
|
||||
return sem
|
||||
|
||||
|
||||
def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
def do_formsemestre_createwithmodules(edit=False):
|
||||
"Form choix modules / responsables et creation formsemestre"
|
||||
# Fonction accessible à tous, controle acces à la main:
|
||||
vals = scu.get_request_args()
|
||||
if edit:
|
||||
formsemestre_id = int(REQUEST.form["formsemestre_id"])
|
||||
formsemestre_id = int(vals["formsemestre_id"])
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not current_user.has_permission(Permission.ScoImplement):
|
||||
if not edit:
|
||||
@ -156,14 +157,14 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
uid2display[u.id] = u.get_nomplogin()
|
||||
allowed_user_names = list(uid2display.values()) + [""]
|
||||
#
|
||||
formation_id = int(REQUEST.form["formation_id"])
|
||||
formation_id = int(vals["formation_id"])
|
||||
F = sco_formations.formation_list(args={"formation_id": formation_id})
|
||||
if not F:
|
||||
raise ScoValueError("Formation inexistante !")
|
||||
F = F[0]
|
||||
if not edit:
|
||||
initvalues = {"titre": _default_sem_title(F)}
|
||||
semestre_id = int(REQUEST.form["semestre_id"])
|
||||
semestre_id = int(vals["semestre_id"])
|
||||
sem_module_ids = set()
|
||||
else:
|
||||
# setup form init values
|
||||
@ -309,7 +310,9 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
{
|
||||
"size": 40,
|
||||
"title": "Nom de ce semestre",
|
||||
"explanation": """n'indiquez pas les dates, ni le semestre, ni la modalité dans le titre: ils seront automatiquement ajoutés <input type="button" value="remettre titre par défaut" onClick="document.tf.titre.value='%s';"/>"""
|
||||
"explanation": """n'indiquez pas les dates, ni le semestre, ni la modalité dans
|
||||
le titre: ils seront automatiquement ajoutés <input type="button"
|
||||
value="remettre titre par défaut" onClick="document.tf.titre.value='%s';"/>"""
|
||||
% _default_sem_title(F),
|
||||
},
|
||||
),
|
||||
@ -501,6 +504,14 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
"labels": [""],
|
||||
},
|
||||
),
|
||||
(
|
||||
"block_moyennes",
|
||||
{
|
||||
"input_type": "boolcheckbox",
|
||||
"title": "Bloquer moyennes",
|
||||
"explanation": "empêcher le calcul des moyennes d'UE et générale.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"sep",
|
||||
{
|
||||
@ -534,7 +545,7 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
select_name = "%s!group_id" % mod["module_id"]
|
||||
|
||||
def opt_selected(gid):
|
||||
if gid == REQUEST.form.get(select_name):
|
||||
if gid == vals.get(select_name):
|
||||
return "selected"
|
||||
else:
|
||||
return ""
|
||||
@ -623,38 +634,29 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
initvalues["gestion_compensation_lst"] = ["X"]
|
||||
else:
|
||||
initvalues["gestion_compensation_lst"] = []
|
||||
if (
|
||||
REQUEST.form.get("tf_submitted", False)
|
||||
and "gestion_compensation_lst" not in REQUEST.form
|
||||
):
|
||||
REQUEST.form["gestion_compensation_lst"] = []
|
||||
if vals.get("tf_submitted", False) and "gestion_compensation_lst" not in vals:
|
||||
vals["gestion_compensation_lst"] = []
|
||||
|
||||
initvalues["gestion_semestrielle"] = initvalues.get("gestion_semestrielle", False)
|
||||
if initvalues["gestion_semestrielle"]:
|
||||
initvalues["gestion_semestrielle_lst"] = ["X"]
|
||||
else:
|
||||
initvalues["gestion_semestrielle_lst"] = []
|
||||
if (
|
||||
REQUEST.form.get("tf_submitted", False)
|
||||
and "gestion_semestrielle_lst" not in REQUEST.form
|
||||
):
|
||||
REQUEST.form["gestion_semestrielle_lst"] = []
|
||||
if vals.get("tf_submitted", False) and "gestion_semestrielle_lst" not in vals:
|
||||
vals["gestion_semestrielle_lst"] = []
|
||||
|
||||
initvalues["bul_hide_xml"] = initvalues.get("bul_hide_xml", False)
|
||||
if not initvalues["bul_hide_xml"]:
|
||||
initvalues["bul_publish_xml_lst"] = ["X"]
|
||||
else:
|
||||
initvalues["bul_publish_xml_lst"] = []
|
||||
if (
|
||||
REQUEST.form.get("tf_submitted", False)
|
||||
and "bul_publish_xml_lst" not in REQUEST.form
|
||||
):
|
||||
REQUEST.form["bul_publish_xml_lst"] = []
|
||||
if vals.get("tf_submitted", False) and "bul_publish_xml_lst" not in vals:
|
||||
vals["bul_publish_xml_lst"] = []
|
||||
|
||||
#
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
vals,
|
||||
modform,
|
||||
submitlabel=submitlabel,
|
||||
cancelbutton="Annuler",
|
||||
@ -693,7 +695,6 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
tf[2]["bul_hide_xml"] = False
|
||||
else:
|
||||
tf[2]["bul_hide_xml"] = True
|
||||
|
||||
# remap les identifiants de responsables:
|
||||
tf[2]["responsable_id"] = User.get_user_id_from_nomplogin(
|
||||
tf[2]["responsable_id"]
|
||||
@ -786,7 +787,6 @@ def do_formsemestre_createwithmodules(REQUEST=None, edit=False):
|
||||
moduleimpl_id,
|
||||
formsemestre_id,
|
||||
etudids,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
msg += [
|
||||
"inscription de %d étudiants au module %s"
|
||||
@ -867,7 +867,7 @@ def formsemestre_delete_moduleimpls(formsemestre_id, module_ids_to_del):
|
||||
return ok, msg
|
||||
|
||||
|
||||
def formsemestre_clone(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_clone(formsemestre_id):
|
||||
"""
|
||||
Formulaire clonage d'un semestre
|
||||
"""
|
||||
@ -888,7 +888,6 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Copie du semestre",
|
||||
sem,
|
||||
javascripts=["libjs/AutoSuggest.js"],
|
||||
@ -959,8 +958,8 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
|
||||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
submitlabel="Dupliquer ce semestre",
|
||||
cancelbutton="Annuler",
|
||||
@ -985,7 +984,6 @@ def formsemestre_clone(formsemestre_id, REQUEST=None):
|
||||
tf[2]["date_fin"],
|
||||
clone_evaluations=tf[2]["clone_evaluations"],
|
||||
clone_partitions=tf[2]["clone_partitions"],
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s&head_message=Nouveau%%20semestre%%20créé"
|
||||
@ -1000,7 +998,6 @@ def do_formsemestre_clone(
|
||||
date_fin, # 'dd/mm/yyyy'
|
||||
clone_evaluations=False,
|
||||
clone_partitions=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Clone a semestre: make copy, same modules, same options, same resps, same partitions.
|
||||
New dates, responsable_id
|
||||
@ -1040,7 +1037,7 @@ def do_formsemestre_clone(
|
||||
args = e.copy()
|
||||
del args["jour"] # erase date
|
||||
args["moduleimpl_id"] = mid
|
||||
_ = sco_evaluations.do_evaluation_create(REQUEST=REQUEST, **args)
|
||||
_ = sco_evaluations.do_evaluation_create(**args)
|
||||
|
||||
# 3- copy uecoefs
|
||||
objs = sco_formsemestre.formsemestre_uecoef_list(
|
||||
@ -1113,10 +1110,11 @@ def do_formsemestre_clone(
|
||||
def formsemestre_associate_new_version(
|
||||
formsemestre_id,
|
||||
other_formsemestre_ids=[],
|
||||
REQUEST=None,
|
||||
dialog_confirmed=False,
|
||||
):
|
||||
"""Formulaire changement formation d'un semestre"""
|
||||
formsemestre_id = int(formsemestre_id)
|
||||
other_formsemestre_ids = [int(x) for x in other_formsemestre_ids]
|
||||
if not dialog_confirmed:
|
||||
# dresse le liste des semestres de la meme formation et version
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
@ -1161,15 +1159,19 @@ def formsemestre_associate_new_version(
|
||||
)
|
||||
else:
|
||||
do_formsemestres_associate_new_version(
|
||||
[formsemestre_id] + other_formsemestre_ids, REQUEST=REQUEST
|
||||
[formsemestre_id] + other_formsemestre_ids
|
||||
)
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s&head_message=Formation%%20dupliquée"
|
||||
% formsemestre_id
|
||||
url_for(
|
||||
"notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
head_message="Formation dupliquée",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
||||
def do_formsemestres_associate_new_version(formsemestre_ids):
|
||||
"""Cree une nouvelle version de la formation du semestre, et y rattache les semestres.
|
||||
Tous les moduleimpl sont ré-associés à la nouvelle formation, ainsi que les decisions de jury
|
||||
si elles existent (codes d'UE validées).
|
||||
@ -1179,9 +1181,11 @@ def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
||||
if not formsemestre_ids:
|
||||
return
|
||||
# Check: tous de la même formation
|
||||
assert isinstance(formsemestre_ids[0], int)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_ids[0])
|
||||
formation_id = sem["formation_id"]
|
||||
for formsemestre_id in formsemestre_ids[1:]:
|
||||
assert isinstance(formsemestre_id, int)
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if formation_id != sem["formation_id"]:
|
||||
raise ScoValueError("les semestres ne sont pas tous de la même formation !")
|
||||
@ -1192,9 +1196,7 @@ def do_formsemestres_associate_new_version(formsemestre_ids, REQUEST=None):
|
||||
formation_id,
|
||||
modules_old2new,
|
||||
ues_old2new,
|
||||
) = sco_formations.formation_create_new_version(
|
||||
formation_id, redirect=False, REQUEST=REQUEST
|
||||
)
|
||||
) = sco_formations.formation_create_new_version(formation_id, redirect=False)
|
||||
|
||||
for formsemestre_id in formsemestre_ids:
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
@ -1230,12 +1232,12 @@ def _reassociate_moduleimpls(cnx, formsemestre_id, ues_old2new, modules_old2new)
|
||||
sco_parcours_dut.scolar_formsemestre_validation_edit(cnx, e)
|
||||
|
||||
|
||||
def formsemestre_delete(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_delete(formsemestre_id):
|
||||
"""Delete a formsemestre (affiche avertissements)"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
F = sco_formations.formation_list(args={"formation_id": sem["formation_id"]})[0]
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Suppression du semestre", sem),
|
||||
html_sco_header.html_sem_header("Suppression du semestre", sem),
|
||||
"""<div class="ue_warning"><span>Attention !</span>
|
||||
<p class="help">A n'utiliser qu'en cas d'erreur lors de la saisie d'une formation. Normalement,
|
||||
<b>un semestre ne doit jamais être supprimé</b> (on perd la mémoire des notes et de tous les événements liés à ce semestre !).</p>
|
||||
@ -1261,8 +1263,8 @@ def formsemestre_delete(formsemestre_id, REQUEST=None):
|
||||
else:
|
||||
submit_label = "Confirmer la suppression du semestre"
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(("formsemestre_id", {"input_type": "hidden"}),),
|
||||
initvalues=F,
|
||||
submitlabel=submit_label,
|
||||
@ -1421,7 +1423,7 @@ def do_formsemestre_delete(formsemestre_id):
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
def formsemestre_edit_options(formsemestre_id, target_url=None, REQUEST=None):
|
||||
def formsemestre_edit_options(formsemestre_id):
|
||||
"""dialog to change formsemestre options
|
||||
(accessible par ScoImplement ou dir. etudes)
|
||||
"""
|
||||
@ -1429,12 +1431,10 @@ def formsemestre_edit_options(formsemestre_id, target_url=None, REQUEST=None):
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
if not ok:
|
||||
return err
|
||||
return sco_preferences.SemPreferences(formsemestre_id).edit(
|
||||
REQUEST=REQUEST, categories=["bul"]
|
||||
)
|
||||
return sco_preferences.SemPreferences(formsemestre_id).edit(categories=["bul"])
|
||||
|
||||
|
||||
def formsemestre_change_lock(formsemestre_id, REQUEST=None, dialog_confirmed=False):
|
||||
def formsemestre_change_lock(formsemestre_id) -> None:
|
||||
"""Change etat (verrouille si ouvert, déverrouille si fermé)
|
||||
nota: etat (1 ouvert, 0 fermé)
|
||||
"""
|
||||
@ -1444,34 +1444,12 @@ def formsemestre_change_lock(formsemestre_id, REQUEST=None, dialog_confirmed=Fal
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etat = not sem["etat"]
|
||||
|
||||
if REQUEST and not dialog_confirmed:
|
||||
if etat:
|
||||
msg = "déverrouillage"
|
||||
else:
|
||||
msg = "verrouillage"
|
||||
return scu.confirm_dialog(
|
||||
"<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
|
||||
(par son responsable ou un administrateur).
|
||||
<br/>
|
||||
Le programme d'une formation qui a un semestre verrouillé ne peut plus être modifié.
|
||||
""",
|
||||
dest_url="",
|
||||
cancel_url="formsemestre_status?formsemestre_id=%s" % formsemestre_id,
|
||||
parameters={"formsemestre_id": formsemestre_id},
|
||||
)
|
||||
|
||||
args = {"formsemestre_id": formsemestre_id, "etat": etat}
|
||||
sco_formsemestre.do_formsemestre_edit(args)
|
||||
if REQUEST:
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||
)
|
||||
|
||||
|
||||
def formsemestre_change_publication_bul(
|
||||
formsemestre_id, REQUEST=None, dialog_confirmed=False
|
||||
formsemestre_id, dialog_confirmed=False, redirect=True
|
||||
):
|
||||
"""Change etat publication bulletins sur portail"""
|
||||
ok, err = sco_permissions_check.check_access_diretud(formsemestre_id)
|
||||
@ -1480,7 +1458,7 @@ def formsemestre_change_publication_bul(
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etat = not sem["bul_hide_xml"]
|
||||
|
||||
if REQUEST and not dialog_confirmed:
|
||||
if not dialog_confirmed:
|
||||
if etat:
|
||||
msg = "non"
|
||||
else:
|
||||
@ -1499,14 +1477,14 @@ def formsemestre_change_publication_bul(
|
||||
|
||||
args = {"formsemestre_id": formsemestre_id, "bul_hide_xml": etat}
|
||||
sco_formsemestre.do_formsemestre_edit(args)
|
||||
if REQUEST:
|
||||
if redirect:
|
||||
return flask.redirect(
|
||||
"formsemestre_status?formsemestre_id=%s" % formsemestre_id
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
||||
def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None):
|
||||
"""Changement manuel des coefficients des UE capitalisées."""
|
||||
from app.scodoc import notes_table
|
||||
|
||||
@ -1538,9 +1516,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
||||
</p>
|
||||
"""
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Coefficients des UE du semestre", sem
|
||||
),
|
||||
html_sco_header.html_sem_header("Coefficients des UE du semestre", sem),
|
||||
help,
|
||||
]
|
||||
#
|
||||
@ -1576,8 +1552,8 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
||||
form.append(("ue_" + str(ue["ue_id"]), descr))
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
form,
|
||||
submitlabel="Changer les coefficients",
|
||||
cancelbutton="Annuler",
|
||||
@ -1652,9 +1628,7 @@ def formsemestre_edit_uecoefs(formsemestre_id, err_ue_id=None, REQUEST=None):
|
||||
formsemestre_id=formsemestre_id
|
||||
) # > modif coef UE cap (modifs notes de _certains_ etudiants)
|
||||
|
||||
header = html_sco_header.html_sem_header(
|
||||
REQUEST, "Coefficients des UE du semestre", sem
|
||||
)
|
||||
header = html_sco_header.html_sem_header("Coefficients des UE du semestre", sem)
|
||||
return (
|
||||
header
|
||||
+ "\n".join(z)
|
||||
|
@ -34,7 +34,7 @@ Ces semestres n'auront qu'un seul inscrit !
|
||||
import time
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -52,7 +52,7 @@ from app.scodoc import sco_parcours_dut
|
||||
from app.scodoc import sco_etud
|
||||
|
||||
|
||||
def formsemestre_ext_create(etudid, sem_params, REQUEST=None):
|
||||
def formsemestre_ext_create(etudid, sem_params):
|
||||
"""Crée un formsemestre exterieur et y inscrit l'étudiant.
|
||||
sem_params: dict nécessaire à la création du formsemestre
|
||||
"""
|
||||
@ -79,7 +79,7 @@ def formsemestre_ext_create(etudid, sem_params, REQUEST=None):
|
||||
return formsemestre_id
|
||||
|
||||
|
||||
def formsemestre_ext_create_form(etudid, formsemestre_id, REQUEST=None):
|
||||
def formsemestre_ext_create_form(etudid, formsemestre_id):
|
||||
"""Formulaire creation/inscription à un semestre extérieur"""
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
H = [
|
||||
@ -181,8 +181,8 @@ def formsemestre_ext_create_form(etudid, formsemestre_id, REQUEST=None):
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
method="post",
|
||||
@ -204,13 +204,13 @@ def formsemestre_ext_create_form(etudid, formsemestre_id, REQUEST=None):
|
||||
)
|
||||
else:
|
||||
tf[2]["formation_id"] = orig_sem["formation_id"]
|
||||
formsemestre_ext_create(etudid, tf[2], REQUEST=REQUEST)
|
||||
formsemestre_ext_create(etudid, tf[2])
|
||||
return flask.redirect(
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
)
|
||||
|
||||
|
||||
def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
||||
def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid):
|
||||
"""Edition des validations d'UE et de semestre (jury)
|
||||
pour un semestre extérieur.
|
||||
On peut saisir pour chaque UE du programme de formation
|
||||
@ -222,8 +222,8 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
ue_list = _list_ue_with_coef_and_validations(sem, etudid)
|
||||
descr = _ue_form_description(ue_list, REQUEST.form)
|
||||
if REQUEST and REQUEST.method == "GET":
|
||||
descr = _ue_form_description(ue_list, scu.get_request_args())
|
||||
if request.method == "GET":
|
||||
initvalues = {
|
||||
"note_" + str(ue["ue_id"]): ue["validation"].get("moy_ue", "")
|
||||
for ue in ue_list
|
||||
@ -231,8 +231,8 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
||||
else:
|
||||
initvalues = {}
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cssclass="tf_ext_edit_ue_validations",
|
||||
submitlabel="Enregistrer ces validations",
|
||||
@ -242,19 +242,19 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
||||
if tf[0] == -1:
|
||||
return "<h4>annulation</h4>"
|
||||
else:
|
||||
H = _make_page(etud, sem, tf, REQUEST=REQUEST)
|
||||
H = _make_page(etud, sem, tf)
|
||||
if tf[0] == 0: # premier affichage
|
||||
return "\n".join(H)
|
||||
else: # soumission
|
||||
# simule erreur
|
||||
ok, message = _check_values(ue_list, tf[2])
|
||||
if not ok:
|
||||
H = _make_page(etud, sem, tf, message=message, REQUEST=REQUEST)
|
||||
H = _make_page(etud, sem, tf, message=message)
|
||||
return "\n".join(H)
|
||||
else:
|
||||
# Submit
|
||||
_record_ue_validations_and_coefs(
|
||||
formsemestre_id, etudid, ue_list, tf[2], REQUEST=REQUEST
|
||||
formsemestre_id, etudid, ue_list, tf[2]
|
||||
)
|
||||
return flask.redirect(
|
||||
"formsemestre_bulletinetud?formsemestre_id=%s&etudid=%s"
|
||||
@ -262,7 +262,7 @@ def formsemestre_ext_edit_ue_validations(formsemestre_id, etudid, REQUEST=None):
|
||||
)
|
||||
|
||||
|
||||
def _make_page(etud, sem, tf, message="", REQUEST=None):
|
||||
def _make_page(etud, sem, tf, message=""):
|
||||
nt = sco_cache.NotesTableCache.get(sem["formsemestre_id"])
|
||||
moy_gen = nt.get_etud_moy_gen(etud["etudid"])
|
||||
H = [
|
||||
@ -465,9 +465,7 @@ def _list_ue_with_coef_and_validations(sem, etudid):
|
||||
return ue_list
|
||||
|
||||
|
||||
def _record_ue_validations_and_coefs(
|
||||
formsemestre_id, etudid, ue_list, values, REQUEST=None
|
||||
):
|
||||
def _record_ue_validations_and_coefs(formsemestre_id, etudid, ue_list, values):
|
||||
for ue in ue_list:
|
||||
code = values.get("valid_" + str(ue["ue_id"]), False)
|
||||
if code == "None":
|
||||
@ -492,5 +490,4 @@ def _record_ue_validations_and_coefs(
|
||||
now_dmy,
|
||||
code=code,
|
||||
ue_coefficient=coef,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
@ -30,7 +30,7 @@
|
||||
import time
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
@ -55,6 +55,7 @@ _formsemestre_inscriptionEditor = ndb.EditableTable(
|
||||
"formsemestre_inscription_id",
|
||||
("formsemestre_inscription_id", "etudid", "formsemestre_id", "etat", "etape"),
|
||||
sortkey="formsemestre_id",
|
||||
insert_ignore_conflicts=True,
|
||||
)
|
||||
|
||||
|
||||
@ -248,7 +249,7 @@ def do_formsemestre_inscription_with_modules(
|
||||
|
||||
|
||||
def formsemestre_inscription_with_modules_etud(
|
||||
formsemestre_id, etudid=None, group_ids=None, REQUEST=None
|
||||
formsemestre_id, etudid=None, group_ids=None
|
||||
):
|
||||
"""Form. inscription d'un étudiant au semestre.
|
||||
Si etudid n'est pas specifié, form. choix etudiant.
|
||||
@ -263,7 +264,7 @@ def formsemestre_inscription_with_modules_etud(
|
||||
)
|
||||
|
||||
return formsemestre_inscription_with_modules(
|
||||
etudid, formsemestre_id, REQUEST=REQUEST, group_ids=group_ids
|
||||
etudid, formsemestre_id, group_ids=group_ids
|
||||
)
|
||||
|
||||
|
||||
@ -318,7 +319,7 @@ def formsemestre_inscription_with_modules_form(etudid, only_ext=False):
|
||||
|
||||
|
||||
def formsemestre_inscription_with_modules(
|
||||
etudid, formsemestre_id, group_ids=None, multiple_ok=False, REQUEST=None
|
||||
etudid, formsemestre_id, group_ids=None, multiple_ok=False
|
||||
):
|
||||
"""
|
||||
Inscription de l'etud dans ce semestre.
|
||||
@ -334,7 +335,6 @@ def formsemestre_inscription_with_modules(
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Inscription de %s dans ce semestre" % etud["nomprenom"],
|
||||
sem,
|
||||
)
|
||||
@ -415,7 +415,7 @@ def formsemestre_inscription_with_modules(
|
||||
<input type="hidden" name="etudid" value="%s">
|
||||
<input type="hidden" name="formsemestre_id" value="%s">
|
||||
"""
|
||||
% (REQUEST.URL0, etudid, formsemestre_id)
|
||||
% (request.base_url, etudid, formsemestre_id)
|
||||
)
|
||||
|
||||
H.append(sco_groups.form_group_choice(formsemestre_id, allow_none=True))
|
||||
@ -431,7 +431,7 @@ def formsemestre_inscription_with_modules(
|
||||
return "\n".join(H) + F
|
||||
|
||||
|
||||
def formsemestre_inscription_option(etudid, formsemestre_id, REQUEST=None):
|
||||
def formsemestre_inscription_option(etudid, formsemestre_id):
|
||||
"""Dialogue pour (dés)inscription à des modules optionnels."""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not sem["etat"]:
|
||||
@ -468,7 +468,8 @@ def formsemestre_inscription_option(etudid, formsemestre_id, REQUEST=None):
|
||||
modimpls_by_ue_names[ue_id].append(
|
||||
"%s %s" % (mod["module"]["code"], mod["module"]["titre"])
|
||||
)
|
||||
if not REQUEST.form.get("tf_submitted", False):
|
||||
vals = scu.get_request_args()
|
||||
if not vals.get("tf_submitted", False):
|
||||
# inscrit ?
|
||||
for ins in inscr:
|
||||
if ins["moduleimpl_id"] == mod["moduleimpl_id"]:
|
||||
@ -533,8 +534,8 @@ function chkbx_select(field_id, state) {
|
||||
"""
|
||||
)
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
initvalues,
|
||||
cancelbutton="Annuler",
|
||||
@ -659,7 +660,7 @@ function chkbx_select(field_id, state) {
|
||||
|
||||
|
||||
def do_moduleimpl_incription_options(
|
||||
etudid, modulesimpls_ainscrire, modulesimpls_adesinscrire, REQUEST=None
|
||||
etudid, modulesimpls_ainscrire, modulesimpls_adesinscrire
|
||||
):
|
||||
"""
|
||||
Effectue l'inscription et la description aux modules optionnels
|
||||
@ -711,17 +712,16 @@ def do_moduleimpl_incription_options(
|
||||
oid, formsemestre_id=mod["formsemestre_id"]
|
||||
)
|
||||
|
||||
if REQUEST:
|
||||
H = [
|
||||
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(),
|
||||
]
|
||||
return "\n".join(H)
|
||||
H = [
|
||||
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(),
|
||||
]
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def est_inscrit_ailleurs(etudid, formsemestre_id):
|
||||
@ -756,14 +756,13 @@ def list_inscrits_ailleurs(formsemestre_id):
|
||||
return d
|
||||
|
||||
|
||||
def formsemestre_inscrits_ailleurs(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_inscrits_ailleurs(formsemestre_id):
|
||||
"""Page listant les étudiants inscrits dans un autre semestre
|
||||
dont les dates recouvrent le semestre indiqué.
|
||||
"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Inscriptions multiples parmi les étudiants du semestre ",
|
||||
sem,
|
||||
)
|
||||
|
@ -105,7 +105,7 @@ def _build_menu_stats(formsemestre_id):
|
||||
"title": "Documents Avis Poursuite Etudes",
|
||||
"endpoint": "notes.pe_view_sem_recap",
|
||||
"args": {"formsemestre_id": formsemestre_id},
|
||||
"enabled": True,
|
||||
"enabled": current_app.config["TESTING"] or current_app.config["DEBUG"],
|
||||
},
|
||||
{
|
||||
"title": 'Table "débouchés"',
|
||||
@ -436,7 +436,7 @@ def formsemestre_status_menubar(sem):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def retreive_formsemestre_from_request():
|
||||
def retreive_formsemestre_from_request() -> int:
|
||||
"""Cherche si on a de quoi déduire le semestre affiché à partir des
|
||||
arguments de la requête:
|
||||
formsemestre_id ou moduleimpl ou evaluation ou group_id ou partition_id
|
||||
@ -447,6 +447,7 @@ def retreive_formsemestre_from_request():
|
||||
args = request.form
|
||||
else:
|
||||
return None
|
||||
formsemestre_id = None
|
||||
# Search formsemestre
|
||||
group_ids = args.get("group_ids", [])
|
||||
if "formsemestre_id" in args:
|
||||
@ -479,16 +480,17 @@ def retreive_formsemestre_from_request():
|
||||
elif "partition_id" in args:
|
||||
partition = sco_groups.get_partition(args["partition_id"])
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
else:
|
||||
|
||||
if not formsemestre_id:
|
||||
return None # no current formsemestre
|
||||
|
||||
return formsemestre_id
|
||||
return int(formsemestre_id)
|
||||
|
||||
|
||||
# Element HTML decrivant un semestre (barre de menu et infos)
|
||||
def formsemestre_page_title():
|
||||
"""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)
|
||||
Cherche dans la requete si un semestre est défini (formsemestre_id ou moduleimpl ou evaluation ou group)
|
||||
"""
|
||||
formsemestre_id = retreive_formsemestre_from_request()
|
||||
#
|
||||
@ -568,7 +570,7 @@ def fill_formsemestre(sem):
|
||||
|
||||
|
||||
# Description du semestre sous forme de table exportable
|
||||
def formsemestre_description_table(formsemestre_id, REQUEST=None, with_evals=False):
|
||||
def formsemestre_description_table(formsemestre_id, with_evals=False):
|
||||
"""Description du semestre sous forme de table exportable
|
||||
Liste des modules et de leurs coefficients
|
||||
"""
|
||||
@ -610,7 +612,7 @@ def formsemestre_description_table(formsemestre_id, REQUEST=None, with_evals=Fal
|
||||
moduleimpl_id=M["moduleimpl_id"]
|
||||
)
|
||||
enseignants = ", ".join(
|
||||
[sco_users.user_info(m["ens_id"], REQUEST)["nomprenom"] for m in M["ens"]]
|
||||
[sco_users.user_info(m["ens_id"])["nomprenom"] for m in M["ens"]]
|
||||
)
|
||||
l = {
|
||||
"UE": M["ue"]["acronyme"],
|
||||
@ -698,41 +700,37 @@ def formsemestre_description_table(formsemestre_id, REQUEST=None, with_evals=Fal
|
||||
html_caption=title,
|
||||
html_class="table_leftalign formsemestre_description",
|
||||
base_url="%s?formsemestre_id=%s&with_evals=%s"
|
||||
% (REQUEST.URL0, formsemestre_id, with_evals),
|
||||
% (request.base_url, formsemestre_id, with_evals),
|
||||
page_title=title,
|
||||
html_title=html_sco_header.html_sem_header(
|
||||
REQUEST, "Description du semestre", sem, with_page_header=False
|
||||
"Description du semestre", sem, with_page_header=False
|
||||
),
|
||||
pdf_title=title,
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
)
|
||||
|
||||
|
||||
def formsemestre_description(
|
||||
formsemestre_id, format="html", with_evals=False, REQUEST=None
|
||||
):
|
||||
def formsemestre_description(formsemestre_id, format="html", with_evals=False):
|
||||
"""Description du semestre sous forme de table exportable
|
||||
Liste des modules et de leurs coefficients
|
||||
"""
|
||||
with_evals = int(with_evals)
|
||||
tab = formsemestre_description_table(
|
||||
formsemestre_id, REQUEST, with_evals=with_evals
|
||||
)
|
||||
tab = formsemestre_description_table(formsemestre_id, with_evals=with_evals)
|
||||
tab.html_before_table = """<form name="f" method="get" action="%s">
|
||||
<input type="hidden" name="formsemestre_id" value="%s"></input>
|
||||
<input type="checkbox" name="with_evals" value="1" onchange="document.f.submit()" """ % (
|
||||
REQUEST.URL0,
|
||||
request.base_url,
|
||||
formsemestre_id,
|
||||
)
|
||||
if with_evals:
|
||||
tab.html_before_table += "checked"
|
||||
tab.html_before_table += ">indiquer les évaluations</input></form>"
|
||||
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
# genere liste html pour accès aux groupes de ce semestre
|
||||
def _make_listes_sem(sem, REQUEST=None, with_absences=True):
|
||||
def _make_listes_sem(sem, with_absences=True):
|
||||
# construit l'URL "destination"
|
||||
# (a laquelle on revient apres saisie absences)
|
||||
destination = url_for(
|
||||
@ -897,7 +895,7 @@ def html_expr_diagnostic(diagnostics):
|
||||
return "".join(H)
|
||||
|
||||
|
||||
def formsemestre_status_head(formsemestre_id=None, REQUEST=None, page_title=None):
|
||||
def formsemestre_status_head(formsemestre_id=None, page_title=None):
|
||||
"""En-tête HTML des pages "semestre" """
|
||||
semlist = sco_formsemestre.do_formsemestre_list(
|
||||
args={"formsemestre_id": formsemestre_id}
|
||||
@ -912,12 +910,12 @@ def formsemestre_status_head(formsemestre_id=None, REQUEST=None, page_title=None
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, page_title, sem, with_page_header=False, with_h2=False
|
||||
page_title, sem, with_page_header=False, with_h2=False
|
||||
),
|
||||
"""<table>
|
||||
f"""<table>
|
||||
<tr><td class="fichetitre2">Formation: </td><td>
|
||||
<a href="Notes/ue_list?formation_id=%(formation_id)s" class="discretelink" title="Formation %(acronyme)s, v%(version)s">%(titre)s</a>"""
|
||||
% F,
|
||||
<a href="{url_for('notes.ue_list', scodoc_dept=g.scodoc_dept, formation_id=F['formation_id'])}"
|
||||
class="discretelink" title="Formation {F['acronyme']}, v{F['version']}">{F['titre']}</a>""",
|
||||
]
|
||||
if sem["semestre_id"] >= 0:
|
||||
H.append(", %s %s" % (parcours.SESSION_NAME, sem["semestre_id"]))
|
||||
@ -948,10 +946,13 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
|
||||
</td></tr>"""
|
||||
)
|
||||
H.append("</table>")
|
||||
sem_warning = ""
|
||||
if sem["bul_hide_xml"]:
|
||||
H.append(
|
||||
'<p class="fontorange"><em>Bulletins non publiés sur le portail</em></p>'
|
||||
)
|
||||
sem_warning += "Bulletins non publiés sur le portail. "
|
||||
if sem["block_moyennes"]:
|
||||
sem_warning += "Calcul des moyennes bloqué !"
|
||||
if sem_warning:
|
||||
H.append('<p class="fontorange"><em>' + sem_warning + "</em></p>")
|
||||
if sem["semestre_id"] >= 0 and not sco_formsemestre.sem_une_annee(sem):
|
||||
H.append(
|
||||
'<p class="fontorange"><em>Attention: ce semestre couvre plusieurs années scolaires !</em></p>'
|
||||
@ -962,7 +963,7 @@ Il y a des notes en attente ! Le classement des étudiants n'a qu'une valeur ind
|
||||
return "".join(H)
|
||||
|
||||
|
||||
def formsemestre_status(formsemestre_id=None, REQUEST=None):
|
||||
def formsemestre_status(formsemestre_id=None):
|
||||
"""Tableau de bord semestre HTML"""
|
||||
# porté du DTML
|
||||
cnx = ndb.GetDBConnexion()
|
||||
@ -975,7 +976,7 @@ def formsemestre_status(formsemestre_id=None, REQUEST=None):
|
||||
# )
|
||||
prev_ue_id = None
|
||||
|
||||
can_edit = sco_formsemestre_edit.can_edit_sem(REQUEST, formsemestre_id, sem=sem)
|
||||
can_edit = sco_formsemestre_edit.can_edit_sem(formsemestre_id, sem=sem)
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(page_title="Semestre %s" % sem["titreannee"]),
|
||||
@ -1018,11 +1019,9 @@ def formsemestre_status(formsemestre_id=None, REQUEST=None):
|
||||
ModInscrits = sco_moduleimpl.do_moduleimpl_inscription_list(
|
||||
moduleimpl_id=M["moduleimpl_id"]
|
||||
)
|
||||
mails_enseignants.add(
|
||||
sco_users.user_info(M["responsable_id"], REQUEST)["email"]
|
||||
)
|
||||
mails_enseignants.add(sco_users.user_info(M["responsable_id"])["email"])
|
||||
mails_enseignants |= set(
|
||||
[sco_users.user_info(m["ens_id"], REQUEST)["email"] for m in M["ens"]]
|
||||
[sco_users.user_info(m["ens_id"])["email"] for m in M["ens"]]
|
||||
)
|
||||
ue = M["ue"]
|
||||
if prev_ue_id != ue["ue_id"]:
|
||||
@ -1147,7 +1146,7 @@ def formsemestre_status(formsemestre_id=None, REQUEST=None):
|
||||
# --- LISTE DES ETUDIANTS
|
||||
H += [
|
||||
'<div id="groupes">',
|
||||
_make_listes_sem(sem, REQUEST),
|
||||
_make_listes_sem(sem),
|
||||
"</div>",
|
||||
]
|
||||
# --- Lien mail enseignants:
|
||||
|
@ -30,7 +30,7 @@
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error, time, datetime
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -64,7 +64,6 @@ def formsemestre_validation_etud_form(
|
||||
desturl=None,
|
||||
sortcol=None,
|
||||
readonly=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
nt = sco_cache.NotesTableCache.get(
|
||||
formsemestre_id
|
||||
@ -149,9 +148,7 @@ def formsemestre_validation_etud_form(
|
||||
'</td><td style="text-align: right;"><a href="%s">%s</a></td></tr></table>'
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de %s" % etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de %s" % etud["nom"]),
|
||||
)
|
||||
)
|
||||
|
||||
@ -163,10 +160,11 @@ def formsemestre_validation_etud_form(
|
||||
if etud_etat != "I":
|
||||
H.append(
|
||||
tf_error_message(
|
||||
"""Impossible de statuer sur cet étudiant:
|
||||
il est démissionnaire ou défaillant (voir <a href="%s">sa fiche</a>)
|
||||
f"""Impossible de statuer sur cet étudiant:
|
||||
il est démissionnaire ou défaillant (voir <a href="{
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
}">sa fiche</a>)
|
||||
"""
|
||||
% url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
)
|
||||
)
|
||||
return "\n".join(H + Footer)
|
||||
@ -178,16 +176,19 @@ def formsemestre_validation_etud_form(
|
||||
)
|
||||
if check:
|
||||
if not desturl:
|
||||
desturl = (
|
||||
"formsemestre_recapcomplet?modejury=1&hidemodules=1&hidebac=1&pref_override=0&formsemestre_id="
|
||||
+ str(formsemestre_id)
|
||||
desturl = url_for(
|
||||
"notes.formsemestre_recapcomplet",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
modejury=1,
|
||||
hidemodules=1,
|
||||
hidebac=1,
|
||||
pref_override=0,
|
||||
formsemestre_id=formsemestre_id,
|
||||
sortcol=sortcol
|
||||
or None, # pour refaire tri sorttable du tableau de notes
|
||||
_anchor="etudid%s" % etudid, # va a la bonne ligne
|
||||
)
|
||||
if sortcol:
|
||||
desturl += (
|
||||
"&sortcol=" + sortcol
|
||||
) # pour refaire tri sorttable du tableau de notes
|
||||
desturl += "#etudid%s" % etudid # va a la bonne ligne
|
||||
H.append('<ul><li><a href="%s">Continuer</a></li></ul>' % desturl)
|
||||
H.append(f'<ul><li><a href="{desturl}">Continuer</a></li></ul>')
|
||||
|
||||
return "\n".join(H + Footer)
|
||||
|
||||
@ -197,8 +198,12 @@ def formsemestre_validation_etud_form(
|
||||
if nt.etud_has_notes_attente(etudid):
|
||||
H.append(
|
||||
tf_error_message(
|
||||
"""Impossible de statuer sur cet étudiant: il a des notes en attente dans des évaluations de ce semestre (voir <a href="formsemestre_status?formsemestre_id=%s">tableau de bord</a>)"""
|
||||
% formsemestre_id
|
||||
f"""Impossible de statuer sur cet étudiant: il a des notes en
|
||||
attente dans des évaluations de ce semestre (voir <a href="{
|
||||
url_for( "notes.formsemestre_status",
|
||||
scodoc_dept=g.scodoc_dept, formsemestre_id=formsemestre_id)
|
||||
}">tableau de bord</a>)
|
||||
"""
|
||||
)
|
||||
)
|
||||
return "\n".join(H + Footer)
|
||||
@ -213,14 +218,24 @@ def formsemestre_validation_etud_form(
|
||||
if not Se.prev_decision:
|
||||
H.append(
|
||||
tf_error_message(
|
||||
"""Le jury n\'a pas statué sur le semestre précédent ! (<a href="formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s">le faire maintenant</a>)"""
|
||||
% (Se.prev["formsemestre_id"], etudid)
|
||||
f"""Le jury n'a pas statué sur le semestre précédent ! (<a href="{
|
||||
url_for("notes.formsemestre_validation_etud_form",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=Se.prev["formsemestre_id"],
|
||||
etudid=etudid)
|
||||
}">le faire maintenant</a>)
|
||||
"""
|
||||
)
|
||||
)
|
||||
if decision_jury:
|
||||
H.append(
|
||||
'<a href="formsemestre_validation_suppress_etud?etudid=%s&formsemestre_id=%s" class="stdlink">Supprimer décision existante</a>'
|
||||
% (etudid, formsemestre_id)
|
||||
f"""<a href="{
|
||||
url_for("notes.formsemestre_validation_suppress_etud",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
etudid=etudid, formsemestre_id=formsemestre_id
|
||||
)
|
||||
}" class="stdlink">Supprimer décision existante</a>
|
||||
"""
|
||||
)
|
||||
H.append(html_sco_header.sco_footer())
|
||||
return "\n".join(H)
|
||||
@ -338,7 +353,6 @@ def formsemestre_validation_etud(
|
||||
codechoice=None, # required
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Enregistre validation"""
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
@ -354,9 +368,9 @@ def formsemestre_validation_etud(
|
||||
if not selected_choice:
|
||||
raise ValueError("code choix invalide ! (%s)" % codechoice)
|
||||
#
|
||||
Se.valide_decision(selected_choice, REQUEST) # enregistre
|
||||
Se.valide_decision(selected_choice) # enregistre
|
||||
return _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol, REQUEST
|
||||
formsemestre_id, etudid, Se, selected_choice, desturl, sortcol
|
||||
)
|
||||
|
||||
|
||||
@ -369,7 +383,6 @@ def formsemestre_validation_etud_manu(
|
||||
assidu=False,
|
||||
desturl="",
|
||||
sortcol=None,
|
||||
REQUEST=None,
|
||||
redirect=True,
|
||||
):
|
||||
"""Enregistre validation"""
|
||||
@ -399,22 +412,20 @@ def formsemestre_validation_etud_manu(
|
||||
formsemestre_id_utilise_pour_compenser=formsemestre_id_utilise_pour_compenser,
|
||||
)
|
||||
#
|
||||
Se.valide_decision(choice, REQUEST) # enregistre
|
||||
Se.valide_decision(choice) # enregistre
|
||||
if redirect:
|
||||
return _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol
|
||||
)
|
||||
|
||||
|
||||
def _redirect_valid_choice(
|
||||
formsemestre_id, etudid, Se, choice, desturl, sortcol, REQUEST
|
||||
):
|
||||
def _redirect_valid_choice(formsemestre_id, etudid, Se, choice, desturl, sortcol):
|
||||
adr = "formsemestre_validation_etud_form?formsemestre_id=%s&etudid=%s&check=1" % (
|
||||
formsemestre_id,
|
||||
etudid,
|
||||
)
|
||||
if sortcol:
|
||||
adr += "&sortcol=" + sortcol
|
||||
adr += "&sortcol=" + str(sortcol)
|
||||
# if desturl:
|
||||
# desturl += "&desturl=" + desturl
|
||||
return flask.redirect(adr)
|
||||
@ -821,12 +832,12 @@ def form_decision_manuelle(Se, formsemestre_id, etudid, desturl="", sortcol=None
|
||||
|
||||
|
||||
# -----------
|
||||
def formsemestre_validation_auto(formsemestre_id, REQUEST):
|
||||
def formsemestre_validation_auto(formsemestre_id):
|
||||
"Formulaire saisie automatisee des decisions d'un semestre"
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Saisie automatique des décisions du semestre", sem
|
||||
"Saisie automatique des décisions du semestre", sem
|
||||
),
|
||||
"""
|
||||
<ul>
|
||||
@ -851,7 +862,7 @@ def formsemestre_validation_auto(formsemestre_id, REQUEST):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def do_formsemestre_validation_auto(formsemestre_id, REQUEST):
|
||||
def do_formsemestre_validation_auto(formsemestre_id):
|
||||
"Saisie automatisee des decisions d'un semestre"
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
next_semestre_id = sem["semestre_id"] + 1
|
||||
@ -907,7 +918,6 @@ def do_formsemestre_validation_auto(formsemestre_id, REQUEST):
|
||||
code_etat=ADM,
|
||||
devenir="NEXT",
|
||||
assidu=True,
|
||||
REQUEST=REQUEST,
|
||||
redirect=False,
|
||||
)
|
||||
nb_valid += 1
|
||||
@ -972,7 +982,7 @@ def formsemestre_validation_suppress_etud(formsemestre_id, etudid):
|
||||
) # > suppr. decision jury (peut affecter de plusieurs semestres utilisant UE capitalisée)
|
||||
|
||||
|
||||
def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
||||
def formsemestre_validate_previous_ue(formsemestre_id, etudid):
|
||||
"""Form. saisie UE validée hors ScoDoc
|
||||
(pour étudiants arrivant avec un UE antérieurement validée).
|
||||
"""
|
||||
@ -994,9 +1004,7 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
||||
'</td><td style="text-align: right;"><a href="%s">%s</a></td></tr></table>'
|
||||
% (
|
||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid),
|
||||
sco_photos.etud_photo_html(
|
||||
etud, title="fiche de %s" % etud["nom"], REQUEST=REQUEST
|
||||
),
|
||||
sco_photos.etud_photo_html(etud, title="fiche de %s" % etud["nom"]),
|
||||
)
|
||||
),
|
||||
"""<p class="help">Utiliser cette page pour enregistrer une UE validée antérieurement,
|
||||
@ -1017,8 +1025,8 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
||||
ue_names = ["Choisir..."] + ["%(acronyme)s %(titre)s" % ue for ue in ues]
|
||||
ue_ids = [""] + [ue["ue_id"] for ue in ues]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
("formsemestre_id", {"input_type": "hidden"}),
|
||||
@ -1091,7 +1099,6 @@ def formsemestre_validate_previous_ue(formsemestre_id, etudid, REQUEST=None):
|
||||
tf[2]["moy_ue"],
|
||||
tf[2]["date"],
|
||||
semestre_id=semestre_id,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
return flask.redirect(
|
||||
scu.ScoURL()
|
||||
@ -1109,7 +1116,6 @@ def do_formsemestre_validate_previous_ue(
|
||||
code=ADM,
|
||||
semestre_id=None,
|
||||
ue_coefficient=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Enregistre (ou modifie) validation d'UE (obtenue hors ScoDoc).
|
||||
Si le coefficient est spécifié, modifie le coefficient de
|
||||
@ -1165,7 +1171,7 @@ def _invalidate_etud_formation_caches(etudid, formation_id):
|
||||
) # > modif decision UE (inval tous semestres avec cet etudiant, ok mais conservatif)
|
||||
|
||||
|
||||
def get_etud_ue_cap_html(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
def get_etud_ue_cap_html(etudid, formsemestre_id, ue_id):
|
||||
"""Ramene bout de HTML pour pouvoir supprimer une validation de cette UE"""
|
||||
valids = ndb.SimpleDictFetch(
|
||||
"""SELECT SFV.*
|
||||
@ -1201,7 +1207,7 @@ def get_etud_ue_cap_html(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
def etud_ue_suppress_validation(etudid, formsemestre_id, ue_id):
|
||||
"""Suppress a validation (ue_id, etudid) and redirect to formsemestre"""
|
||||
log("etud_ue_suppress_validation( %s, %s, %s)" % (etudid, formsemestre_id, ue_id))
|
||||
cnx = ndb.GetDBConnexion()
|
||||
|
@ -42,8 +42,8 @@ from xml.etree import ElementTree
|
||||
from xml.etree.ElementTree import Element
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import url_for
|
||||
from flask import g, request
|
||||
from flask import url_for, make_response
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -468,7 +468,7 @@ def get_etud_groups_in_partition(partition_id):
|
||||
return R
|
||||
|
||||
|
||||
def formsemestre_partition_list(formsemestre_id, format="xml", REQUEST=None):
|
||||
def formsemestre_partition_list(formsemestre_id, format="xml"):
|
||||
"""Get partitions and groups in this semestre
|
||||
Supported formats: xml, json
|
||||
"""
|
||||
@ -476,11 +476,11 @@ def formsemestre_partition_list(formsemestre_id, format="xml", REQUEST=None):
|
||||
# Ajoute les groupes
|
||||
for p in partitions:
|
||||
p["group"] = get_partition_groups(p)
|
||||
return scu.sendResult(REQUEST, partitions, name="partition", format=format)
|
||||
return scu.sendResult(partitions, name="partition", format=format)
|
||||
|
||||
|
||||
# Encore utilisé par groupmgr.js
|
||||
def XMLgetGroupsInPartition(partition_id, REQUEST=None): # was XMLgetGroupesTD
|
||||
def XMLgetGroupsInPartition(partition_id): # was XMLgetGroupesTD
|
||||
"""
|
||||
Deprecated: use group_list
|
||||
Liste des étudiants dans chaque groupe de cette partition.
|
||||
@ -499,8 +499,7 @@ def XMLgetGroupsInPartition(partition_id, REQUEST=None): # was XMLgetGroupesTD
|
||||
groups = get_partition_groups(partition)
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > inscrdict
|
||||
etuds_set = set(nt.inscrdict)
|
||||
# XML response:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
# Build XML:
|
||||
doc = Element("ajax-response")
|
||||
x_response = Element("response", type="object", id="MyUpdater")
|
||||
doc.append(x_response)
|
||||
@ -552,7 +551,11 @@ def XMLgetGroupsInPartition(partition_id, REQUEST=None): # was XMLgetGroupesTD
|
||||
)
|
||||
)
|
||||
log("XMLgetGroupsInPartition: %s seconds" % (time.time() - t0))
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
# XML response:
|
||||
data = sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
response = make_response(data)
|
||||
response.headers["Content-Type"] = scu.XML_MIMETYPE
|
||||
return response
|
||||
|
||||
|
||||
def comp_origin(etud, cur_sem):
|
||||
@ -652,7 +655,6 @@ def setGroups(
|
||||
groupsLists="", # members of each existing group
|
||||
groupsToCreate="", # name and members of new groups
|
||||
groupsToDelete="", # groups to delete
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Affect groups (Ajax request)
|
||||
groupsLists: lignes de la forme "group_id;etudid;...\n"
|
||||
@ -716,7 +718,7 @@ def setGroups(
|
||||
|
||||
# Supprime les groupes indiqués comme supprimés:
|
||||
for group_id in groupsToDelete:
|
||||
suppressGroup(group_id, partition_id=partition_id, REQUEST=REQUEST)
|
||||
suppressGroup(group_id, partition_id=partition_id)
|
||||
|
||||
# Crée les nouveaux groupes
|
||||
for line in groupsToCreate.split("\n"): # for each group_name (one per line)
|
||||
@ -733,10 +735,12 @@ def setGroups(
|
||||
for etudid in fs[1:-1]:
|
||||
change_etud_group_in_partition(etudid, group_id, partition)
|
||||
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
return (
|
||||
data = (
|
||||
'<?xml version="1.0" encoding="utf-8"?><response>Groupes enregistrés</response>'
|
||||
)
|
||||
response = make_response(data)
|
||||
response.headers["Content-Type"] = scu.XML_MIMETYPE
|
||||
return response
|
||||
|
||||
|
||||
def createGroup(partition_id, group_name="", default=False):
|
||||
@ -764,7 +768,7 @@ def createGroup(partition_id, group_name="", default=False):
|
||||
return group_id
|
||||
|
||||
|
||||
def suppressGroup(group_id, partition_id=None, REQUEST=None):
|
||||
def suppressGroup(group_id, partition_id=None):
|
||||
"""form suppression d'un groupe.
|
||||
(ne desinscrit pas les etudiants, change juste leur
|
||||
affectation aux groupes)
|
||||
@ -840,7 +844,7 @@ def getArrowIconsTags():
|
||||
return arrow_up, arrow_down, arrow_none
|
||||
|
||||
|
||||
def editPartitionForm(formsemestre_id=None, REQUEST=None):
|
||||
def editPartitionForm(formsemestre_id=None):
|
||||
"""Form to create/suppress partitions"""
|
||||
# ad-hoc form
|
||||
if not sco_permissions_check.can_change_groups(formsemestre_id):
|
||||
@ -968,7 +972,7 @@ def editPartitionForm(formsemestre_id=None, REQUEST=None):
|
||||
return "\n".join(H) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def partition_set_attr(partition_id, attr, value, REQUEST=None):
|
||||
def partition_set_attr(partition_id, attr, value):
|
||||
"""Set partition attribute: bul_show_rank or show_in_lists"""
|
||||
if attr not in {"bul_show_rank", "show_in_lists"}:
|
||||
raise ValueError("invalid partition attribute: %s" % attr)
|
||||
@ -991,9 +995,7 @@ def partition_set_attr(partition_id, attr, value, REQUEST=None):
|
||||
return "enregistré"
|
||||
|
||||
|
||||
def partition_delete(
|
||||
partition_id, REQUEST=None, force=False, redirect=1, dialog_confirmed=False
|
||||
):
|
||||
def partition_delete(partition_id, force=False, redirect=1, dialog_confirmed=False):
|
||||
"""Suppress a partition (and all groups within).
|
||||
default partition cannot be suppressed (unless force)"""
|
||||
partition = get_partition(partition_id)
|
||||
@ -1036,7 +1038,7 @@ def partition_delete(
|
||||
)
|
||||
|
||||
|
||||
def partition_move(partition_id, after=0, REQUEST=None, redirect=1):
|
||||
def partition_move(partition_id, after=0, redirect=1):
|
||||
"""Move before/after previous one (decrement/increment numero)"""
|
||||
partition = get_partition(partition_id)
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
@ -1071,7 +1073,7 @@ def partition_move(partition_id, after=0, REQUEST=None, redirect=1):
|
||||
)
|
||||
|
||||
|
||||
def partition_rename(partition_id, REQUEST=None):
|
||||
def partition_rename(partition_id):
|
||||
"""Form to rename a partition"""
|
||||
partition = get_partition(partition_id)
|
||||
formsemestre_id = partition["formsemestre_id"]
|
||||
@ -1079,8 +1081,8 @@ def partition_rename(partition_id, REQUEST=None):
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
H = ["<h2>Renommer une partition</h2>"]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("partition_id", {"default": partition_id, "input_type": "hidden"}),
|
||||
(
|
||||
@ -1110,12 +1112,10 @@ def partition_rename(partition_id, REQUEST=None):
|
||||
)
|
||||
else:
|
||||
# form submission
|
||||
return partition_set_name(
|
||||
partition_id, tf[2]["partition_name"], REQUEST=REQUEST, redirect=1
|
||||
)
|
||||
return partition_set_name(partition_id, tf[2]["partition_name"])
|
||||
|
||||
|
||||
def partition_set_name(partition_id, partition_name, REQUEST=None, redirect=1):
|
||||
def partition_set_name(partition_id, partition_name, redirect=1):
|
||||
"""Set partition name"""
|
||||
partition_name = partition_name.strip()
|
||||
if not partition_name:
|
||||
@ -1153,7 +1153,7 @@ def partition_set_name(partition_id, partition_name, REQUEST=None, redirect=1):
|
||||
)
|
||||
|
||||
|
||||
def group_set_name(group_id, group_name, REQUEST=None, redirect=1):
|
||||
def group_set_name(group_id, group_name, redirect=1):
|
||||
"""Set group name"""
|
||||
if group_name:
|
||||
group_name = group_name.strip()
|
||||
@ -1180,7 +1180,7 @@ def group_set_name(group_id, group_name, REQUEST=None, redirect=1):
|
||||
)
|
||||
|
||||
|
||||
def group_rename(group_id, REQUEST=None):
|
||||
def group_rename(group_id):
|
||||
"""Form to rename a group"""
|
||||
group = get_group(group_id)
|
||||
formsemestre_id = group["formsemestre_id"]
|
||||
@ -1188,8 +1188,8 @@ def group_rename(group_id, REQUEST=None):
|
||||
raise AccessDenied("Vous n'avez pas le droit d'effectuer cette opération !")
|
||||
H = ["<h2>Renommer un groupe de %s</h2>" % group["partition_name"]]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("group_id", {"default": group_id, "input_type": "hidden"}),
|
||||
(
|
||||
@ -1223,12 +1223,10 @@ def group_rename(group_id, REQUEST=None):
|
||||
)
|
||||
else:
|
||||
# form submission
|
||||
return group_set_name(
|
||||
group_id, tf[2]["group_name"], REQUEST=REQUEST, redirect=1
|
||||
)
|
||||
return group_set_name(group_id, tf[2]["group_name"], redirect=1)
|
||||
|
||||
|
||||
def groups_auto_repartition(partition_id=None, REQUEST=None):
|
||||
def groups_auto_repartition(partition_id=None):
|
||||
"""Reparti les etudiants dans des groupes dans une partition, en respectant le niveau
|
||||
et la mixité.
|
||||
"""
|
||||
@ -1268,8 +1266,8 @@ def groups_auto_repartition(partition_id=None, REQUEST=None):
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
{},
|
||||
cancelbutton="Annuler",
|
||||
@ -1299,7 +1297,7 @@ def groups_auto_repartition(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( REQUEST)
|
||||
# return '\n'.join(H) + tf[1] + html_sco_header.sco_footer()
|
||||
group_ids.append(createGroup(partition_id, group_name))
|
||||
#
|
||||
nt = sco_cache.NotesTableCache.get(formsemestre_id) # > identdict
|
||||
@ -1499,7 +1497,7 @@ def _sortgroups(groups):
|
||||
# Tri: place 'all' en tête, puis groupe par partition / nom de groupe
|
||||
R = [g for g in groups if g["partition_name"] is None]
|
||||
o = [g for g in groups if g["partition_name"] != None]
|
||||
o.sort(key=lambda x: (x["numero"], x["group_name"]))
|
||||
o.sort(key=lambda x: (x["numero"] or 0, x["group_name"]))
|
||||
|
||||
return R + o
|
||||
|
||||
|
@ -33,7 +33,7 @@ from app.scodoc import sco_groups
|
||||
from app.scodoc.sco_exceptions import AccessDenied
|
||||
|
||||
|
||||
def affectGroups(partition_id, REQUEST=None):
|
||||
def affectGroups(partition_id):
|
||||
"""Formulaire affectation des etudiants aux groupes de la partition.
|
||||
Permet aussi la creation et la suppression de groupes.
|
||||
"""
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -219,21 +219,20 @@ def sco_import_generate_excel_sample(
|
||||
|
||||
def students_import_excel(
|
||||
csvfile,
|
||||
REQUEST=None,
|
||||
formsemestre_id=None,
|
||||
check_homonyms=True,
|
||||
require_ine=False,
|
||||
return_html=True,
|
||||
):
|
||||
"import students from Excel file"
|
||||
diag = scolars_import_excel_file(
|
||||
csvfile,
|
||||
REQUEST,
|
||||
formsemestre_id=formsemestre_id,
|
||||
check_homonyms=check_homonyms,
|
||||
require_ine=require_ine,
|
||||
exclude_cols=["photo_filename"],
|
||||
)
|
||||
if REQUEST:
|
||||
if return_html:
|
||||
if formsemestre_id:
|
||||
dest = url_for(
|
||||
"notes.formsemestre_status",
|
||||
@ -254,7 +253,6 @@ def students_import_excel(
|
||||
|
||||
def scolars_import_excel_file(
|
||||
datafile,
|
||||
REQUEST,
|
||||
formsemestre_id=None,
|
||||
check_homonyms=True,
|
||||
require_ine=False,
|
||||
@ -419,7 +417,6 @@ def scolars_import_excel_file(
|
||||
formsemestre_to_invalidate.add(
|
||||
_import_one_student(
|
||||
cnx,
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
values,
|
||||
GroupIdInferers,
|
||||
@ -492,16 +489,15 @@ def scolars_import_excel_file(
|
||||
|
||||
|
||||
def students_import_admission(
|
||||
csvfile, type_admission="", REQUEST=None, formsemestre_id=None
|
||||
csvfile, type_admission="", formsemestre_id=None, return_html=True
|
||||
):
|
||||
"import donnees admission from Excel file (v2016)"
|
||||
diag = scolars_import_admission(
|
||||
csvfile,
|
||||
REQUEST,
|
||||
formsemestre_id=formsemestre_id,
|
||||
type_admission=type_admission,
|
||||
)
|
||||
if REQUEST:
|
||||
if return_html:
|
||||
H = [html_sco_header.sco_header(page_title="Import données admissions")]
|
||||
H.append("<p>Import terminé !</p>")
|
||||
H.append(
|
||||
@ -520,7 +516,6 @@ def students_import_admission(
|
||||
|
||||
def _import_one_student(
|
||||
cnx,
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
values,
|
||||
GroupIdInferers,
|
||||
@ -538,7 +533,7 @@ def _import_one_student(
|
||||
)
|
||||
# Identite
|
||||
args = values.copy()
|
||||
etudid = sco_etud.identite_create(cnx, args, REQUEST=REQUEST)
|
||||
etudid = sco_etud.identite_create(cnx, args)
|
||||
created_etudids.append(etudid)
|
||||
# Admissions
|
||||
args["etudid"] = etudid
|
||||
@ -587,9 +582,7 @@ def _is_new_ine(cnx, code_ine):
|
||||
|
||||
|
||||
# ------ Fonction ré-écrite en nov 2016 pour lire des fichiers sans etudid (fichiers APB)
|
||||
def scolars_import_admission(
|
||||
datafile, REQUEST, formsemestre_id=None, type_admission=None
|
||||
):
|
||||
def scolars_import_admission(datafile, formsemestre_id=None, type_admission=None):
|
||||
"""Importe données admission depuis un fichier Excel quelconque
|
||||
par exemple ceux utilisés avec APB
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
import datetime
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -81,6 +81,7 @@ def list_authorized_etuds_by_sem(sem, delai=274):
|
||||
"title": src["titreannee"],
|
||||
"title_target": "formsemestre_status?formsemestre_id=%s"
|
||||
% src["formsemestre_id"],
|
||||
"filename": "etud_autorises",
|
||||
},
|
||||
}
|
||||
# ajoute attribut inscrit qui indique si l'étudiant est déjà inscrit dans le semestre dest.
|
||||
@ -99,6 +100,7 @@ def list_authorized_etuds_by_sem(sem, delai=274):
|
||||
% sem["formsemestre_id"],
|
||||
"comment": " actuellement inscrits dans ce semestre",
|
||||
"help": "Ces étudiants sont actuellement inscrits dans ce semestre. Si vous les décochez, il seront désinscrits.",
|
||||
"filename": "etud_inscrits",
|
||||
},
|
||||
}
|
||||
|
||||
@ -146,16 +148,15 @@ def list_inscrits_date(sem):
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"])
|
||||
cursor.execute(
|
||||
"""SELECT I.etudid
|
||||
FROM
|
||||
notes_formsemestre_inscription ins,
|
||||
notes_formsemestre S,
|
||||
identite i
|
||||
"""SELECT ins.etudid
|
||||
FROM
|
||||
notes_formsemestre_inscription ins,
|
||||
notes_formsemestre S
|
||||
WHERE ins.formsemestre_id = S.id
|
||||
AND S.id != %(formsemestre_id)s
|
||||
AND S.date_debut <= %(date_debut_iso)s
|
||||
AND S.date_fin >= %(date_debut_iso)s
|
||||
AND ins.dept_id = %(dept_id)
|
||||
AND S.dept_id = %(dept_id)s
|
||||
""",
|
||||
sem,
|
||||
)
|
||||
@ -264,7 +265,6 @@ def formsemestre_inscr_passage(
|
||||
inscrit_groupes=False,
|
||||
submitted=False,
|
||||
dialog_confirmed=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Form. pour inscription des etudiants d'un semestre dans un autre
|
||||
(donné par formsemestre_id).
|
||||
@ -287,9 +287,11 @@ def formsemestre_inscr_passage(
|
||||
header = html_sco_header.sco_header(page_title="Passage des étudiants")
|
||||
footer = html_sco_header.sco_footer()
|
||||
H = [header]
|
||||
if type(etuds) == type(""):
|
||||
if isinstance(etuds, str):
|
||||
etuds = etuds.split(",") # vient du form de confirmation
|
||||
|
||||
elif isinstance(etuds, int):
|
||||
etuds = [etuds]
|
||||
etuds = [int(x) for x in etuds]
|
||||
auth_etuds_by_sem, inscrits, candidats = list_authorized_etuds_by_sem(sem)
|
||||
etuds_set = set(etuds)
|
||||
candidats_set = set(candidats)
|
||||
@ -312,7 +314,6 @@ def formsemestre_inscr_passage(
|
||||
|
||||
if not submitted:
|
||||
H += build_page(
|
||||
REQUEST,
|
||||
sem,
|
||||
auth_etuds_by_sem,
|
||||
inscrits,
|
||||
@ -342,18 +343,22 @@ def formsemestre_inscr_passage(
|
||||
% inscrits[etudid]
|
||||
)
|
||||
H.append("</ol>")
|
||||
if not a_inscrire and not a_desinscrire:
|
||||
todo = a_inscrire or a_desinscrire
|
||||
if not todo:
|
||||
H.append("""<h3>Il n'y a rien à modifier !</h3>""")
|
||||
H.append(
|
||||
scu.confirm_dialog(
|
||||
dest_url="formsemestre_inscr_passage",
|
||||
dest_url="formsemestre_inscr_passage"
|
||||
if todo
|
||||
else "formsemestre_status",
|
||||
message="<p>Confirmer ?</p>" if todo else "",
|
||||
add_headers=False,
|
||||
cancel_url="formsemestre_inscr_passage?formsemestre_id="
|
||||
+ str(formsemestre_id),
|
||||
OK="Effectuer l'opération",
|
||||
OK="Effectuer l'opération" if todo else "",
|
||||
parameters={
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"etuds": ",".join(etuds),
|
||||
"etuds": ",".join([str(x) for x in etuds]),
|
||||
"inscrit_groupes": inscrit_groupes,
|
||||
"submitted": 1,
|
||||
},
|
||||
@ -397,7 +402,6 @@ def formsemestre_inscr_passage(
|
||||
|
||||
|
||||
def build_page(
|
||||
REQUEST,
|
||||
sem,
|
||||
auth_etuds_by_sem,
|
||||
inscrits,
|
||||
@ -413,9 +417,9 @@ def build_page(
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Passages dans le semestre", sem, with_page_header=False
|
||||
"Passages dans le semestre", sem, with_page_header=False
|
||||
),
|
||||
"""<form method="post" action="%s">""" % REQUEST.URL0,
|
||||
"""<form method="post" action="%s">""" % request.base_url,
|
||||
"""<input type="hidden" name="formsemestre_id" value="%(formsemestre_id)s"/>
|
||||
<input type="submit" name="submitted" value="Appliquer les modifications"/>
|
||||
<a href="#help">aide</a>
|
||||
@ -507,7 +511,12 @@ def etuds_select_boxes(
|
||||
</script>
|
||||
<div class="etuds_select_boxes">"""
|
||||
] # "
|
||||
|
||||
# Élimine les boites vides:
|
||||
auth_etuds_by_cat = {
|
||||
k: auth_etuds_by_cat[k]
|
||||
for k in auth_etuds_by_cat
|
||||
if auth_etuds_by_cat[k]["etuds"]
|
||||
}
|
||||
for src_cat in auth_etuds_by_cat.keys():
|
||||
infos = auth_etuds_by_cat[src_cat]["infos"]
|
||||
infos["comment"] = infos.get("comment", "") # commentaire dans sous-titre boite
|
||||
@ -550,10 +559,8 @@ def etuds_select_boxes(
|
||||
if with_checkbox or sel_inscrits:
|
||||
H.append(")")
|
||||
if base_url and etuds:
|
||||
H.append(
|
||||
'<a href="%s&export_cat_xls=%s">%s</a> '
|
||||
% (base_url, src_cat, scu.ICON_XLS)
|
||||
)
|
||||
url = scu.build_url_query(base_url, export_cat_xls=src_cat)
|
||||
H.append(f'<a href="{url}">{scu.ICON_XLS}</a> ')
|
||||
H.append("</div>")
|
||||
for etud in etuds:
|
||||
if etud.get("inscrit", False):
|
||||
@ -633,4 +640,4 @@ def etuds_select_box_xls(src_cat):
|
||||
caption="%(title)s. %(help)s" % src_cat["infos"],
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
return tab.excel()
|
||||
return tab.excel() # tab.make_page(filename=src_cat["infos"]["filename"])
|
||||
|
@ -27,11 +27,11 @@
|
||||
|
||||
"""Liste des notes d'une évaluation
|
||||
"""
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
from operator import itemgetter
|
||||
import urllib
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -56,7 +56,7 @@ from app.scodoc.gen_tables import GenTable
|
||||
from app.scodoc.htmlutils import histogram_notes
|
||||
|
||||
|
||||
def do_evaluation_listenotes(REQUEST):
|
||||
def do_evaluation_listenotes():
|
||||
"""
|
||||
Affichage des notes d'une évaluation
|
||||
|
||||
@ -64,12 +64,13 @@ def do_evaluation_listenotes(REQUEST):
|
||||
(si moduleimpl_id, affiche toutes les évaluatons du module)
|
||||
"""
|
||||
mode = None
|
||||
if "evaluation_id" in REQUEST.form:
|
||||
evaluation_id = int(REQUEST.form["evaluation_id"])
|
||||
vals = scu.get_request_args()
|
||||
if "evaluation_id" in vals:
|
||||
evaluation_id = int(vals["evaluation_id"])
|
||||
mode = "eval"
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if "moduleimpl_id" in REQUEST.form:
|
||||
moduleimpl_id = int(REQUEST.form["moduleimpl_id"])
|
||||
if "moduleimpl_id" in vals:
|
||||
moduleimpl_id = int(vals["moduleimpl_id"])
|
||||
mode = "module"
|
||||
evals = sco_evaluations.do_evaluation_list({"moduleimpl_id": moduleimpl_id})
|
||||
if not mode:
|
||||
@ -77,7 +78,7 @@ def do_evaluation_listenotes(REQUEST):
|
||||
if not evals:
|
||||
return "<p>Aucune évaluation !</p>"
|
||||
|
||||
format = REQUEST.form.get("format", "html")
|
||||
format = vals.get("format", "html")
|
||||
E = evals[0] # il y a au moins une evaluation
|
||||
# description de l'evaluation
|
||||
if mode == "eval":
|
||||
@ -177,8 +178,8 @@ def do_evaluation_listenotes(REQUEST):
|
||||
),
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cancelbutton=None,
|
||||
submitbutton=None,
|
||||
@ -201,7 +202,6 @@ def do_evaluation_listenotes(REQUEST):
|
||||
hide_groups = tf[2]["hide_groups"]
|
||||
with_emails = tf[2]["with_emails"]
|
||||
return _make_table_notes(
|
||||
REQUEST,
|
||||
tf[1],
|
||||
evals,
|
||||
format=format,
|
||||
@ -214,7 +214,6 @@ def do_evaluation_listenotes(REQUEST):
|
||||
|
||||
|
||||
def _make_table_notes(
|
||||
REQUEST,
|
||||
html_form,
|
||||
evals,
|
||||
format="",
|
||||
@ -482,7 +481,7 @@ def _make_table_notes(
|
||||
# html_generate_cells=False # la derniere ligne (moyennes) est incomplete
|
||||
)
|
||||
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
|
||||
@ -760,9 +759,7 @@ def evaluation_check_absences(evaluation_id):
|
||||
return ValButAbs, AbsNonSignalee, ExcNonSignalee, ExcNonJust, AbsButExc
|
||||
|
||||
|
||||
def evaluation_check_absences_html(
|
||||
evaluation_id, with_header=True, show_ok=True, REQUEST=None
|
||||
):
|
||||
def evaluation_check_absences_html(evaluation_id, with_header=True, show_ok=True):
|
||||
"""Affiche etat verification absences d'une evaluation"""
|
||||
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
@ -778,9 +775,7 @@ def evaluation_check_absences_html(
|
||||
|
||||
if with_header:
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST, "Vérification absences à l'évaluation"
|
||||
),
|
||||
html_sco_header.html_sem_header("Vérification absences à l'évaluation"),
|
||||
sco_evaluations.evaluation_describe(evaluation_id=evaluation_id),
|
||||
"""<p class="help">Vérification de la cohérence entre les notes saisies et les absences signalées.</p>""",
|
||||
]
|
||||
@ -817,8 +812,8 @@ def evaluation_check_absences_html(
|
||||
'<a class="stdlink" href="Absences/doSignaleAbsence?etudid=%s&datedebut=%s&datefin=%s&demijournee=%s&moduleimpl_id=%s">signaler cette absence</a>'
|
||||
% (
|
||||
etud["etudid"],
|
||||
six.moves.urllib.parse.quote(E["jour"]),
|
||||
six.moves.urllib.parse.quote(E["jour"]),
|
||||
urllib.parse.quote(E["jour"]),
|
||||
urllib.parse.quote(E["jour"]),
|
||||
demijournee,
|
||||
E["moduleimpl_id"],
|
||||
)
|
||||
@ -861,12 +856,11 @@ def evaluation_check_absences_html(
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def formsemestre_check_absences_html(formsemestre_id, REQUEST=None):
|
||||
def formsemestre_check_absences_html(formsemestre_id):
|
||||
"""Affiche etat verification absences pour toutes les evaluations du semestre !"""
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Vérification absences aux évaluations de ce semestre",
|
||||
sem,
|
||||
),
|
||||
@ -894,7 +888,6 @@ def formsemestre_check_absences_html(formsemestre_id, REQUEST=None):
|
||||
E["evaluation_id"],
|
||||
with_header=False,
|
||||
show_ok=False,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
)
|
||||
if evals:
|
||||
|
@ -31,7 +31,7 @@
|
||||
"""
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -63,7 +63,7 @@ def formsemestre_table_etuds_lycees(
|
||||
)
|
||||
|
||||
|
||||
def scodoc_table_etuds_lycees(format="html", REQUEST=None):
|
||||
def scodoc_table_etuds_lycees(format="html"):
|
||||
"""Table avec _tous_ les étudiants des semestres non verrouillés
|
||||
de _tous_ les départements.
|
||||
"""
|
||||
@ -84,8 +84,8 @@ def scodoc_table_etuds_lycees(format="html", REQUEST=None):
|
||||
sco_preferences.SemPreferences(),
|
||||
no_links=True,
|
||||
)
|
||||
tab.base_url = REQUEST.URL0
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
tab.base_url = request.base_url
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
H = [
|
||||
@ -181,23 +181,22 @@ def formsemestre_etuds_lycees(
|
||||
format="html",
|
||||
only_primo=False,
|
||||
no_grouping=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Table des lycées d'origine"""
|
||||
tab, etuds_by_lycee = formsemestre_table_etuds_lycees(
|
||||
formsemestre_id, only_primo=only_primo, group_lycees=not no_grouping
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=1"
|
||||
if no_grouping:
|
||||
tab.base_url += "&no_grouping=1"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
F = [
|
||||
sco_report.tsp_form_primo_group(
|
||||
REQUEST, only_primo, no_grouping, formsemestre_id, format
|
||||
only_primo, no_grouping, formsemestre_id, format
|
||||
)
|
||||
]
|
||||
H = [
|
||||
|
@ -88,14 +88,14 @@ def do_modalite_list(*args, **kw):
|
||||
return _modaliteEditor.list(cnx, *args, **kw)
|
||||
|
||||
|
||||
def do_modalite_create(args, REQUEST=None):
|
||||
def do_modalite_create(args):
|
||||
"create a modalite"
|
||||
cnx = ndb.GetDBConnexion()
|
||||
r = _modaliteEditor.create(cnx, args)
|
||||
return r
|
||||
|
||||
|
||||
def do_modalite_delete(oid, REQUEST=None):
|
||||
def do_modalite_delete(oid):
|
||||
"delete a modalite"
|
||||
cnx = ndb.GetDBConnexion()
|
||||
log("do_modalite_delete: form_modalite_id=%s" % oid)
|
||||
|
@ -100,9 +100,7 @@ def do_moduleimpl_delete(oid, formsemestre_id=None):
|
||||
) # > moduleimpl_delete
|
||||
|
||||
|
||||
def do_moduleimpl_list(
|
||||
moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None
|
||||
):
|
||||
def do_moduleimpl_list(moduleimpl_id=None, formsemestre_id=None, module_id=None):
|
||||
"list moduleimpls"
|
||||
args = locals()
|
||||
cnx = ndb.GetDBConnexion()
|
||||
@ -110,7 +108,7 @@ def do_moduleimpl_list(
|
||||
# Ajoute la liste des enseignants
|
||||
for mo in modimpls:
|
||||
mo["ens"] = do_ens_list(args={"moduleimpl_id": mo["moduleimpl_id"]})
|
||||
return scu.return_text_if_published(modimpls, REQUEST)
|
||||
return modimpls
|
||||
|
||||
|
||||
def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None):
|
||||
@ -125,7 +123,7 @@ def do_moduleimpl_edit(args, formsemestre_id=None, cnx=None):
|
||||
|
||||
|
||||
def do_moduleimpl_withmodule_list(
|
||||
moduleimpl_id=None, formsemestre_id=None, module_id=None, REQUEST=None
|
||||
moduleimpl_id=None, formsemestre_id=None, module_id=None
|
||||
):
|
||||
"""Liste les moduleimpls et ajoute dans chacun le module correspondant
|
||||
Tri la liste par semestre/UE/numero_matiere/numero_module.
|
||||
@ -137,7 +135,6 @@ def do_moduleimpl_withmodule_list(
|
||||
from app.scodoc import sco_edit_module
|
||||
|
||||
args = locals()
|
||||
del args["REQUEST"]
|
||||
modimpls = do_moduleimpl_list(
|
||||
**{
|
||||
"moduleimpl_id": moduleimpl_id,
|
||||
@ -166,16 +163,14 @@ def do_moduleimpl_withmodule_list(
|
||||
)
|
||||
)
|
||||
|
||||
return scu.return_text_if_published(modimpls, REQUEST)
|
||||
return modimpls
|
||||
|
||||
|
||||
def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None, REQUEST=None):
|
||||
def do_moduleimpl_inscription_list(moduleimpl_id=None, etudid=None):
|
||||
"list moduleimpl_inscriptions"
|
||||
args = locals()
|
||||
cnx = ndb.GetDBConnexion()
|
||||
return scu.return_text_if_published(
|
||||
_moduleimpl_inscriptionEditor.list(cnx, args), REQUEST
|
||||
)
|
||||
return _moduleimpl_inscriptionEditor.list(cnx, args)
|
||||
|
||||
|
||||
def do_moduleimpl_listeetuds(moduleimpl_id):
|
||||
@ -244,9 +239,7 @@ def do_moduleimpl_inscription_delete(oid, formsemestre_id=None):
|
||||
) # > moduleimpl_inscription
|
||||
|
||||
|
||||
def do_moduleimpl_inscrit_etuds(
|
||||
moduleimpl_id, formsemestre_id, etudids, reset=False, REQUEST=None
|
||||
):
|
||||
def do_moduleimpl_inscrit_etuds(moduleimpl_id, formsemestre_id, etudids, reset=False):
|
||||
"""Inscrit les etudiants (liste d'etudids) a ce module.
|
||||
Si reset, desinscrit tous les autres.
|
||||
"""
|
||||
@ -309,7 +302,7 @@ def do_ens_create(args):
|
||||
return r
|
||||
|
||||
|
||||
def can_change_module_resp(REQUEST, moduleimpl_id):
|
||||
def can_change_module_resp(moduleimpl_id):
|
||||
"""Check if current user can modify module resp. (raise exception if not).
|
||||
= Admin, et dir des etud. (si option l'y autorise)
|
||||
"""
|
||||
|
@ -30,7 +30,8 @@
|
||||
from operator import itemgetter
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -50,9 +51,7 @@ from app.scodoc.sco_exceptions import ScoValueError
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
|
||||
|
||||
def moduleimpl_inscriptions_edit(
|
||||
moduleimpl_id, etuds=[], submitted=False, REQUEST=None
|
||||
):
|
||||
def moduleimpl_inscriptions_edit(moduleimpl_id, etuds=[], submitted=False):
|
||||
"""Formulaire inscription des etudiants a ce module
|
||||
* Gestion des inscriptions
|
||||
Nom TD TA TP (triable)
|
||||
@ -137,7 +136,7 @@ def moduleimpl_inscriptions_edit(
|
||||
|
||||
</script>"""
|
||||
)
|
||||
H.append("""<form method="post" id="mi_form" action="%s">""" % REQUEST.URL0)
|
||||
H.append("""<form method="post" id="mi_form" action="%s">""" % request.base_url)
|
||||
H.append(
|
||||
"""
|
||||
<input type="hidden" name="moduleimpl_id" value="%(moduleimpl_id)s"/>
|
||||
@ -199,7 +198,7 @@ def moduleimpl_inscriptions_edit(
|
||||
else: # SUBMISSION
|
||||
# inscrit a ce module tous les etuds selectionnes
|
||||
sco_moduleimpl.do_moduleimpl_inscrit_etuds(
|
||||
moduleimpl_id, formsemestre_id, etuds, reset=True, REQUEST=REQUEST
|
||||
moduleimpl_id, formsemestre_id, etuds, reset=True
|
||||
)
|
||||
return flask.redirect("moduleimpl_status?moduleimpl_id=%s" % (moduleimpl_id))
|
||||
#
|
||||
@ -230,7 +229,7 @@ def _make_menu(partitions, title="", check="true"):
|
||||
)
|
||||
|
||||
|
||||
def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
|
||||
def moduleimpl_inscriptions_stats(formsemestre_id):
|
||||
"""Affiche quelques informations sur les inscriptions
|
||||
aux modules de ce semestre.
|
||||
|
||||
@ -250,7 +249,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
|
||||
tous sauf <liste d'au plus 7 noms>
|
||||
|
||||
"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
inscrits = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||
@ -285,9 +284,7 @@ def moduleimpl_inscriptions_stats(formsemestre_id, REQUEST=None):
|
||||
mod["nb_inscrits"] = nb_inscrits
|
||||
options.append(mod)
|
||||
# Page HTML:
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Inscriptions aux modules du semestre")
|
||||
]
|
||||
H = [html_sco_header.html_sem_header("Inscriptions aux modules du semestre")]
|
||||
|
||||
H.append("<h3>Inscrits au semestre: %d étudiants</h3>" % len(inscrits))
|
||||
|
||||
@ -524,7 +521,7 @@ def is_inscrit_ue(etudid, formsemestre_id, ue_id):
|
||||
return r
|
||||
|
||||
|
||||
def do_etud_desinscrit_ue(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
def do_etud_desinscrit_ue(etudid, formsemestre_id, ue_id):
|
||||
"""Desincrit l'etudiant de tous les modules de cette UE dans ce semestre."""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor)
|
||||
@ -544,20 +541,19 @@ def do_etud_desinscrit_ue(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
""",
|
||||
{"etudid": etudid, "formsemestre_id": formsemestre_id, "ue_id": ue_id},
|
||||
)
|
||||
if REQUEST:
|
||||
logdb(
|
||||
cnx,
|
||||
method="etud_desinscrit_ue",
|
||||
etudid=etudid,
|
||||
msg="desinscription UE %s" % ue_id,
|
||||
commit=False,
|
||||
)
|
||||
logdb(
|
||||
cnx,
|
||||
method="etud_desinscrit_ue",
|
||||
etudid=etudid,
|
||||
msg="desinscription UE %s" % ue_id,
|
||||
commit=False,
|
||||
)
|
||||
sco_cache.invalidate_formsemestre(
|
||||
formsemestre_id=formsemestre_id
|
||||
) # > desinscription etudiant des modules
|
||||
|
||||
|
||||
def do_etud_inscrit_ue(etudid, formsemestre_id, ue_id, REQUEST=None):
|
||||
def do_etud_inscrit_ue(etudid, formsemestre_id, ue_id):
|
||||
"""Incrit l'etudiant de tous les modules de cette UE dans ce semestre."""
|
||||
# Verifie qu'il est bien inscrit au semestre
|
||||
insem = sco_formsemestre_inscriptions.do_formsemestre_inscription_list(
|
||||
|
@ -28,7 +28,7 @@
|
||||
"""Tableau de bord module
|
||||
"""
|
||||
import time
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import urllib
|
||||
|
||||
from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
@ -55,7 +55,7 @@ from app.scodoc import sco_users
|
||||
# ported from old DTML code in oct 2009
|
||||
|
||||
# menu evaluation dans moduleimpl
|
||||
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0):
|
||||
"Menu avec actions sur une evaluation"
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
modimpl = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
@ -64,7 +64,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
|
||||
if (
|
||||
sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
)
|
||||
and nbnotes != 0
|
||||
):
|
||||
@ -80,7 +80,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
|
||||
current_user, E["moduleimpl_id"]
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -90,7 +90,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -101,7 +101,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
},
|
||||
"enabled": nbnotes == 0
|
||||
and sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -111,7 +111,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -128,16 +128,15 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
"args": {
|
||||
"evaluation_id": evaluation_id,
|
||||
},
|
||||
"enabled": nbnotes == 0
|
||||
and sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"]
|
||||
"enabled": sco_permissions_check.can_edit_notes(
|
||||
current_user, E["moduleimpl_id"]
|
||||
),
|
||||
},
|
||||
{
|
||||
"title": "Absences ce jour",
|
||||
"endpoint": "absences.EtatAbsencesDate",
|
||||
"args": {
|
||||
"date": six.moves.urllib.parse.quote(E["jour"], safe=""),
|
||||
"date": urllib.parse.quote(E["jour"], safe=""),
|
||||
"group_ids": group_id,
|
||||
},
|
||||
"enabled": E["jour"],
|
||||
@ -155,7 +154,7 @@ def moduleimpl_evaluation_menu(evaluation_id, nbnotes=0, REQUEST=None):
|
||||
return htmlutils.make_menu("actions", menuEval, alone=True)
|
||||
|
||||
|
||||
def moduleimpl_status(moduleimpl_id=None, partition_id=None, REQUEST=None):
|
||||
def moduleimpl_status(moduleimpl_id=None, partition_id=None):
|
||||
"""Tableau de bord module (liste des evaluations etc)"""
|
||||
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=moduleimpl_id)[0]
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
@ -191,7 +190,7 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None, REQUEST=None):
|
||||
f"""<span class="blacktt">({module_resp.user_name})</span>""",
|
||||
]
|
||||
try:
|
||||
sco_moduleimpl.can_change_module_resp(REQUEST, moduleimpl_id)
|
||||
sco_moduleimpl.can_change_module_resp(moduleimpl_id)
|
||||
H.append(
|
||||
"""<a class="stdlink" href="edit_moduleimpl_resp?moduleimpl_id=%s">modifier</a>"""
|
||||
% moduleimpl_id
|
||||
@ -515,7 +514,6 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None, REQUEST=None):
|
||||
moduleimpl_evaluation_menu(
|
||||
eval["evaluation_id"],
|
||||
nbnotes=etat["nb_notes"],
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
)
|
||||
H.append("</td>")
|
||||
|
@ -30,7 +30,8 @@
|
||||
Fiche description d'un étudiant et de son parcours
|
||||
|
||||
"""
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -46,6 +47,7 @@ from app.scodoc import sco_groups
|
||||
from app.scodoc import sco_parcours_dut
|
||||
from app.scodoc import sco_permissions_check
|
||||
from app.scodoc import sco_photos
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc import sco_report
|
||||
from app.scodoc import sco_etud
|
||||
from app.scodoc.sco_bulletins import etud_descr_situation_semestre
|
||||
@ -142,18 +144,18 @@ def _menuScolarite(authuser, sem, etudid):
|
||||
)
|
||||
|
||||
|
||||
def ficheEtud(etudid=None, REQUEST=None):
|
||||
def ficheEtud(etudid=None):
|
||||
"fiche d'informations sur un etudiant"
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
cnx = ndb.GetDBConnexion()
|
||||
if etudid and REQUEST:
|
||||
if etudid:
|
||||
# la sidebar est differente s'il y a ou pas un etudid
|
||||
# voir html_sidebar.sidebar()
|
||||
REQUEST.form["etudid"] = etudid
|
||||
g.etudid = etudid
|
||||
args = sco_etud.make_etud_args(etudid=etudid)
|
||||
etuds = sco_etud.etudident_list(cnx, args)
|
||||
if not etuds:
|
||||
log("ficheEtud: etudid=%s REQUEST.form=%s" % (etudid, REQUEST.form))
|
||||
log("ficheEtud: etudid=%s request.args=%s" % (etudid, request.args))
|
||||
raise ScoValueError("Etudiant inexistant !")
|
||||
etud = etuds[0]
|
||||
etudid = etud["etudid"]
|
||||
@ -167,7 +169,7 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
info["info_naissance"] += " à " + info["lieu_naissance"]
|
||||
if info["dept_naissance"]:
|
||||
info["info_naissance"] += " (%s)" % info["dept_naissance"]
|
||||
info["etudfoto"] = sco_photos.etud_photo_html(etud, REQUEST=REQUEST)
|
||||
info["etudfoto"] = sco_photos.etud_photo_html(etud)
|
||||
if (
|
||||
(not info["domicile"])
|
||||
and (not info["codepostaldomicile"])
|
||||
@ -254,10 +256,19 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
with_all_columns=False,
|
||||
a_url="Notes/",
|
||||
)
|
||||
info["link_bul_pdf"] = (
|
||||
'<span class="link_bul_pdf"><a class="stdlink" href="Notes/etud_bulletins_pdf?etudid=%(etudid)s">tous les bulletins</a></span>'
|
||||
% etud
|
||||
)
|
||||
info[
|
||||
"link_bul_pdf"
|
||||
] = f"""<span class="link_bul_pdf"><a class="stdlink" href="{
|
||||
url_for("notes.etud_bulletins_pdf", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
}">tous les bulletins</a></span>"""
|
||||
if authuser.has_permission(Permission.ScoEtudInscrit):
|
||||
info[
|
||||
"link_inscrire_ailleurs"
|
||||
] = f"""<span class="link_bul_pdf"><a class="stdlink" href="{
|
||||
url_for("notes.formsemestre_inscription_with_modules_form", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||
}">inscrire à un autre semestre</a></span>"""
|
||||
else:
|
||||
info["link_inscrire_ailleurs"] = ""
|
||||
else:
|
||||
# non inscrit
|
||||
l = ["<p><b>Etudiant%s non inscrit%s" % (info["ne"], info["ne"])]
|
||||
@ -269,6 +280,7 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
l.append("</b></b>")
|
||||
info["liste_inscriptions"] = "\n".join(l)
|
||||
info["link_bul_pdf"] = ""
|
||||
info["link_inscrire_ailleurs"] = ""
|
||||
|
||||
# Liste des annotations
|
||||
alist = []
|
||||
@ -289,9 +301,11 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
title="Supprimer cette annotation",
|
||||
),
|
||||
)
|
||||
author = sco_users.user_info(a["author"])
|
||||
alist.append(
|
||||
'<tr><td><span class="annodate">Le %(date)s par %(author)s : </span><span class="annoc">%(comment)s</span></td>%(dellink)s</tr>'
|
||||
% a
|
||||
f"""<tr><td><span class="annodate">Le {a['date']} par {author['prenomnom']} :
|
||||
</span><span class="annoc">{a['comment']}</span></td>{a['dellink']}</tr>
|
||||
"""
|
||||
)
|
||||
info["liste_annotations"] = "\n".join(alist)
|
||||
# fiche admission
|
||||
@ -345,7 +359,7 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
# Fichiers archivés:
|
||||
info["fichiers_archive_htm"] = (
|
||||
'<div class="fichetitre">Fichiers associés</div>'
|
||||
+ sco_archives_etud.etud_list_archives_html(REQUEST, etudid)
|
||||
+ sco_archives_etud.etud_list_archives_html(etudid)
|
||||
)
|
||||
|
||||
# Devenir de l'étudiant:
|
||||
@ -392,10 +406,11 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
"inscriptions_mkup"
|
||||
] = """<div class="ficheinscriptions" id="ficheinscriptions">
|
||||
<div class="fichetitre">Parcours</div>%s
|
||||
%s
|
||||
%s %s
|
||||
</div>""" % (
|
||||
info["liste_inscriptions"],
|
||||
info["link_bul_pdf"],
|
||||
info["link_inscrire_ailleurs"],
|
||||
)
|
||||
|
||||
#
|
||||
@ -405,7 +420,7 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
)
|
||||
else:
|
||||
info["groupes_row"] = ""
|
||||
info["menus_etud"] = menus_etud(REQUEST)
|
||||
info["menus_etud"] = menus_etud(etudid)
|
||||
tmpl = """<div class="menus_etud">%(menus_etud)s</div>
|
||||
<div class="ficheEtud" id="ficheEtud"><table>
|
||||
<tr><td>
|
||||
@ -487,13 +502,11 @@ def ficheEtud(etudid=None, REQUEST=None):
|
||||
return header + tmpl % info + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def menus_etud(REQUEST=None):
|
||||
def menus_etud(etudid):
|
||||
"""Menu etudiant (operations sur l'etudiant)"""
|
||||
if "etudid" not in REQUEST.form:
|
||||
return ""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
etud = sco_etud.get_etud_info(etudid=etudid, filled=True)[0]
|
||||
|
||||
menuEtud = [
|
||||
{
|
||||
@ -532,16 +545,14 @@ def menus_etud(REQUEST=None):
|
||||
return htmlutils.make_menu("Etudiant", menuEtud, alone=True)
|
||||
|
||||
|
||||
def etud_info_html(etudid, with_photo="1", REQUEST=None, debug=False):
|
||||
def etud_info_html(etudid, with_photo="1", debug=False):
|
||||
"""An HTML div with basic information and links about this etud.
|
||||
Used for popups information windows.
|
||||
"""
|
||||
formsemestre_id = sco_formsemestre_status.retreive_formsemestre_from_request()
|
||||
with_photo = int(with_photo)
|
||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
||||
photo_html = sco_photos.etud_photo_html(
|
||||
etud, title="fiche de " + etud["nom"], REQUEST=REQUEST
|
||||
)
|
||||
photo_html = sco_photos.etud_photo_html(etud, title="fiche de " + etud["nom"])
|
||||
# experimental: may be too slow to be here
|
||||
etud["codeparcours"], etud["decisions_jury"] = sco_report.get_codeparcoursetud(
|
||||
etud, prefix="S", separator=", "
|
||||
|
@ -535,7 +535,7 @@ class SituationEtudParcoursGeneric(object):
|
||||
validated = True
|
||||
return s
|
||||
|
||||
def valide_decision(self, decision, REQUEST):
|
||||
def valide_decision(self, decision):
|
||||
"""Enregistre la decision (instance de DecisionSem)
|
||||
Enregistre codes semestre et UE, et autorisations inscription.
|
||||
"""
|
||||
@ -588,7 +588,6 @@ class SituationEtudParcoursGeneric(object):
|
||||
self.etudid,
|
||||
decision.code_etat,
|
||||
decision.assiduite,
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
# -- modification du code du semestre precedent
|
||||
if self.prev and decision.new_code_prev:
|
||||
@ -619,7 +618,6 @@ class SituationEtudParcoursGeneric(object):
|
||||
self.etudid,
|
||||
decision.new_code_prev,
|
||||
decision.assiduite, # attention: en toute rigueur il faudrait utiliser une indication de l'assiduite au sem. precedent, que nous n'avons pas...
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
sco_cache.invalidate_formsemestre(
|
||||
@ -897,9 +895,7 @@ def formsemestre_update_validation_sem(
|
||||
return to_invalidate
|
||||
|
||||
|
||||
def formsemestre_validate_ues(
|
||||
formsemestre_id, etudid, code_etat_sem, assiduite, REQUEST=None
|
||||
):
|
||||
def formsemestre_validate_ues(formsemestre_id, etudid, code_etat_sem, assiduite):
|
||||
"""Enregistre codes UE, selon état semestre.
|
||||
Les codes UE sont toujours calculés ici, et non passés en paramètres
|
||||
car ils ne dépendent que de la note d'UE et de la validation ou non du semestre.
|
||||
@ -933,14 +929,13 @@ def formsemestre_validate_ues(
|
||||
cnx, nt, formsemestre_id, etudid, ue_id, code_ue
|
||||
)
|
||||
|
||||
if REQUEST:
|
||||
logdb(
|
||||
cnx,
|
||||
method="validate_ue",
|
||||
etudid=etudid,
|
||||
msg="ue_id=%s code=%s" % (ue_id, code_ue),
|
||||
commit=False,
|
||||
)
|
||||
logdb(
|
||||
cnx,
|
||||
method="validate_ue",
|
||||
etudid=etudid,
|
||||
msg="ue_id=%s code=%s" % (ue_id, code_ue),
|
||||
commit=False,
|
||||
)
|
||||
cnx.commit()
|
||||
|
||||
|
||||
|
@ -43,6 +43,8 @@ Les images sont servies par ScoDoc, via la méthode getphotofile?etudid=xxx
|
||||
|
||||
"""
|
||||
|
||||
from flask.helpers import make_response
|
||||
from app.scodoc.sco_exceptions import ScoGenError
|
||||
import datetime
|
||||
import glob
|
||||
import io
|
||||
@ -52,6 +54,7 @@ import requests
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import PIL
|
||||
from PIL import Image as PILImage
|
||||
|
||||
from flask import request, g
|
||||
@ -118,7 +121,7 @@ def etud_photo_url(etud, size="small", fast=False):
|
||||
return photo_url
|
||||
|
||||
|
||||
def get_photo_image(etudid=None, size="small", REQUEST=None):
|
||||
def get_photo_image(etudid=None, size="small"):
|
||||
"""Returns photo image (HTTP response)
|
||||
If not etudid, use "unknown" image
|
||||
"""
|
||||
@ -129,24 +132,14 @@ def get_photo_image(etudid=None, size="small", REQUEST=None):
|
||||
filename = photo_pathname(etud, size=size)
|
||||
if not filename:
|
||||
filename = UNKNOWN_IMAGE_PATH
|
||||
return _http_jpeg_file(filename, REQUEST=REQUEST)
|
||||
return _http_jpeg_file(filename)
|
||||
|
||||
|
||||
def _http_jpeg_file(filename, REQUEST=None):
|
||||
"""returns an image.
|
||||
This function will be modified when we kill #zope
|
||||
"""
|
||||
def _http_jpeg_file(filename):
|
||||
"""returns an image as a Flask response"""
|
||||
st = os.stat(filename)
|
||||
last_modified = st.st_mtime # float timestamp
|
||||
last_modified_str = time.strftime(
|
||||
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last_modified)
|
||||
)
|
||||
file_size = st.st_size
|
||||
RESPONSE = REQUEST.RESPONSE
|
||||
RESPONSE.setHeader("Content-Type", "image/jpeg")
|
||||
RESPONSE.setHeader("Last-Modified", last_modified_str)
|
||||
RESPONSE.setHeader("Cache-Control", "max-age=3600")
|
||||
RESPONSE.setHeader("Content-Length", str(file_size))
|
||||
header = request.headers.get("If-Modified-Since")
|
||||
if header is not None:
|
||||
header = header.split(";")[0]
|
||||
@ -159,20 +152,27 @@ def _http_jpeg_file(filename, REQUEST=None):
|
||||
try:
|
||||
dt = datetime.datetime.strptime(header, "%a, %d %b %Y %H:%M:%S GMT")
|
||||
mod_since = dt.timestamp()
|
||||
except:
|
||||
except ValueError:
|
||||
mod_since = None
|
||||
if (mod_since is not None) and last_modified <= mod_since:
|
||||
RESPONSE.setStatus(304) # not modified
|
||||
return ""
|
||||
|
||||
return open(filename, mode="rb").read()
|
||||
return "", 304 # not modified
|
||||
#
|
||||
last_modified_str = time.strftime(
|
||||
"%a, %d %b %Y %H:%M:%S GMT", time.gmtime(last_modified)
|
||||
)
|
||||
response = make_response(open(filename, mode="rb").read())
|
||||
response.headers["Content-Type"] = "image/jpeg"
|
||||
response.headers["Last-Modified"] = last_modified_str
|
||||
response.headers["Cache-Control"] = "max-age=3600"
|
||||
response.headers["Content-Length"] = str(file_size)
|
||||
return response
|
||||
|
||||
|
||||
def etud_photo_is_local(etud, size="small"):
|
||||
return photo_pathname(etud, size=size)
|
||||
|
||||
|
||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small", REQUEST=None):
|
||||
def etud_photo_html(etud=None, etudid=None, title=None, size="small"):
|
||||
"""HTML img tag for the photo, either in small size (h90)
|
||||
or original size (size=="orig")
|
||||
"""
|
||||
@ -204,14 +204,12 @@ def etud_photo_html(etud=None, etudid=None, title=None, size="small", REQUEST=No
|
||||
)
|
||||
|
||||
|
||||
def etud_photo_orig_html(etud=None, etudid=None, title=None, REQUEST=None):
|
||||
def etud_photo_orig_html(etud=None, etudid=None, title=None):
|
||||
"""HTML img tag for the photo, in full size.
|
||||
Full-size images are always stored locally in the filesystem.
|
||||
They are the original uploaded images, converted in jpeg.
|
||||
"""
|
||||
return etud_photo_html(
|
||||
etud=etud, etudid=etudid, title=title, size="orig", REQUEST=REQUEST
|
||||
)
|
||||
return etud_photo_html(etud=etud, etudid=etudid, title=title, size="orig")
|
||||
|
||||
|
||||
def photo_pathname(etud, size="orig"):
|
||||
@ -246,7 +244,10 @@ def store_photo(etud, data):
|
||||
filesize = len(data)
|
||||
if filesize < 10 or filesize > MAX_FILE_SIZE:
|
||||
return 0, "Fichier image de taille invalide ! (%d)" % filesize
|
||||
filename = save_image(etud["etudid"], data)
|
||||
try:
|
||||
filename = save_image(etud["etudid"], data)
|
||||
except PIL.UnidentifiedImageError:
|
||||
raise ScoGenError(msg="Fichier d'image invalide ou non format non supporté")
|
||||
# update database:
|
||||
etud["photo_filename"] = filename
|
||||
etud["foto"] = None
|
||||
@ -260,7 +261,7 @@ def store_photo(etud, data):
|
||||
return 1, "ok"
|
||||
|
||||
|
||||
def suppress_photo(etud, REQUEST=None):
|
||||
def suppress_photo(etud):
|
||||
"""Suppress a photo"""
|
||||
log("suppress_photo etudid=%s" % etud["etudid"])
|
||||
rel_path = photo_pathname(etud)
|
||||
@ -278,8 +279,7 @@ def suppress_photo(etud, REQUEST=None):
|
||||
log("removing file %s" % filename)
|
||||
os.remove(filename)
|
||||
# 3- log
|
||||
if REQUEST:
|
||||
logdb(cnx, method="changePhoto", msg="suppression", etudid=etud["etudid"])
|
||||
logdb(cnx, method="changePhoto", msg="suppression", etudid=etud["etudid"])
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@ -298,6 +298,7 @@ def save_image(etudid, data):
|
||||
filename = get_new_filename(etudid)
|
||||
path = os.path.join(PHOTO_DIR, filename)
|
||||
log("saving %dx%d jpeg to %s" % (img.size[0], img.size[1], path))
|
||||
img = img.convert("RGB")
|
||||
img.save(path + IMAGE_EXT, format="JPEG", quality=92)
|
||||
# resize:
|
||||
img = scale_height(img)
|
||||
@ -341,7 +342,7 @@ def find_new_dir():
|
||||
|
||||
def copy_portal_photo_to_fs(etud):
|
||||
"""Copy the photo from portal (distant website) to local fs.
|
||||
Returns rel. path or None if copy failed, with a diagnotic message
|
||||
Returns rel. path or None if copy failed, with a diagnostic message
|
||||
"""
|
||||
sco_etud.format_etud_ident(etud)
|
||||
url = photo_portal_url(etud)
|
||||
@ -353,11 +354,12 @@ def copy_portal_photo_to_fs(etud):
|
||||
log("copy_portal_photo_to_fs: getting %s" % url)
|
||||
r = requests.get(url, timeout=portal_timeout)
|
||||
except:
|
||||
log("download failed: exception:\n%s" % traceback.format_exc())
|
||||
log("called from:\n" + "".join(traceback.format_stack()))
|
||||
# log("download failed: exception:\n%s" % traceback.format_exc())
|
||||
# log("called from:\n" + "".join(traceback.format_stack()))
|
||||
log("copy_portal_photo_to_fs: error.")
|
||||
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
|
||||
if r.status_code != 200:
|
||||
log("download failed")
|
||||
log(f"copy_portal_photo_to_fs: download failed {r.status_code }")
|
||||
return None, "%s: erreur chargement de %s" % (etud["nomprenom"], url)
|
||||
data = r.content # image bytes
|
||||
try:
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ Recapitule tous les semestres validés dans une feuille excel.
|
||||
"""
|
||||
import collections
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_abs
|
||||
@ -164,7 +164,7 @@ def _getEtudInfoGroupes(group_ids, etat=None):
|
||||
return etuds
|
||||
|
||||
|
||||
def formsemestre_poursuite_report(formsemestre_id, format="html", REQUEST=None):
|
||||
def formsemestre_poursuite_report(formsemestre_id, format="html"):
|
||||
"""Table avec informations "poursuite" """
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
etuds = _getEtudInfoGroupes([sco_groups.get_default_group(formsemestre_id)])
|
||||
@ -211,12 +211,11 @@ def formsemestre_poursuite_report(formsemestre_id, format="html", REQUEST=None):
|
||||
)
|
||||
tab.caption = "Récapitulatif %s." % sem["titreannee"]
|
||||
tab.html_caption = "Récapitulatif %s." % sem["titreannee"]
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
return tab.make_page(
|
||||
title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
|
||||
init_qtip=True,
|
||||
javascripts=["js/etud_info.js"],
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=True,
|
||||
)
|
||||
|
@ -77,7 +77,7 @@ sinon, elle ne concerne que le semestre indiqué.
|
||||
- avoir un mapping (read only) de toutes les valeurs:
|
||||
sco_preferences.SemPreferences(formsemestre_id)
|
||||
- editer les preferences globales:
|
||||
sco_preferences.get_base_preferences(self).edit(REQUEST=REQUEST)
|
||||
sco_preferences.get_base_preferences(self).edit()
|
||||
- editer les preferences d'un semestre:
|
||||
SemPreferences(formsemestre_id).edit()
|
||||
|
||||
@ -111,7 +111,8 @@ get_base_preferences(formsemestre_id)
|
||||
|
||||
"""
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.models import Departement
|
||||
from app.scodoc import sco_cache
|
||||
@ -180,7 +181,7 @@ def _convert_pref_type(p, pref_spec):
|
||||
|
||||
def _get_pref_default_value_from_config(name, pref_spec):
|
||||
"""get default value store in application level config.
|
||||
If not found, use defalut value hardcoded in pref_spec.
|
||||
If not found, use default value hardcoded in pref_spec.
|
||||
"""
|
||||
# XXX va changer avec la nouvelle base
|
||||
# search in scu.CONFIG
|
||||
@ -1408,7 +1409,7 @@ class BasePreferences(object):
|
||||
{
|
||||
"initvalue": 1,
|
||||
"title": "Indique si les bulletins sont publiés",
|
||||
"explanation": "décocher si vous n'avez pas de portal étudiant publiant les bulletins",
|
||||
"explanation": "décocher si vous n'avez pas de portail étudiant publiant les bulletins",
|
||||
"input_type": "boolcheckbox",
|
||||
"labels": ["non", "oui"],
|
||||
"category": "bul",
|
||||
@ -1891,7 +1892,7 @@ class BasePreferences(object):
|
||||
|
||||
def get(self, formsemestre_id, name):
|
||||
"""Returns preference value.
|
||||
If global_lookup, when no value defined for this semestre, returns global value.
|
||||
when no value defined for this semestre, returns global value.
|
||||
"""
|
||||
params = {
|
||||
"dept_id": self.dept_id,
|
||||
@ -1901,7 +1902,7 @@ class BasePreferences(object):
|
||||
cnx = ndb.GetDBConnexion()
|
||||
plist = self._editor.list(cnx, params)
|
||||
if not plist:
|
||||
del params["formsemestre_id"]
|
||||
params["formsemestre_id"] = None
|
||||
plist = self._editor.list(cnx, params)
|
||||
if not plist:
|
||||
return self.default[name]
|
||||
@ -2013,7 +2014,7 @@ class BasePreferences(object):
|
||||
self._editor.delete(cnx, pdb[0]["pref_id"])
|
||||
sco_cache.invalidate_formsemestre() # > modif preferences
|
||||
|
||||
def edit(self, REQUEST):
|
||||
def edit(self):
|
||||
"""HTML dialog: edit global preferences"""
|
||||
from app.scodoc import html_sco_header
|
||||
|
||||
@ -2022,15 +2023,17 @@ class BasePreferences(object):
|
||||
html_sco_header.sco_header(page_title="Préférences"),
|
||||
"<h2>Préférences globales pour %s</h2>" % scu.ScoURL(),
|
||||
f"""<p><a href="{url_for("scolar.config_logos", scodoc_dept=g.scodoc_dept)
|
||||
}">modification des logos du département (pour documents pdf)</a></p>""",
|
||||
}">modification des logos du département (pour documents pdf)</a></p>"""
|
||||
if current_user.is_administrator()
|
||||
else "",
|
||||
"""<p class="help">Ces paramètres s'appliquent par défaut à tous les semestres, sauf si ceux-ci définissent des valeurs spécifiques.</p>
|
||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||
""",
|
||||
]
|
||||
form = self.build_tf_form()
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
form,
|
||||
initvalues=self.prefs[None],
|
||||
submitlabel="Enregistrer les modifications",
|
||||
@ -2140,7 +2143,7 @@ class SemPreferences(object):
|
||||
return self.base_prefs.is_global(self.formsemestre_id, name)
|
||||
|
||||
# The dialog
|
||||
def edit(self, categories=[], REQUEST=None):
|
||||
def edit(self, categories=[]):
|
||||
"""Dialog to edit semestre preferences in given categories"""
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_formsemestre
|
||||
@ -2151,7 +2154,7 @@ class SemPreferences(object):
|
||||
) # a bug !
|
||||
sem = sco_formsemestre.get_formsemestre(self.formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(REQUEST, "Préférences du semestre", sem),
|
||||
html_sco_header.html_sem_header("Préférences du semestre", sem),
|
||||
"""
|
||||
<p class="help">Les paramètres définis ici ne s'appliqueront qu'à ce semestre.</p>
|
||||
<p class="msg">Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !</p>
|
||||
@ -2194,8 +2197,8 @@ function set_global_pref(el, pref_name) {
|
||||
form.append(("destination", {"input_type": "hidden"}))
|
||||
form.append(("formsemestre_id", {"input_type": "hidden"}))
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
form,
|
||||
initvalues=self,
|
||||
cssclass="sco_pref",
|
||||
@ -2245,7 +2248,7 @@ function set_global_pref(el, pref_name) {
|
||||
return flask.redirect(dest_url + "&head_message=Préférences modifiées")
|
||||
elif destination == "again":
|
||||
return flask.redirect(
|
||||
REQUEST.URL0 + "?formsemestre_id=" + str(self.formsemestre_id)
|
||||
request.base_url + "?formsemestre_id=" + str(self.formsemestre_id)
|
||||
)
|
||||
elif destination == "global":
|
||||
return flask.redirect(scu.ScoURL() + "/edit_preferences")
|
||||
@ -2253,7 +2256,7 @@ function set_global_pref(el, pref_name) {
|
||||
|
||||
#
|
||||
def doc_preferences():
|
||||
""" Liste les preferences en MarkDown, pour la documentation"""
|
||||
"""Liste les preferences en MarkDown, pour la documentation"""
|
||||
L = []
|
||||
for cat, cat_descr in PREF_CATEGORIES:
|
||||
L.append([""])
|
||||
|
@ -31,6 +31,9 @@ import time
|
||||
|
||||
from openpyxl.styles.numbers import FORMAT_NUMBER_00
|
||||
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_abs
|
||||
from app.scodoc import sco_groups
|
||||
@ -45,7 +48,7 @@ from app.scodoc import sco_preferences
|
||||
from app.scodoc.sco_excel import ScoExcelSheet
|
||||
|
||||
|
||||
def feuille_preparation_jury(formsemestre_id, REQUEST):
|
||||
def feuille_preparation_jury(formsemestre_id):
|
||||
"Feuille excel pour preparation des jurys"
|
||||
nt = sco_cache.NotesTableCache.get(
|
||||
formsemestre_id
|
||||
@ -318,9 +321,14 @@ def feuille_preparation_jury(formsemestre_id, REQUEST):
|
||||
% (
|
||||
sco_version.SCONAME,
|
||||
time.strftime("%d/%m/%Y"),
|
||||
REQUEST.BASE0,
|
||||
REQUEST.AUTHENTICATED_USER,
|
||||
request.url_root,
|
||||
current_user,
|
||||
)
|
||||
)
|
||||
xls = ws.generate_standalone()
|
||||
return sco_excel.send_excel_file(REQUEST, xls, f"PrepaJury{sn}{scu.XLSX_SUFFIX}")
|
||||
xls = ws.generate()
|
||||
return scu.send_file(
|
||||
xls,
|
||||
f"PrepaJury{sn}",
|
||||
scu.XLSX_SUFFIX,
|
||||
mime=scu.XLSX_MIMETYPE,
|
||||
)
|
||||
|
@ -52,7 +52,7 @@ from reportlab.platypus import Paragraph
|
||||
from reportlab.lib import styles
|
||||
|
||||
import flask
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -495,7 +495,7 @@ def pvjury_table(
|
||||
return lines, titles, columns_ids
|
||||
|
||||
|
||||
def formsemestre_pvjury(formsemestre_id, format="html", publish=True, REQUEST=None):
|
||||
def formsemestre_pvjury(formsemestre_id, format="html", publish=True):
|
||||
"""Page récapitulant les décisions de jury
|
||||
dpv: result of dict_pvjury
|
||||
"""
|
||||
@ -535,13 +535,11 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True, REQUEST=No
|
||||
return tab.make_page(
|
||||
format=format,
|
||||
with_html_headers=False,
|
||||
REQUEST=REQUEST,
|
||||
publish=publish,
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Décisions du jury pour le semestre",
|
||||
sem,
|
||||
init_qtip=True,
|
||||
@ -599,7 +597,7 @@ def formsemestre_pvjury(formsemestre_id, format="html", publish=True, REQUEST=No
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=None):
|
||||
def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None):
|
||||
"""Generation PV jury en PDF: saisie des paramètres
|
||||
Si etudid, PV pour un seul etudiant. Sinon, tout les inscrits au groupe indiqué.
|
||||
"""
|
||||
@ -628,7 +626,6 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Edition du PV de jury %s" % etuddescr,
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
@ -658,8 +655,8 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
||||
else:
|
||||
menu_choix_groupe = "" # un seul etudiant à editer
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
method="get",
|
||||
@ -707,7 +704,7 @@ def formsemestre_pvjury_pdf(formsemestre_id, group_ids=[], etudid=None, REQUEST=
|
||||
else:
|
||||
groups_filename = ""
|
||||
filename = "PV-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
def descrform_pvjury(sem):
|
||||
@ -793,7 +790,7 @@ def descrform_pvjury(sem):
|
||||
]
|
||||
|
||||
|
||||
def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=None):
|
||||
def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[]):
|
||||
"Lettres avis jury en PDF"
|
||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||
if not group_ids:
|
||||
@ -806,8 +803,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Edition des lettres individuelles",
|
||||
"Édition des lettres individuelles",
|
||||
sem=sem,
|
||||
javascripts=sco_groups_view.JAVASCRIPTS,
|
||||
cssstyles=sco_groups_view.CSSSTYLES,
|
||||
@ -827,8 +823,8 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
cancelbutton="Annuler",
|
||||
method="POST",
|
||||
@ -868,7 +864,7 @@ def formsemestre_lettres_individuelles(formsemestre_id, group_ids=[], REQUEST=No
|
||||
dt = time.strftime("%Y-%m-%d")
|
||||
groups_filename = "-" + groups_infos.groups_filename
|
||||
filename = "lettres-%s%s-%s.pdf" % (sem["titre_num"], groups_filename, dt)
|
||||
return scu.sendPDFFile(REQUEST, pdfdoc, filename)
|
||||
return scu.sendPDFFile(pdfdoc, filename)
|
||||
|
||||
|
||||
def descrform_lettres_individuelles():
|
||||
|
@ -27,10 +27,14 @@
|
||||
|
||||
"""Tableau recapitulatif des notes d'un semestre
|
||||
"""
|
||||
import time
|
||||
import datetime
|
||||
import json
|
||||
import time
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from flask import request
|
||||
from flask import make_response
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app import log
|
||||
from app.scodoc import html_sco_header
|
||||
@ -65,7 +69,6 @@ def formsemestre_recapcomplet(
|
||||
rank_partition_id=None, # si None, calcul rang global
|
||||
pref_override=True, # si vrai, les prefs ont la priorite sur le param hidebac
|
||||
force_publishing=True, # publie les XML/JSON meme si bulletins non publiés
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Page récapitulant les notes d'un semestre.
|
||||
Grand tableau récapitulatif avec toutes les notes de modules
|
||||
@ -98,9 +101,9 @@ def formsemestre_recapcomplet(
|
||||
javascripts=["libjs/sorttable.js", "js/etud_info.js"],
|
||||
),
|
||||
sco_formsemestre_status.formsemestre_status_head(
|
||||
formsemestre_id=formsemestre_id, REQUEST=REQUEST
|
||||
formsemestre_id=formsemestre_id
|
||||
),
|
||||
'<form name="f" method="get" action="%s">' % REQUEST.URL0,
|
||||
'<form name="f" method="get" action="%s">' % request.base_url,
|
||||
'<input type="hidden" name="formsemestre_id" value="%s"></input>'
|
||||
% formsemestre_id,
|
||||
'<input type="hidden" name="pref_override" value="0"></input>',
|
||||
@ -144,22 +147,22 @@ def formsemestre_recapcomplet(
|
||||
if hidebac:
|
||||
H.append("checked")
|
||||
H.append(""" >cacher bac</input>""")
|
||||
if tabformat == "xml":
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
H.append(
|
||||
do_formsemestre_recapcomplet(
|
||||
REQUEST,
|
||||
formsemestre_id,
|
||||
format=tabformat,
|
||||
hidemodules=hidemodules,
|
||||
hidebac=hidebac,
|
||||
modejury=modejury,
|
||||
sortcol=sortcol,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
rank_partition_id=rank_partition_id,
|
||||
force_publishing=force_publishing,
|
||||
)
|
||||
data = do_formsemestre_recapcomplet(
|
||||
formsemestre_id,
|
||||
format=tabformat,
|
||||
hidemodules=hidemodules,
|
||||
hidebac=hidebac,
|
||||
modejury=modejury,
|
||||
sortcol=sortcol,
|
||||
xml_with_decisions=xml_with_decisions,
|
||||
rank_partition_id=rank_partition_id,
|
||||
force_publishing=force_publishing,
|
||||
)
|
||||
if tabformat == "xml":
|
||||
response = make_response(data)
|
||||
response.headers["Content-Type"] = scu.XML_MIMETYPE
|
||||
return response
|
||||
H.append(data)
|
||||
|
||||
if not isFile:
|
||||
H.append("</form>")
|
||||
@ -197,7 +200,6 @@ def formsemestre_recapcomplet(
|
||||
|
||||
|
||||
def do_formsemestre_recapcomplet(
|
||||
REQUEST=None,
|
||||
formsemestre_id=None,
|
||||
format="html", # html, xml, xls, xlsall, json
|
||||
hidemodules=False, # ne pas montrer les modules (ignoré en XML)
|
||||
@ -227,11 +229,14 @@ def do_formsemestre_recapcomplet(
|
||||
if format == "xml" or format == "html":
|
||||
return data
|
||||
elif format == "csv":
|
||||
return scu.sendCSVFile(REQUEST, data, filename)
|
||||
elif format[:3] == "xls":
|
||||
return sco_excel.send_excel_file(REQUEST, data, filename)
|
||||
return scu.send_file(data, filename=filename, mime=scu.CSV_MIMETYPE)
|
||||
elif format.startswith("xls") or format.startswith("xlsx"):
|
||||
return scu.send_file(data, filename=filename, mime=scu.XLSX_MIMETYPE)
|
||||
elif format == "json":
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
js = json.dumps(data, indent=1, cls=scu.ScoDocJSONEncoder)
|
||||
return scu.send_file(
|
||||
js, filename=filename, suffix=scu.JSON_SUFFIX, mime=scu.JSON_MIMETYPE
|
||||
)
|
||||
else:
|
||||
raise ValueError("unknown format %s" % format)
|
||||
|
||||
@ -953,7 +958,7 @@ def _formsemestre_recapcomplet_json(
|
||||
return J, "", "json"
|
||||
|
||||
|
||||
def formsemestres_bulletins(annee_scolaire, REQUEST=None):
|
||||
def formsemestres_bulletins(annee_scolaire):
|
||||
"""Tous les bulletins des semestres publiés des semestres de l'année indiquée.
|
||||
:param annee_scolaire(int): année de début de l'année scoalaire
|
||||
:returns: JSON
|
||||
@ -967,4 +972,4 @@ def formsemestres_bulletins(annee_scolaire, REQUEST=None):
|
||||
)
|
||||
jslist.append(J)
|
||||
|
||||
return scu.sendJSON(REQUEST, jslist)
|
||||
return scu.sendJSON(jslist)
|
||||
|
@ -37,7 +37,7 @@ import time
|
||||
import datetime
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
import pydot
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -210,7 +210,6 @@ def _results_by_category(
|
||||
def formsemestre_report(
|
||||
formsemestre_id,
|
||||
etuds,
|
||||
REQUEST=None,
|
||||
category="bac",
|
||||
result="codedecision",
|
||||
category_name="",
|
||||
@ -247,32 +246,31 @@ def formsemestre_report(
|
||||
sem["titreannee"],
|
||||
)
|
||||
tab.html_caption = "Répartition des résultats par %s." % category_name
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=on"
|
||||
return tab
|
||||
|
||||
|
||||
# def formsemestre_report_bacs(formsemestre_id, format='html', REQUEST=None):
|
||||
# def formsemestre_report_bacs(formsemestre_id, format='html'):
|
||||
# """
|
||||
# Tableau sur résultats par type de bac
|
||||
# """
|
||||
# sem = sco_formsemestre.get_formsemestre( formsemestre_id)
|
||||
# title = 'Statistiques bacs ' + sem['titreannee']
|
||||
# etuds = formsemestre_etuds_stats(sem)
|
||||
# tab = formsemestre_report(formsemestre_id, etuds, REQUEST=REQUEST,
|
||||
# tab = formsemestre_report(formsemestre_id, etuds,
|
||||
# category='bac', result='codedecision',
|
||||
# category_name='Bac',
|
||||
# title=title)
|
||||
# return tab.make_page(
|
||||
# title = """<h2>Résultats de <a href="formsemestre_status?formsemestre_id=%(formsemestre_id)s">%(titreannee)s</a></h2>""" % sem,
|
||||
# format=format, page_title = title, REQUEST=REQUEST )
|
||||
# format=format, page_title = title)
|
||||
|
||||
|
||||
def formsemestre_report_counts(
|
||||
formsemestre_id,
|
||||
format="html",
|
||||
REQUEST=None,
|
||||
category="bac",
|
||||
result="codedecision",
|
||||
allkeys=False,
|
||||
@ -288,7 +286,6 @@ def formsemestre_report_counts(
|
||||
tab = formsemestre_report(
|
||||
formsemestre_id,
|
||||
etuds,
|
||||
REQUEST=REQUEST,
|
||||
category=category,
|
||||
result=result,
|
||||
category_name=category_name,
|
||||
@ -322,7 +319,7 @@ def formsemestre_report_counts(
|
||||
F = [
|
||||
"""<form name="f" method="get" action="%s"><p>
|
||||
Colonnes: <select name="result" onchange="document.f.submit()">"""
|
||||
% REQUEST.URL0
|
||||
% request.base_url
|
||||
]
|
||||
for k in keys:
|
||||
if k == result:
|
||||
@ -355,7 +352,6 @@ def formsemestre_report_counts(
|
||||
t = tab.make_page(
|
||||
title="""<h2 class="formsemestre">Comptes croisés</h2>""",
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_html_headers=False,
|
||||
)
|
||||
if format != "html":
|
||||
@ -693,7 +689,6 @@ def formsemestre_suivi_cohorte(
|
||||
civilite=None,
|
||||
statut="",
|
||||
only_primo=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Affiche suivi cohortes par numero de semestre"""
|
||||
annee_bac = str(annee_bac)
|
||||
@ -718,15 +713,15 @@ def formsemestre_suivi_cohorte(
|
||||
)
|
||||
tab.base_url = (
|
||||
"%s?formsemestre_id=%s&percent=%s&bac=%s&bacspecialite=%s&civilite=%s"
|
||||
% (REQUEST.URL0, formsemestre_id, percent, bac, bacspecialite, civilite)
|
||||
% (request.base_url, formsemestre_id, percent, bac, bacspecialite, civilite)
|
||||
)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=on"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
|
||||
base_url = REQUEST.URL0
|
||||
base_url = request.base_url
|
||||
burl = "%s?formsemestre_id=%s&bac=%s&bacspecialite=%s&civilite=%s&statut=%s" % (
|
||||
base_url,
|
||||
formsemestre_id,
|
||||
@ -756,7 +751,6 @@ def formsemestre_suivi_cohorte(
|
||||
"""<h2 class="formsemestre">Suivi cohorte: devenir des étudiants de ce semestre</h2>""",
|
||||
_gen_form_selectetuds(
|
||||
formsemestre_id,
|
||||
REQUEST=REQUEST,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
@ -780,7 +774,6 @@ def formsemestre_suivi_cohorte(
|
||||
|
||||
def _gen_form_selectetuds(
|
||||
formsemestre_id,
|
||||
REQUEST=None,
|
||||
percent=None,
|
||||
only_primo=None,
|
||||
bac=None,
|
||||
@ -816,7 +809,7 @@ def _gen_form_selectetuds(
|
||||
<p>Bac: <select name="bac" onchange="javascript: submit(this);">
|
||||
<option value="" %s>tous</option>
|
||||
"""
|
||||
% (REQUEST.URL0, selected)
|
||||
% (request.base_url, selected)
|
||||
]
|
||||
for b in bacs:
|
||||
if bac == b:
|
||||
@ -1165,9 +1158,9 @@ def table_suivi_parcours(formsemestre_id, only_primo=False, grouped_parcours=Tru
|
||||
return tab
|
||||
|
||||
|
||||
def tsp_form_primo_group(REQUEST, only_primo, no_grouping, formsemestre_id, format):
|
||||
def tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, format):
|
||||
"""Element de formulaire pour choisir si restriction aux primos entrants et groupement par lycees"""
|
||||
F = ["""<form name="f" method="get" action="%s">""" % REQUEST.URL0]
|
||||
F = ["""<form name="f" method="get" action="%s">""" % request.base_url]
|
||||
if only_primo:
|
||||
checked = 'checked="1"'
|
||||
else:
|
||||
@ -1197,7 +1190,6 @@ def formsemestre_suivi_parcours(
|
||||
format="html",
|
||||
only_primo=False,
|
||||
no_grouping=False,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Effectifs dans les differents parcours possibles."""
|
||||
tab = table_suivi_parcours(
|
||||
@ -1205,17 +1197,15 @@ def formsemestre_suivi_parcours(
|
||||
only_primo=only_primo,
|
||||
grouped_parcours=not no_grouping,
|
||||
)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id)
|
||||
tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
|
||||
if only_primo:
|
||||
tab.base_url += "&only_primo=1"
|
||||
if no_grouping:
|
||||
tab.base_url += "&no_grouping=1"
|
||||
t = tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
t = tab.make_page(format=format, with_html_headers=False)
|
||||
if format != "html":
|
||||
return t
|
||||
F = [
|
||||
tsp_form_primo_group(REQUEST, only_primo, no_grouping, formsemestre_id, format)
|
||||
]
|
||||
F = [tsp_form_primo_group(only_primo, no_grouping, formsemestre_id, format)]
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
@ -1405,7 +1395,7 @@ def graph_parcours(
|
||||
if len(edges[(src_id, dst_id)]) <= MAX_ETUD_IN_DESCR:
|
||||
etud_descr = _descr_etud_set(edges[(src_id, dst_id)])
|
||||
bubbles[src_id + ":" + dst_id] = etud_descr
|
||||
e.set_URL("__xxxetudlist__?" + src_id + ":" + dst_id)
|
||||
e.set_URL(f"__xxxetudlist__?{src_id}:{dst_id}")
|
||||
# Genere graphe
|
||||
_, path = tempfile.mkstemp(".gr")
|
||||
g.write(path=path, format=format)
|
||||
@ -1466,7 +1456,6 @@ def formsemestre_graph_parcours(
|
||||
civilite="",
|
||||
statut="",
|
||||
allkeys=False, # unused
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Graphe suivi cohortes"""
|
||||
annee_bac = str(annee_bac)
|
||||
@ -1492,7 +1481,7 @@ def formsemestre_graph_parcours(
|
||||
statut=statut,
|
||||
)
|
||||
filename = scu.make_filename("flux " + sem["titreannee"])
|
||||
return scu.sendPDFFile(REQUEST, doc, filename + ".pdf")
|
||||
return scu.sendPDFFile(doc, filename + ".pdf")
|
||||
elif format == "png":
|
||||
#
|
||||
(
|
||||
@ -1513,12 +1502,13 @@ def formsemestre_graph_parcours(
|
||||
civilite=civilite,
|
||||
statut=statut,
|
||||
)
|
||||
filename = scu.make_filename("flux " + sem["titreannee"])
|
||||
REQUEST.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
return scu.send_file(
|
||||
doc,
|
||||
filename="flux " + sem["titreannee"],
|
||||
suffix=".png",
|
||||
attached=True,
|
||||
mime="image/png",
|
||||
)
|
||||
REQUEST.RESPONSE.setHeader("content-type", "image/png")
|
||||
return doc
|
||||
elif format == "html":
|
||||
url_kw = {
|
||||
"scodoc_dept": g.scodoc_dept,
|
||||
@ -1558,7 +1548,6 @@ def formsemestre_graph_parcours(
|
||||
"<p>%d étudiants sélectionnés</p>" % len(etuds),
|
||||
_gen_form_selectetuds(
|
||||
formsemestre_id,
|
||||
REQUEST=REQUEST,
|
||||
only_primo=only_primo,
|
||||
bac=bac,
|
||||
bacspecialite=bacspecialite,
|
||||
|
@ -9,48 +9,50 @@ from app.scodoc.sco_permissions import Permission as p
|
||||
SCO_ROLES_DEFAULTS = {
|
||||
"Observateur": (p.ScoObservateur,),
|
||||
"Ens": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoEnsView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoAbsChange,
|
||||
p.ScoEnsView,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
"Secr": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoAbsChange,
|
||||
p.ScoEditApo,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
# Admin est le chef du département, pas le "super admin"
|
||||
# on doit donc lister toutes ses permissions:
|
||||
"Admin": (
|
||||
p.ScoObservateur,
|
||||
p.ScoView,
|
||||
p.ScoEnsView,
|
||||
p.ScoUsersView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoAbsChange,
|
||||
p.ScoAbsAddBillet,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoAbsChange,
|
||||
p.ScoChangeFormation,
|
||||
p.ScoEditFormationTags,
|
||||
p.ScoEditAllNotes,
|
||||
p.ScoChangePreferences,
|
||||
p.ScoEditAllEvals,
|
||||
p.ScoImplement,
|
||||
p.ScoEditAllNotes,
|
||||
p.ScoEditApo,
|
||||
p.ScoEditFormationTags,
|
||||
p.ScoEnsView,
|
||||
p.ScoEntrepriseChange,
|
||||
p.ScoEntrepriseView,
|
||||
p.ScoEtudAddAnnotations,
|
||||
p.ScoEtudChangeAdr,
|
||||
p.ScoEtudChangeGroups,
|
||||
p.ScoEtudInscrit,
|
||||
p.ScoImplement,
|
||||
p.ScoObservateur,
|
||||
p.ScoUsersAdmin,
|
||||
p.ScoChangePreferences,
|
||||
p.ScoUsersView,
|
||||
p.ScoView,
|
||||
),
|
||||
# RespPE est le responsable poursuites d'études
|
||||
# il peut ajouter des tags sur les formations:
|
||||
|
@ -35,7 +35,7 @@ import datetime
|
||||
import psycopg2
|
||||
|
||||
import flask
|
||||
from flask import g, url_for
|
||||
from flask import g, url_for, request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -164,13 +164,14 @@ def _check_notes(notes, evaluation, mod):
|
||||
return L, invalids, withoutnotes, absents, tosuppress
|
||||
|
||||
|
||||
def do_evaluation_upload_xls(REQUEST):
|
||||
def do_evaluation_upload_xls():
|
||||
"""
|
||||
Soumission d'un fichier XLS (evaluation_id, notefile)
|
||||
"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
evaluation_id = int(REQUEST.form["evaluation_id"])
|
||||
comment = REQUEST.form["comment"]
|
||||
authuser = current_user
|
||||
vals = scu.get_request_args()
|
||||
evaluation_id = int(vals["evaluation_id"])
|
||||
comment = vals["comment"]
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[
|
||||
0
|
||||
@ -181,7 +182,7 @@ def do_evaluation_upload_xls(REQUEST):
|
||||
# XXX imaginer un redirect + msg erreur
|
||||
raise AccessDenied("Modification des notes impossible pour %s" % authuser)
|
||||
#
|
||||
diag, lines = sco_excel.excel_file_to_list(REQUEST.form["notefile"])
|
||||
diag, lines = sco_excel.excel_file_to_list(vals["notefile"])
|
||||
try:
|
||||
if not lines:
|
||||
raise InvalidNoteValue()
|
||||
@ -287,7 +288,6 @@ def do_evaluation_upload_xls(REQUEST):
|
||||
|
||||
def do_evaluation_set_missing(evaluation_id, value, dialog_confirmed=False):
|
||||
"""Initialisation des notes manquantes"""
|
||||
# ? evaluation_id = REQUEST.form["evaluation_id"]
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.do_moduleimpl_withmodule_list(moduleimpl_id=E["moduleimpl_id"])[
|
||||
0
|
||||
@ -494,9 +494,10 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
}
|
||||
ndb.quote_dict(aa)
|
||||
cursor.execute(
|
||||
"""INSERT INTO notes_notes
|
||||
(etudid,evaluation_id,value,comment,date,uid)
|
||||
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)""",
|
||||
"""INSERT INTO notes_notes
|
||||
(etudid, evaluation_id, value, comment, date, uid)
|
||||
VALUES (%(etudid)s,%(evaluation_id)s,%(value)s,%(comment)s,%(date)s,%(uid)s)
|
||||
""",
|
||||
aa,
|
||||
)
|
||||
changed = True
|
||||
@ -515,10 +516,10 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
# recopie l'ancienne note dans notes_notes_log, puis update
|
||||
if do_it:
|
||||
cursor.execute(
|
||||
"""INSERT INTO notes_notes_log
|
||||
"""INSERT INTO notes_notes_log
|
||||
(etudid,evaluation_id,value,comment,date,uid)
|
||||
SELECT etudid, evaluation_id, value, comment, date, uid
|
||||
FROM notes_notes
|
||||
FROM notes_notes
|
||||
WHERE etudid=%(etudid)s
|
||||
and evaluation_id=%(evaluation_id)s
|
||||
""",
|
||||
@ -536,8 +537,8 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
if value != scu.NOTES_SUPPRESS:
|
||||
if do_it:
|
||||
cursor.execute(
|
||||
"""UPDATE notes_notes
|
||||
SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
|
||||
"""UPDATE notes_notes
|
||||
SET value=%(value)s, comment=%(comment)s, date=%(date)s, uid=%(uid)s
|
||||
WHERE etudid = %(etudid)s
|
||||
and evaluation_id = %(evaluation_id)s
|
||||
""",
|
||||
@ -550,7 +551,7 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
% (evaluation_id, etudid, oldval)
|
||||
)
|
||||
cursor.execute(
|
||||
"""DELETE FROM notes_notes
|
||||
"""DELETE FROM notes_notes
|
||||
WHERE etudid = %(etudid)s
|
||||
AND evaluation_id = %(evaluation_id)s
|
||||
""",
|
||||
@ -587,20 +588,19 @@ def _notes_add(user, evaluation_id: int, notes: list, comment=None, do_it=True):
|
||||
return nb_changed, nb_suppress, existing_decisions
|
||||
|
||||
|
||||
def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
def saisie_notes_tableur(evaluation_id, group_ids=[]):
|
||||
"""Saisie des notes via un fichier Excel"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authusername = str(authuser)
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
E = evals[0]
|
||||
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
||||
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>"
|
||||
% current_user.user_name
|
||||
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
|
||||
avez l'autorisation d'effectuer cette opération)</p>
|
||||
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
|
||||
@ -657,8 +657,8 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
)
|
||||
|
||||
nf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("evaluation_id", {"default": evaluation_id, "input_type": "hidden"}),
|
||||
(
|
||||
@ -687,7 +687,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
elif nf[0] == -1:
|
||||
H.append("<p>Annulation</p>")
|
||||
elif nf[0] == 1:
|
||||
updiag = do_evaluation_upload_xls(REQUEST)
|
||||
updiag = do_evaluation_upload_xls()
|
||||
if updiag[0]:
|
||||
H.append(updiag[1])
|
||||
H.append(
|
||||
@ -711,7 +711,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
#
|
||||
H.append("""</div><h3>Autres opérations</h3><ul>""")
|
||||
if sco_permissions_check.can_edit_notes(
|
||||
REQUEST.AUTHENTICATED_USER, E["moduleimpl_id"], allow_ens=False
|
||||
current_user, E["moduleimpl_id"], allow_ens=False
|
||||
):
|
||||
H.append(
|
||||
"""
|
||||
@ -759,7 +759,7 @@ def saisie_notes_tableur(evaluation_id, group_ids=[], REQUEST=None):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def feuille_saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
def feuille_saisie_notes(evaluation_id, group_ids=[]):
|
||||
"""Document Excel pour saisie notes dans l'évaluation et les groupes indiqués"""
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
@ -829,7 +829,8 @@ def feuille_saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
|
||||
filename = "notes_%s_%s.xlsx" % (evalname, gr_title_filename)
|
||||
xls = sco_excel.excel_feuille_saisie(E, sem["titreannee"], description, lines=L)
|
||||
return sco_excel.send_excel_file(REQUEST, xls, filename)
|
||||
return scu.send_file(xls, filename, scu.XLSX_SUFFIX, mime=scu.XLSX_MIMETYPE)
|
||||
# return sco_excel.send_excel_file(xls, filename)
|
||||
|
||||
|
||||
def has_existing_decision(M, E, etudid):
|
||||
@ -856,11 +857,9 @@ def has_existing_decision(M, E, etudid):
|
||||
# Nouveau formulaire saisie notes (2016)
|
||||
|
||||
|
||||
def saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
def saisie_notes(evaluation_id, group_ids=[]):
|
||||
"""Formulaire saisie notes d'une évaluation pour un groupe"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authusername = str(authuser)
|
||||
|
||||
group_ids = [int(group_id) for group_id in group_ids]
|
||||
evals = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})
|
||||
if not evals:
|
||||
raise ScoValueError("invalid evaluation_id")
|
||||
@ -871,10 +870,11 @@ def saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
formsemestre_id = M["formsemestre_id"]
|
||||
# Check access
|
||||
# (admin, respformation, and responsable_id)
|
||||
if not sco_permissions_check.can_edit_notes(authuser, E["moduleimpl_id"]):
|
||||
if not sco_permissions_check.can_edit_notes(current_user, E["moduleimpl_id"]):
|
||||
return (
|
||||
html_sco_header.sco_header()
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>" % authusername
|
||||
+ "<h2>Modification des notes impossible pour %s</h2>"
|
||||
% current_user.user_name
|
||||
+ """<p>(vérifiez que le semestre n'est pas verrouillé et que vous
|
||||
avez l'autorisation d'effectuer cette opération)</p>
|
||||
<p><a href="moduleimpl_status?moduleimpl_id=%s">Continuer</a></p>
|
||||
@ -946,9 +946,7 @@ def saisie_notes(evaluation_id, group_ids=[], REQUEST=None):
|
||||
moduleimpl_id=E["moduleimpl_id"],
|
||||
)
|
||||
|
||||
form = _form_saisie_notes(
|
||||
E, M, groups_infos.group_ids, destination=destination, REQUEST=REQUEST
|
||||
)
|
||||
form = _form_saisie_notes(E, M, groups_infos.group_ids, destination=destination)
|
||||
if form is None:
|
||||
log(f"redirecting to {destination}")
|
||||
return flask.redirect(destination)
|
||||
@ -1039,7 +1037,7 @@ def _get_sorted_etuds(E, etudids, formsemestre_id):
|
||||
return etuds
|
||||
|
||||
|
||||
def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
||||
def _form_saisie_notes(E, M, group_ids, destination=""):
|
||||
"""Formulaire HTML saisie des notes dans l'évaluation E du moduleimpl M
|
||||
pour les groupes indiqués.
|
||||
|
||||
@ -1154,9 +1152,9 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
||||
"attributes": [
|
||||
'class="note%s"' % classdem,
|
||||
disabled_attr,
|
||||
"data-last-saved-value=%s" % e["val"],
|
||||
"data-orig-value=%s" % e["val"],
|
||||
"data-etudid=%s" % etudid,
|
||||
'data-last-saved-value="%s"' % e["val"],
|
||||
'data-orig-value="%s"' % e["val"],
|
||||
'data-etudid="%s"' % etudid,
|
||||
],
|
||||
"template": """<tr%(item_dom_attr)s class="etud_elem """
|
||||
+ " ".join(etud_classes)
|
||||
@ -1181,7 +1179,7 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
||||
|
||||
tf = TF(
|
||||
destination,
|
||||
REQUEST.form,
|
||||
scu.get_request_args(),
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
submitbutton=False,
|
||||
@ -1220,9 +1218,9 @@ def _form_saisie_notes(E, M, group_ids, destination="", REQUEST=None):
|
||||
return None
|
||||
|
||||
|
||||
def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=None):
|
||||
def save_note(etudid=None, evaluation_id=None, value=None, comment=""):
|
||||
"""Enregistre une note (ajax)"""
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
log(
|
||||
"save_note: evaluation_id=%s etudid=%s uid=%s value=%s"
|
||||
% (evaluation_id, etudid, authuser, value)
|
||||
@ -1259,7 +1257,7 @@ def save_note(etudid=None, evaluation_id=None, value=None, comment="", REQUEST=N
|
||||
else:
|
||||
result["history_menu"] = "" # no update needed
|
||||
result["status"] = "ok"
|
||||
return scu.sendJSON(REQUEST, result)
|
||||
return scu.sendJSON(result)
|
||||
|
||||
|
||||
def get_note_history_menu(evaluation_id, etudid):
|
||||
@ -1290,7 +1288,7 @@ def get_note_history_menu(evaluation_id, etudid):
|
||||
nv = "" # ne repete pas la valeur de la note courante
|
||||
else:
|
||||
# ancienne valeur
|
||||
nv = '<span class="histvalue">: %s</span>' % dispnote
|
||||
nv = ": %s" % dispnote
|
||||
first = False
|
||||
if i["comment"]:
|
||||
comment = ' <span class="histcomment">%s</span>' % i["comment"]
|
||||
|
@ -171,7 +171,7 @@ class SemSet(dict):
|
||||
def remove(self, formsemestre_id):
|
||||
ndb.SimpleQuery(
|
||||
"""DELETE FROM notes_semset_formsemestre
|
||||
WHERE id=%(semset_id)s
|
||||
WHERE semset_id=%(semset_id)s
|
||||
AND formsemestre_id=%(formsemestre_id)s
|
||||
""",
|
||||
{"formsemestre_id": formsemestre_id, "semset_id": self.semset_id},
|
||||
@ -418,7 +418,7 @@ def do_semset_remove_sem(semset_id, formsemestre_id):
|
||||
# ----------------------------------------
|
||||
|
||||
|
||||
def semset_page(format="html", REQUEST=None):
|
||||
def semset_page(format="html"):
|
||||
"""Page avec liste semsets:
|
||||
Table avec : date_debut date_fin titre liste des semestres
|
||||
"""
|
||||
@ -468,7 +468,7 @@ def semset_page(format="html", REQUEST=None):
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
if format != "html":
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
page_title = "Ensembles de semestres"
|
||||
H = [
|
||||
|
@ -32,7 +32,7 @@ import time
|
||||
import pprint
|
||||
from operator import itemgetter
|
||||
|
||||
from flask import g, url_for, send_file
|
||||
from flask import g, url_for
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -112,6 +112,7 @@ def formsemestre_synchro_etuds(
|
||||
base_url = url_for(
|
||||
"notes.formsemestre_synchro_etuds",
|
||||
scodoc_dept=g.scodoc_dept,
|
||||
formsemestre_id=formsemestre_id,
|
||||
anneeapogee=anneeapogee or None, # si None, le param n'est pas dans l'URL
|
||||
)
|
||||
|
||||
@ -125,9 +126,13 @@ def formsemestre_synchro_etuds(
|
||||
etuds = etuds.split(",") # vient du form de confirmation
|
||||
elif isinstance(etuds, int):
|
||||
etuds = [etuds]
|
||||
if isinstance(inscrits_without_key, str):
|
||||
if isinstance(inscrits_without_key, int):
|
||||
inscrits_without_key = [inscrits_without_key]
|
||||
elif isinstance(inscrits_without_key, str):
|
||||
inscrits_without_key = inscrits_without_key.split(",")
|
||||
|
||||
elif not isinstance(inscrits_without_key, list):
|
||||
raise ValueError("invalid type for inscrits_without_key")
|
||||
inscrits_without_key = [int(x) for x in inscrits_without_key if x]
|
||||
(
|
||||
etuds_by_cat,
|
||||
a_importer,
|
||||
@ -146,11 +151,11 @@ def formsemestre_synchro_etuds(
|
||||
base_url=base_url,
|
||||
read_only=read_only,
|
||||
)
|
||||
return send_file(
|
||||
return scu.send_file(
|
||||
xls,
|
||||
mimetype=scu.XLS_MIMETYPE,
|
||||
download_name=scu.sanitize_filename(filename + scu.XLSX_SUFFIX),
|
||||
as_attachment=True,
|
||||
mime=scu.XLS_MIMETYPE,
|
||||
filename=filename,
|
||||
suffix=scu.XLSX_SUFFIX,
|
||||
)
|
||||
|
||||
H = [header]
|
||||
@ -199,31 +204,38 @@ def formsemestre_synchro_etuds(
|
||||
)
|
||||
H.append("</ol>")
|
||||
|
||||
if a_desinscrire or a_desinscrire_without_key:
|
||||
if a_desinscrire:
|
||||
H.append("<h3>Etudiants à désinscrire :</h3><ol>")
|
||||
for key in a_desinscrire:
|
||||
etud = sco_etud.get_etud_info(filled=True, code_nip=key)[0]
|
||||
H.append('<li class="desinscription">%(nomprenom)s</li>' % etud)
|
||||
H.append("</ol>")
|
||||
if a_desinscrire_without_key:
|
||||
H.append("<h3>Etudiants à désinscrire (sans code):</h3><ol>")
|
||||
for etudid in a_desinscrire_without_key:
|
||||
etud = inscrits_without_key_all[etudid]
|
||||
sco_etud.format_etud_ident(etud)
|
||||
H.append('<li class="desinscription">%(nomprenom)s</li>' % etud)
|
||||
H.append("</ol>")
|
||||
|
||||
if not a_importer and not a_inscrire and not a_desinscrire:
|
||||
todo = (
|
||||
a_importer or a_inscrire or a_desinscrire or a_desinscrire_without_key
|
||||
)
|
||||
if not todo:
|
||||
H.append("""<h3>Il n'y a rien à modifier !</h3>""")
|
||||
|
||||
H.append(
|
||||
scu.confirm_dialog(
|
||||
dest_url="formsemestre_synchro_etuds",
|
||||
add_headers=False,
|
||||
cancel_url="formsemestre_synchro_etuds?formsemestre_id="
|
||||
+ str(formsemestre_id),
|
||||
OK="Effectuer l'opération",
|
||||
OK="Effectuer l'opération" if todo else "OK",
|
||||
parameters={
|
||||
"formsemestre_id": formsemestre_id,
|
||||
"etuds": ",".join(etuds),
|
||||
"inscrits_without_key": ",".join(inscrits_without_key),
|
||||
"inscrits_without_key": ",".join(
|
||||
[str(x) for x in inscrits_without_key]
|
||||
),
|
||||
"submitted": 1,
|
||||
"anneeapogee": anneeapogee,
|
||||
},
|
||||
@ -317,7 +329,8 @@ def build_page(
|
||||
"""
|
||||
% sem,
|
||||
"""
|
||||
Année Apogée: <select id="anneeapogee" name="anneeapogee" onchange="document.location='formsemestre_synchro_etuds?formsemestre_id=%s&anneeapogee='+document.getElementById('anneeapogee').value">"""
|
||||
Année Apogée: <select id="anneeapogee" name="anneeapogee"
|
||||
onchange="document.location='formsemestre_synchro_etuds?formsemestre_id=%s&anneeapogee='+document.getElementById('anneeapogee').value">"""
|
||||
% (sem["formsemestre_id"]),
|
||||
"\n".join(options),
|
||||
"""
|
||||
@ -430,17 +443,20 @@ def list_synch(sem, anneeapogee=None):
|
||||
return etuds
|
||||
|
||||
#
|
||||
r = {
|
||||
"etuds_ok": {
|
||||
"etuds": set_to_sorted_list(etuds_ok, is_inscrit=True),
|
||||
boites = {
|
||||
"etuds_a_importer": {
|
||||
"etuds": set_to_sorted_list(a_importer, is_inscrit=True, etud_apo=True),
|
||||
"infos": {
|
||||
"id": "etuds_ok",
|
||||
"title": "Etudiants dans Apogée et déjà inscrits",
|
||||
"help": "Ces etudiants sont inscrits dans le semestre ScoDoc et sont présents dans Apogée: tout est donc correct. Décocher les étudiants que vous souhaitez désinscrire.",
|
||||
"id": "etuds_a_importer",
|
||||
"title": "Etudiants dans Apogée à importer",
|
||||
"help": """Ces étudiants sont inscrits dans cette étape Apogée mais ne sont pas connus par ScoDoc:
|
||||
cocher les noms à importer et inscrire puis appuyer sur le bouton "Appliquer".""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"etud_key": EKEY_APO, # clé à stocker dans le formulaire html
|
||||
"filename": "etuds_a_importer",
|
||||
},
|
||||
"nomprenoms": etudsapo_ident,
|
||||
},
|
||||
"etuds_noninscrits": {
|
||||
"etuds": set_to_sorted_list(etuds_noninscrits, is_inscrit=True),
|
||||
@ -453,20 +469,9 @@ def list_synch(sem, anneeapogee=None):
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_non_inscrits",
|
||||
},
|
||||
},
|
||||
"etuds_a_importer": {
|
||||
"etuds": set_to_sorted_list(a_importer, is_inscrit=True, etud_apo=True),
|
||||
"infos": {
|
||||
"id": "etuds_a_importer",
|
||||
"title": "Etudiants dans Apogée à importer",
|
||||
"help": """Ces étudiants sont inscrits dans cette étape Apogée mais ne sont pas connus par ScoDoc: cocher les noms à importer et inscrire puis appuyer sur le bouton "Appliquer".""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_APO, # clé à stocker dans le formulaire html
|
||||
},
|
||||
"nomprenoms": etudsapo_ident,
|
||||
},
|
||||
"etuds_nonapogee": {
|
||||
"etuds": set_to_sorted_list(etuds_nonapogee, is_inscrit=True),
|
||||
"infos": {
|
||||
@ -478,6 +483,7 @@ def list_synch(sem, anneeapogee=None):
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_non_apogee",
|
||||
},
|
||||
},
|
||||
"inscrits_without_key": {
|
||||
@ -489,11 +495,25 @@ def list_synch(sem, anneeapogee=None):
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"checkbox_name": "inscrits_without_key",
|
||||
"filename": "inscrits_without_key",
|
||||
},
|
||||
},
|
||||
"etuds_ok": {
|
||||
"etuds": set_to_sorted_list(etuds_ok, is_inscrit=True),
|
||||
"infos": {
|
||||
"id": "etuds_ok",
|
||||
"title": "Etudiants dans Apogée et déjà inscrits",
|
||||
"help": """Ces etudiants sont inscrits dans le semestre ScoDoc et sont présents dans Apogée:
|
||||
tout est donc correct. Décocher les étudiants que vous souhaitez désinscrire.""",
|
||||
"title_target": "",
|
||||
"with_checkbox": True,
|
||||
"etud_key": EKEY_SCO,
|
||||
"filename": "etuds_inscrits_ok_apo",
|
||||
},
|
||||
},
|
||||
}
|
||||
return (
|
||||
r,
|
||||
boites,
|
||||
a_importer,
|
||||
etuds_noninscrits,
|
||||
inscrits_set,
|
||||
|
@ -199,7 +199,7 @@ class ModuleTag(ScoTag):
|
||||
# API
|
||||
|
||||
|
||||
def module_tag_search(term, REQUEST=None):
|
||||
def module_tag_search(term):
|
||||
"""List all used tag names (for auto-completion)"""
|
||||
# restrict charset to avoid injections
|
||||
if not scu.ALPHANUM_EXP.match(term):
|
||||
@ -214,7 +214,7 @@ def module_tag_search(term, REQUEST=None):
|
||||
)
|
||||
data = [x["title"] for x in r]
|
||||
|
||||
return scu.sendJSON(REQUEST, data)
|
||||
return scu.sendJSON(data)
|
||||
|
||||
|
||||
def module_tag_list(module_id=""):
|
||||
|
@ -44,7 +44,7 @@ from reportlab.lib import colors
|
||||
from PIL import Image as PILImage
|
||||
|
||||
import flask
|
||||
from flask import url_for, g, send_file
|
||||
from flask import url_for, g, send_file, request
|
||||
|
||||
from app import log
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -66,7 +66,6 @@ from app.scodoc import sco_etud
|
||||
|
||||
|
||||
def trombino(
|
||||
REQUEST=None,
|
||||
group_ids=[], # liste des groupes à afficher
|
||||
formsemestre_id=None, # utilisé si pas de groupes selectionné
|
||||
etat=None,
|
||||
@ -83,28 +82,26 @@ def trombino(
|
||||
|
||||
#
|
||||
if format != "html" and not dialog_confirmed:
|
||||
ok, dialog = check_local_photos_availability(
|
||||
groups_infos, REQUEST, format=format
|
||||
)
|
||||
ok, dialog = check_local_photos_availability(groups_infos, format=format)
|
||||
if not ok:
|
||||
return dialog
|
||||
|
||||
if format == "zip":
|
||||
return _trombino_zip(groups_infos)
|
||||
elif format == "pdf":
|
||||
return _trombino_pdf(groups_infos, REQUEST)
|
||||
return _trombino_pdf(groups_infos)
|
||||
elif format == "pdflist":
|
||||
return _listeappel_photos_pdf(groups_infos, REQUEST)
|
||||
return _listeappel_photos_pdf(groups_infos)
|
||||
else:
|
||||
raise Exception("invalid format")
|
||||
# return _trombino_html_header() + trombino_html( group, members, REQUEST=REQUEST) + html_sco_header.sco_footer( REQUEST)
|
||||
# return _trombino_html_header() + trombino_html( group, members) + html_sco_header.sco_footer()
|
||||
|
||||
|
||||
def _trombino_html_header():
|
||||
return html_sco_header.sco_header(javascripts=["js/trombino.js"])
|
||||
|
||||
|
||||
def trombino_html(groups_infos, REQUEST=None):
|
||||
def trombino_html(groups_infos):
|
||||
"HTML snippet for trombino (with title and menu)"
|
||||
menuTrombi = [
|
||||
{
|
||||
@ -150,7 +147,7 @@ def trombino_html(groups_infos, REQUEST=None):
|
||||
% t["etudid"]
|
||||
)
|
||||
if sco_photos.etud_photo_is_local(t, size="small"):
|
||||
foto = sco_photos.etud_photo_html(t, title="", REQUEST=REQUEST)
|
||||
foto = sco_photos.etud_photo_html(t, title="")
|
||||
else: # la photo n'est pas immédiatement dispo
|
||||
foto = (
|
||||
'<span class="unloaded_img" id="%s"><img border="0" height="90" alt="en cours" src="/ScoDoc/static/icons/loading.jpg"/></span>'
|
||||
@ -183,7 +180,7 @@ def trombino_html(groups_infos, REQUEST=None):
|
||||
return "\n".join(H)
|
||||
|
||||
|
||||
def check_local_photos_availability(groups_infos, REQUEST, format=""):
|
||||
def check_local_photos_availability(groups_infos, format=""):
|
||||
"""Verifie que toutes les photos (des gropupes indiqués) sont copiées localement
|
||||
dans ScoDoc (seules les photos dont nous disposons localement peuvent être exportées
|
||||
en pdf ou en zip).
|
||||
@ -245,7 +242,7 @@ def _trombino_zip(groups_infos):
|
||||
|
||||
|
||||
# Copy photos from portal to ScoDoc
|
||||
def trombino_copy_photos(group_ids=[], REQUEST=None, dialog_confirmed=False):
|
||||
def trombino_copy_photos(group_ids=[], dialog_confirmed=False):
|
||||
"Copy photos from portal to ScoDoc (overwriting local copy)"
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
|
||||
@ -316,7 +313,7 @@ def _get_etud_platypus_image(t, image_width=2 * cm):
|
||||
raise
|
||||
|
||||
|
||||
def _trombino_pdf(groups_infos, REQUEST):
|
||||
def _trombino_pdf(groups_infos):
|
||||
"Send photos as pdf page"
|
||||
# Generate PDF page
|
||||
filename = "trombino_%s" % groups_infos.groups_filename + ".pdf"
|
||||
@ -394,7 +391,7 @@ def _trombino_pdf(groups_infos, REQUEST):
|
||||
|
||||
|
||||
# --------------------- Sur une idée de l'IUT d'Orléans:
|
||||
def _listeappel_photos_pdf(groups_infos, REQUEST):
|
||||
def _listeappel_photos_pdf(groups_infos):
|
||||
"Doc pdf pour liste d'appel avec photos"
|
||||
filename = "trombino_%s" % groups_infos.groups_filename + ".pdf"
|
||||
sem = groups_infos.formsemestre # suppose 1 seul semestre
|
||||
@ -466,11 +463,11 @@ def _listeappel_photos_pdf(groups_infos, REQUEST):
|
||||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
||||
|
||||
# --------------------- Upload des photos de tout un groupe
|
||||
def photos_generate_excel_sample(group_ids=[], REQUEST=None):
|
||||
def photos_generate_excel_sample(group_ids=[]):
|
||||
"""Feuille excel pour import fichiers photos"""
|
||||
fmt = sco_import_etuds.sco_import_format()
|
||||
data = sco_import_etuds.sco_import_generate_excel_sample(
|
||||
@ -486,10 +483,13 @@ def photos_generate_excel_sample(group_ids=[], REQUEST=None):
|
||||
],
|
||||
extra_cols=["fichier_photo"],
|
||||
)
|
||||
return sco_excel.send_excel_file(REQUEST, data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
||||
return scu.send_file(
|
||||
data, "ImportPhotos", scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE, attached=True
|
||||
)
|
||||
# return sco_excel.send_excel_file(data, "ImportPhotos" + scu.XLSX_SUFFIX)
|
||||
|
||||
|
||||
def photos_import_files_form(group_ids=[], REQUEST=None):
|
||||
def photos_import_files_form(group_ids=[]):
|
||||
"""Formulaire pour importation photos"""
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
|
||||
@ -514,10 +514,11 @@ def photos_import_files_form(group_ids=[], REQUEST=None):
|
||||
% groups_infos.groups_query_args,
|
||||
]
|
||||
F = html_sco_header.sco_footer()
|
||||
REQUEST.form["group_ids"] = groups_infos.group_ids
|
||||
vals = scu.get_request_args()
|
||||
vals["group_ids"] = groups_infos.group_ids
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
vals,
|
||||
(
|
||||
("xlsfile", {"title": "Fichier Excel:", "input_type": "file", "size": 40}),
|
||||
("zipfile", {"title": "Fichier zip:", "input_type": "file", "size": 40}),
|
||||
@ -534,11 +535,10 @@ def photos_import_files_form(group_ids=[], REQUEST=None):
|
||||
group_ids=tf[2]["group_ids"],
|
||||
xlsfile=tf[2]["xlsfile"],
|
||||
zipfile=tf[2]["zipfile"],
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
|
||||
def photos_import_files(group_ids=[], xlsfile=None, zipfile=None, REQUEST=None):
|
||||
def photos_import_files(group_ids=[], xlsfile=None, zipfile=None):
|
||||
"""Importation des photos"""
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args
|
||||
|
@ -56,7 +56,6 @@ N_PER_ROW = 5
|
||||
def pdf_trombino_tours(
|
||||
group_ids=[], # liste des groupes à afficher
|
||||
formsemestre_id=None, # utilisé si pas de groupes selectionné
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Generation du trombinoscope en fichier PDF"""
|
||||
# Informations sur les groupes à afficher:
|
||||
@ -272,7 +271,7 @@ def pdf_trombino_tours(
|
||||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
||||
|
||||
# Feuille d'absences en pdf avec photos:
|
||||
@ -281,7 +280,6 @@ def pdf_trombino_tours(
|
||||
def pdf_feuille_releve_absences(
|
||||
group_ids=[], # liste des groupes à afficher
|
||||
formsemestre_id=None, # utilisé si pas de groupes selectionné
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Generation de la feuille d'absence en fichier PDF, avec photos"""
|
||||
|
||||
@ -466,4 +464,4 @@ def pdf_feuille_releve_absences(
|
||||
document.build(objects)
|
||||
data = report.getvalue()
|
||||
|
||||
return scu.sendPDFFile(REQUEST, data, filename)
|
||||
return scu.sendPDFFile(data, filename)
|
||||
|
@ -54,6 +54,7 @@ Solution proposée (nov 2014):
|
||||
|
||||
"""
|
||||
import flask
|
||||
from flask import request
|
||||
from flask_login import current_user
|
||||
|
||||
import app.scodoc.notesdb as ndb
|
||||
@ -134,9 +135,7 @@ def external_ue_create(
|
||||
return moduleimpl_id
|
||||
|
||||
|
||||
def external_ue_inscrit_et_note(
|
||||
moduleimpl_id, formsemestre_id, notes_etuds, REQUEST=None
|
||||
):
|
||||
def external_ue_inscrit_et_note(moduleimpl_id, formsemestre_id, notes_etuds):
|
||||
log(
|
||||
"external_ue_inscrit_et_note(moduleimpl_id=%s, notes_etuds=%s)"
|
||||
% (moduleimpl_id, notes_etuds)
|
||||
@ -146,7 +145,6 @@ def external_ue_inscrit_et_note(
|
||||
moduleimpl_id,
|
||||
formsemestre_id,
|
||||
list(notes_etuds.keys()),
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
|
||||
# Création d'une évaluation si il n'y en a pas déjà:
|
||||
@ -157,7 +155,6 @@ def external_ue_inscrit_et_note(
|
||||
else:
|
||||
# crée une évaluation:
|
||||
evaluation_id = sco_evaluations.do_evaluation_create(
|
||||
REQUEST=REQUEST,
|
||||
moduleimpl_id=moduleimpl_id,
|
||||
note_max=20.0,
|
||||
coefficient=1.0,
|
||||
@ -168,7 +165,7 @@ def external_ue_inscrit_et_note(
|
||||
)
|
||||
# Saisie des notes
|
||||
_, _, _ = sco_saisie_notes._notes_add(
|
||||
REQUEST.AUTHENTICATED_USER,
|
||||
current_user,
|
||||
evaluation_id,
|
||||
list(notes_etuds.items()),
|
||||
do_it=True,
|
||||
@ -200,7 +197,7 @@ def get_external_moduleimpl_id(formsemestre_id, ue_id):
|
||||
|
||||
|
||||
# Web function
|
||||
def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
||||
def external_ue_create_form(formsemestre_id, etudid):
|
||||
"""Formulaire création UE externe + inscription étudiant et saisie note
|
||||
- Demande UE: peut-être existante (liste les UE externes de cette formation),
|
||||
ou sinon spécifier titre, acronyme, type, ECTS
|
||||
@ -221,7 +218,6 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
||||
|
||||
H = [
|
||||
html_sco_header.html_sem_header(
|
||||
REQUEST,
|
||||
"Ajout d'une UE externe pour %(nomprenom)s" % etud,
|
||||
sem,
|
||||
javascripts=["js/sco_ue_external.js"],
|
||||
@ -248,8 +244,8 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
||||
default_label = "Aucune UE externe existante"
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("formsemestre_id", {"input_type": "hidden"}),
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
@ -365,6 +361,5 @@ def external_ue_create_form(formsemestre_id, etudid, REQUEST=None):
|
||||
moduleimpl_id,
|
||||
formsemestre_id,
|
||||
{etudid: note_value},
|
||||
REQUEST=REQUEST,
|
||||
)
|
||||
return flask.redirect(bull_url + "&head_message=Ajout%20effectué")
|
||||
|
@ -46,6 +46,8 @@ Opérations:
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from flask import request
|
||||
|
||||
from app.scodoc.intervals import intervalmap
|
||||
|
||||
import app.scodoc.sco_utils as scu
|
||||
@ -144,7 +146,7 @@ def list_operations(evaluation_id):
|
||||
return Ops
|
||||
|
||||
|
||||
def evaluation_list_operations(evaluation_id, REQUEST=None):
|
||||
def evaluation_list_operations(evaluation_id):
|
||||
"""Page listing operations on evaluation"""
|
||||
E = sco_evaluations.do_evaluation_list({"evaluation_id": evaluation_id})[0]
|
||||
M = sco_moduleimpl.do_moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
|
||||
@ -167,10 +169,10 @@ def evaluation_list_operations(evaluation_id, REQUEST=None):
|
||||
% (E["description"], E["jour"]),
|
||||
preferences=sco_preferences.SemPreferences(M["formsemestre_id"]),
|
||||
)
|
||||
return tab.make_page(REQUEST=REQUEST)
|
||||
return tab.make_page()
|
||||
|
||||
|
||||
def formsemestre_list_saisies_notes(formsemestre_id, format="html", REQUEST=None):
|
||||
def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
|
||||
"""Table listant toutes les opérations de saisies de notes, dans toutes
|
||||
les évaluations du semestre.
|
||||
"""
|
||||
@ -217,15 +219,15 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html", REQUEST=None
|
||||
html_sortable=True,
|
||||
caption="Saisies de notes dans %s" % sem["titreannee"],
|
||||
preferences=sco_preferences.SemPreferences(formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (REQUEST.URL0, formsemestre_id),
|
||||
base_url="%s?formsemestre_id=%s" % (request.base_url, formsemestre_id),
|
||||
origin="Généré par %s le " % sco_version.SCONAME
|
||||
+ scu.timedate_human_repr()
|
||||
+ "",
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
def get_note_history(evaluation_id, etudid, REQUEST=None, fmt=""):
|
||||
def get_note_history(evaluation_id, etudid, fmt=""):
|
||||
"""Historique d'une note
|
||||
= liste chronologique d'opérations, la plus récente d'abord
|
||||
[ { 'value', 'date', 'comment', 'uid' } ]
|
||||
@ -261,7 +263,7 @@ def get_note_history(evaluation_id, etudid, REQUEST=None, fmt=""):
|
||||
x["user_name"] = sco_users.user_info(x["uid"])["nomcomplet"]
|
||||
|
||||
if fmt == "json":
|
||||
return scu.sendJSON(REQUEST, history)
|
||||
return scu.sendJSON(history)
|
||||
else:
|
||||
return history
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
# Anciennement ZScoUsers.py, fonctions de gestion des données réécrite avec flask/SQLAlchemy
|
||||
|
||||
from flask import url_for, g
|
||||
from flask import url_for, g, request
|
||||
from flask_login import current_user
|
||||
|
||||
import cracklib # pylint: disable=import-error
|
||||
@ -51,11 +51,7 @@ import app.scodoc.sco_utils as scu
|
||||
|
||||
from app.scodoc.sco_exceptions import (
|
||||
AccessDenied,
|
||||
ScoException,
|
||||
ScoValueError,
|
||||
ScoInvalidDateError,
|
||||
ScoLockedFormError,
|
||||
ScoGenError,
|
||||
)
|
||||
|
||||
|
||||
@ -81,12 +77,12 @@ def is_valid_password(cleartxt):
|
||||
# ---------------
|
||||
|
||||
|
||||
def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
||||
def index_html(all_depts=False, with_inactives=False, format="html"):
|
||||
"gestion utilisateurs..."
|
||||
all_depts = int(all_depts)
|
||||
with_inactives = int(with_inactives)
|
||||
|
||||
H = [html_sco_header.html_sem_header(REQUEST, "Gestion des utilisateurs")]
|
||||
H = [html_sco_header.html_sem_header("Gestion des utilisateurs")]
|
||||
|
||||
if current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept):
|
||||
H.append(
|
||||
@ -117,7 +113,7 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
||||
<input type="checkbox" name="all_depts" value="1" onchange="document.f.submit();" %s>Tous les départements</input>
|
||||
<input type="checkbox" name="with_inactives" value="1" onchange="document.f.submit();" %s>Avec anciens utilisateurs</input>
|
||||
</form></p>"""
|
||||
% (REQUEST.URL0, checked, olds_checked)
|
||||
% (request.base_url, checked, olds_checked)
|
||||
)
|
||||
|
||||
L = list_users(
|
||||
@ -125,7 +121,6 @@ def index_html(REQUEST, all_depts=False, with_inactives=False, format="html"):
|
||||
all_depts=all_depts,
|
||||
with_inactives=with_inactives,
|
||||
format=format,
|
||||
REQUEST=REQUEST,
|
||||
with_links=current_user.has_permission(Permission.ScoUsersAdmin, g.scodoc_dept),
|
||||
)
|
||||
if format != "html":
|
||||
@ -142,7 +137,6 @@ def list_users(
|
||||
with_inactives=False, # inclut les anciens utilisateurs (status "old")
|
||||
format="html",
|
||||
with_links=True,
|
||||
REQUEST=None,
|
||||
):
|
||||
"List users, returns a table in the specified format"
|
||||
from app.scodoc.sco_permissions_check import can_handle_passwd
|
||||
@ -212,12 +206,12 @@ def list_users(
|
||||
html_class="table_leftalign list_users",
|
||||
html_with_td_classes=True,
|
||||
html_sortable=True,
|
||||
base_url="%s?all=%s" % (REQUEST.URL0, all),
|
||||
base_url="%s?all=%s" % (request.base_url, all),
|
||||
pdf_link=False, # table is too wide to fit in a paper page => disable pdf
|
||||
preferences=sco_preferences.SemPreferences(),
|
||||
)
|
||||
|
||||
return tab.make_page(format=format, with_html_headers=False, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format, with_html_headers=False)
|
||||
|
||||
|
||||
def get_user_list(dept=None, with_inactives=False):
|
||||
|
@ -39,21 +39,16 @@ import os
|
||||
import pydot
|
||||
import re
|
||||
import requests
|
||||
import six
|
||||
import six.moves._thread
|
||||
import sys
|
||||
import _thread
|
||||
import time
|
||||
import traceback
|
||||
import types
|
||||
import unicodedata
|
||||
import urllib
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from flask import g, current_app
|
||||
from urllib.parse import urlparse, parse_qsl, urlunparse, urlencode
|
||||
|
||||
from PIL import Image as PILImage
|
||||
|
||||
from flask import g, url_for, request
|
||||
from flask import g, request
|
||||
from flask import url_for, make_response
|
||||
|
||||
from config import Config
|
||||
from app import log
|
||||
@ -217,7 +212,7 @@ def group_by_key(d, key):
|
||||
|
||||
|
||||
# ----- Global lock for critical sections (except notes_tables caches)
|
||||
GSL = six.moves._thread.allocate_lock() # Global ScoDoc Lock
|
||||
GSL = _thread.allocate_lock() # Global ScoDoc Lock
|
||||
|
||||
SCODOC_DIR = Config.SCODOC_DIR
|
||||
|
||||
@ -228,7 +223,7 @@ SCODOC_CFG_DIR = os.path.join(Config.SCODOC_VAR_DIR, "config")
|
||||
SCODOC_VERSION_DIR = os.path.join(SCODOC_CFG_DIR, "version")
|
||||
# ----- Repertoire tmp : /opt/scodoc-data/tmp
|
||||
SCO_TMP_DIR = os.path.join(Config.SCODOC_VAR_DIR, "tmp")
|
||||
if not os.path.exists(SCO_TMP_DIR):
|
||||
if not os.path.exists(SCO_TMP_DIR) and os.path.exists(Config.SCODOC_VAR_DIR):
|
||||
os.mkdir(SCO_TMP_DIR, 0o755)
|
||||
# ----- Les logos: /opt/scodoc-data/config/logos
|
||||
SCODOC_LOGOS_DIR = os.path.join(SCODOC_CFG_DIR, "logos")
|
||||
@ -296,16 +291,39 @@ SCO_DEV_MAIL = "emmanuel.viennet@gmail.com" # SVP ne pas changer
|
||||
# Adresse pour l'envoi des dumps (pour assistance technnique):
|
||||
# ne pas changer (ou vous perdez le support)
|
||||
SCO_DUMP_UP_URL = "https://scodoc.iutv.univ-paris13.fr/scodoc-installmgr/upload-dump"
|
||||
# SCO_DUMP_UP_URL = "http://192.168.56.1:5000/upload_dump"
|
||||
|
||||
CSV_FIELDSEP = ";"
|
||||
CSV_LINESEP = "\n"
|
||||
CSV_MIMETYPE = "text/comma-separated-values"
|
||||
CSV_SUFFIX = ".csv"
|
||||
JSON_MIMETYPE = "application/json"
|
||||
JSON_SUFFIX = ".json"
|
||||
PDF_MIMETYPE = "application/pdf"
|
||||
PDF_SUFFIX = ".pdf"
|
||||
XLS_MIMETYPE = "application/vnd.ms-excel"
|
||||
XLS_SUFFIX = ".xls"
|
||||
XLSX_MIMETYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
XLSX_SUFFIX = ".xlsx"
|
||||
PDF_MIMETYPE = "application/pdf"
|
||||
XML_MIMETYPE = "text/xml"
|
||||
JSON_MIMETYPE = "application/json"
|
||||
XML_SUFFIX = ".xml"
|
||||
|
||||
|
||||
def get_mime_suffix(format_code: str) -> tuple[str, str]:
|
||||
"""Returns (MIME, SUFFIX) from format_code == "xls", "xml", ...
|
||||
SUFFIX includes the dot: ".xlsx", ".xml", ...
|
||||
"xls" and "xlsx" format codes give XLSX
|
||||
"""
|
||||
d = {
|
||||
"csv": (CSV_MIMETYPE, CSV_SUFFIX),
|
||||
"xls": (XLSX_MIMETYPE, XLSX_SUFFIX),
|
||||
"xlsx": (XLSX_MIMETYPE, XLSX_SUFFIX),
|
||||
"pdf": (PDF_MIMETYPE, PDF_SUFFIX),
|
||||
"xml": (XML_MIMETYPE, XML_SUFFIX),
|
||||
"json": (JSON_MIMETYPE, JSON_SUFFIX),
|
||||
}
|
||||
return d[format_code]
|
||||
|
||||
|
||||
# Admissions des étudiants
|
||||
# Différents types de voies d'admission:
|
||||
@ -388,6 +406,18 @@ def unescape_html(s):
|
||||
return s
|
||||
|
||||
|
||||
def build_url_query(url: str, **params) -> str:
|
||||
"""Add parameters to existing url, as a query string"""
|
||||
url_parse = urlparse(url)
|
||||
query = url_parse.query
|
||||
url_dict = dict(parse_qsl(query))
|
||||
url_dict.update(params)
|
||||
url_new_query = urlencode(url_dict)
|
||||
url_parse = url_parse._replace(query=url_new_query)
|
||||
new_url = urlunparse(url_parse)
|
||||
return new_url
|
||||
|
||||
|
||||
# test if obj is iterable (but not a string)
|
||||
isiterable = lambda obj: getattr(obj, "__iter__", False)
|
||||
|
||||
@ -445,6 +475,22 @@ def suppress_accents(s):
|
||||
return s # may be int
|
||||
|
||||
|
||||
class PurgeChars:
|
||||
"""delete all chars except those belonging to the specified string"""
|
||||
|
||||
def __init__(self, allowed_chars=""):
|
||||
self.allowed_chars_set = {ord(c) for c in allowed_chars}
|
||||
|
||||
def __getitem__(self, x):
|
||||
if x not in self.allowed_chars_set:
|
||||
return None
|
||||
raise LookupError()
|
||||
|
||||
|
||||
def purge_chars(s, allowed_chars=""):
|
||||
return s.translate(PurgeChars(allowed_chars=allowed_chars))
|
||||
|
||||
|
||||
def sanitize_string(s):
|
||||
"""s is an ordinary string, encoding given by SCO_ENCODING"
|
||||
suppress accents and chars interpreted in XML
|
||||
@ -457,14 +503,17 @@ def sanitize_string(s):
|
||||
return suppress_accents(s.translate(trans)).replace(" ", "_").replace("\t", "_")
|
||||
|
||||
|
||||
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\")
|
||||
_BAD_FILENAME_CHARS = str.maketrans("", "", ":/\\&[]*?'")
|
||||
|
||||
|
||||
def make_filename(name):
|
||||
"""Try to convert name to a reasonable filename
|
||||
without spaces, (back)slashes, : and without accents
|
||||
"""
|
||||
return suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
|
||||
return (
|
||||
suppress_accents(name.translate(_BAD_FILENAME_CHARS)).replace(" ", "_")
|
||||
or "scodoc"
|
||||
)
|
||||
|
||||
|
||||
VALID_CARS = (
|
||||
@ -490,30 +539,21 @@ def is_valid_filename(filename):
|
||||
return VALID_EXP.match(filename)
|
||||
|
||||
|
||||
def sendCSVFile(REQUEST, data, filename):
|
||||
"""publication fichier.
|
||||
(on ne doit rien avoir émis avant, car ici sont générés les entetes)
|
||||
"""
|
||||
filename = (
|
||||
unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_")
|
||||
)
|
||||
REQUEST.RESPONSE.setHeader("content-type", CSV_MIMETYPE)
|
||||
REQUEST.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
)
|
||||
return data
|
||||
def bul_filename(sem, etud, format):
|
||||
"""Build a filename for this bulletin"""
|
||||
dt = time.strftime("%Y-%m-%d")
|
||||
filename = f"bul-{sem['titre_num']}-{dt}-{etud['nom']}.{format}"
|
||||
filename = make_filename(filename)
|
||||
return filename
|
||||
|
||||
|
||||
def sendPDFFile(REQUEST, data, filename):
|
||||
filename = (
|
||||
unescape_html(suppress_accents(filename)).replace("&", "").replace(" ", "_")
|
||||
)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", PDF_MIMETYPE)
|
||||
REQUEST.RESPONSE.setHeader(
|
||||
"content-disposition", 'attachment; filename="%s"' % filename
|
||||
)
|
||||
return data
|
||||
def sendCSVFile(data, filename): # DEPRECATED utiliser send_file
|
||||
"""publication fichier CSV."""
|
||||
return send_file(data, filename=filename, mime=CSV_MIMETYPE, attached=True)
|
||||
|
||||
|
||||
def sendPDFFile(data, filename): # DEPRECATED utiliser send_file
|
||||
return send_file(data, filename=filename, mime=PDF_MIMETYPE, attached=True)
|
||||
|
||||
|
||||
class ScoDocJSONEncoder(json.JSONEncoder):
|
||||
@ -526,38 +566,88 @@ class ScoDocJSONEncoder(json.JSONEncoder):
|
||||
return json.JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
def sendJSON(REQUEST, data):
|
||||
def sendJSON(data, attached=False):
|
||||
js = json.dumps(data, indent=1, cls=ScoDocJSONEncoder)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
|
||||
return js
|
||||
return send_file(
|
||||
js, filename="sco_data.json", mime=JSON_MIMETYPE, attached=attached
|
||||
)
|
||||
|
||||
|
||||
def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True):
|
||||
def sendXML(data, tagname=None, force_outer_xml_tag=True, attached=False):
|
||||
if type(data) != list:
|
||||
data = [data] # always list-of-dicts
|
||||
if force_outer_xml_tag:
|
||||
data = [{tagname: data}]
|
||||
tagname += "_list"
|
||||
doc = sco_xml.simple_dictlist2xml(data, tagname=tagname)
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
||||
return doc
|
||||
return send_file(doc, filename="sco_data.xml", mime=XML_MIMETYPE, attached=attached)
|
||||
|
||||
|
||||
def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True):
|
||||
def sendResult(data, name=None, format=None, force_outer_xml_tag=True, attached=False):
|
||||
if (format is None) or (format == "html"):
|
||||
return data
|
||||
elif format == "xml": # name is outer tagname
|
||||
return sendXML(
|
||||
REQUEST, data, tagname=name, force_outer_xml_tag=force_outer_xml_tag
|
||||
data,
|
||||
tagname=name,
|
||||
force_outer_xml_tag=force_outer_xml_tag,
|
||||
attached=attached,
|
||||
)
|
||||
elif format == "json":
|
||||
return sendJSON(REQUEST, data)
|
||||
return sendJSON(data, attached=attached)
|
||||
else:
|
||||
raise ValueError("invalid format: %s" % format)
|
||||
|
||||
|
||||
def send_file(data, filename="", suffix="", mime=None, attached=None):
|
||||
"""Build Flask Response for file download of given type
|
||||
By default (attached is None), json and xml are inlined and otrher types are attached.
|
||||
"""
|
||||
if attached is None:
|
||||
if mime == XML_MIMETYPE or mime == JSON_MIMETYPE:
|
||||
attached = False
|
||||
else:
|
||||
attached = True
|
||||
# if attached and not filename:
|
||||
# raise ValueError("send_file: missing attachement filename")
|
||||
if filename:
|
||||
if suffix:
|
||||
filename += suffix
|
||||
filename = make_filename(filename)
|
||||
response = make_response(data)
|
||||
response.headers["Content-Type"] = mime
|
||||
if attached and filename:
|
||||
response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename
|
||||
return response
|
||||
|
||||
|
||||
def get_request_args():
|
||||
"""returns a dict with request (POST or GET) arguments
|
||||
converted to suit legacy Zope style (scodoc7) functions.
|
||||
"""
|
||||
# copy to get a mutable object (necessary for TrivialFormulator and several methods)
|
||||
if request.method == "POST":
|
||||
# request.form is a werkzeug.datastructures.ImmutableMultiDict
|
||||
# must copy to get a mutable version (needed by TrivialFormulator)
|
||||
vals = request.form.copy()
|
||||
if request.files:
|
||||
# Add files in form:
|
||||
vals.update(request.files)
|
||||
for k in request.form:
|
||||
if k.endswith(":list"):
|
||||
vals[k[:-5]] = request.form.getlist(k)
|
||||
elif request.method == "GET":
|
||||
vals = {}
|
||||
for k in request.args:
|
||||
# current_app.logger.debug("%s\t%s" % (k, request.args.getlist(k)))
|
||||
if k.endswith(":list"):
|
||||
vals[k[:-5]] = request.args.getlist(k)
|
||||
else:
|
||||
values = request.args.getlist(k)
|
||||
vals[k] = values[0] if len(values) == 1 else values
|
||||
return vals
|
||||
|
||||
|
||||
def get_scodoc_version():
|
||||
"return a string identifying ScoDoc version"
|
||||
return sco_version.SCOVERSION
|
||||
@ -759,45 +849,6 @@ def AnneeScolaire(sco_year=None):
|
||||
return year
|
||||
|
||||
|
||||
def log_unknown_etud(REQUEST=None, format="html"):
|
||||
"""Log request: cas ou getEtudInfo n'a pas ramene de resultat"""
|
||||
etudid = REQUEST.form.get("etudid", "?")
|
||||
code_nip = REQUEST.form.get("code_nip", "?")
|
||||
code_ine = REQUEST.form.get("code_ine", "?")
|
||||
log(
|
||||
"unknown student: etudid=%s code_nip=%s code_ine=%s"
|
||||
% (etudid, code_nip, code_ine)
|
||||
)
|
||||
return _sco_error_response("unknown student", format=format, REQUEST=REQUEST)
|
||||
|
||||
|
||||
# XXX #sco8 à tester ou ré-écrire
|
||||
def _sco_error_response(msg, format="html", REQUEST=None):
|
||||
"""Send an error message to the client, in html or xml format."""
|
||||
REQUEST.RESPONSE.setStatus(404, reason=msg)
|
||||
if format == "html" or format == "pdf":
|
||||
raise sco_exceptions.ScoValueError(msg)
|
||||
elif format == "xml":
|
||||
REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE)
|
||||
doc = ElementTree.Element("error", msg=msg)
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(SCO_ENCODING)
|
||||
elif format == "json":
|
||||
REQUEST.RESPONSE.setHeader("content-type", JSON_MIMETYPE)
|
||||
return "undefined" # XXX voir quoi faire en cas d'erreur json
|
||||
else:
|
||||
raise ValueError("ScoErrorResponse: invalid format")
|
||||
|
||||
|
||||
def return_text_if_published(val, REQUEST):
|
||||
"""Pour les méthodes publiées qui ramènent soit du texte (HTML) soit du JSON
|
||||
sauf quand elles sont appellées depuis python.
|
||||
La présence de l'argument REQUEST indique la publication.
|
||||
"""
|
||||
if REQUEST and not isinstance(val, str):
|
||||
return sendJSON(REQUEST, val)
|
||||
return val
|
||||
|
||||
|
||||
def confirm_dialog(
|
||||
message="<p>Confirmer ?</p>",
|
||||
OK="OK",
|
||||
@ -823,10 +874,12 @@ def confirm_dialog(
|
||||
action = f'action="{dest_url}"'
|
||||
|
||||
H = [
|
||||
f"""<form {action} method="post">""",
|
||||
message,
|
||||
"""<input type="submit" value="%s"/>""" % OK,
|
||||
f"""<form {action} method="POST">
|
||||
{message}
|
||||
""",
|
||||
]
|
||||
if OK or not cancel_url:
|
||||
H.append(f'<input type="submit" value="{OK}"/>')
|
||||
if cancel_url:
|
||||
H.append(
|
||||
"""<input type ="button" value="%s"
|
||||
|
@ -27,6 +27,10 @@ h3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div#gtrcontent {
|
||||
margin-bottom: 4ex;
|
||||
}
|
||||
|
||||
.scotext {
|
||||
font-family : TimesNewRoman, "Times New Roman", Times, Baskerville, Georgia, serif;
|
||||
}
|
||||
@ -154,7 +158,7 @@ div.scovalueerror {
|
||||
p.footer {
|
||||
font-size: 80%;
|
||||
color: rgb(60,60,60);
|
||||
margin-top: 10px;
|
||||
margin-top: 15px;
|
||||
border-top: 1px solid rgb(60,60,60);
|
||||
}
|
||||
|
||||
@ -688,6 +692,7 @@ td.fichetitre2 .fl {
|
||||
|
||||
span.link_bul_pdf {
|
||||
font-size: 80%;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
/* Page accueil Sco */
|
||||
@ -1092,13 +1097,13 @@ h2.formsemestre, .gtrcontent h2 {
|
||||
#formnotes td.tf-fieldlabel {
|
||||
border-bottom: 1px dotted #fdcaca;
|
||||
}
|
||||
|
||||
/* Formulaires ScoDoc 9 */
|
||||
form.sco-form {
|
||||
margin-top: 1em;
|
||||
.wtf-field li {
|
||||
display: inline;
|
||||
}.wtf-field ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
div.sco-submit {
|
||||
margin-top: 2em;
|
||||
.wtf-field .errors {
|
||||
color: red ; font-weight: bold;
|
||||
}
|
||||
/*
|
||||
.formsemestre_menubar {
|
||||
|
@ -11,7 +11,7 @@
|
||||
ou écrire la liste "notes" <a href="mailto:notes@listes.univ-paris13.fr">notes@listes.univ-paris13.fr</a> en
|
||||
indiquant la version du logiciel
|
||||
<br />
|
||||
(plus d'informations sur les listes de diffusion<a href="https://scodoc.org/ListesDeDiffusion/">voir
|
||||
(plus d'informations sur les listes de diffusion <a href="https://scodoc.org/ListesDeDiffusion/">voir
|
||||
cette page</a>).
|
||||
</p>
|
||||
|
||||
|
@ -5,13 +5,14 @@
|
||||
|
||||
<h2>Erreur !</h2>
|
||||
|
||||
<p>{{ exc }}</p>
|
||||
{{ exc | safe }}
|
||||
|
||||
<p>
|
||||
<p class="footer">
|
||||
{% if g.scodoc_dept %}
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">continuer</a>
|
||||
<a href="{{ exc.dest_url or url_for('scolar.index_html', scodoc_dept=g.scodoc_dept) }}">retour page d'accueil
|
||||
departement {{ g.scodoc_dept }}</a>
|
||||
{% else %}
|
||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">continuer</a>
|
||||
<a href="{{ exc.dest_url or url_for('scodoc.index') }}">retour page d'accueil</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
|
||||
|
80
app/templates/scodoc/forms/placement.html
Normal file
80
app/templates/scodoc/forms/placement.html
Normal file
@ -0,0 +1,80 @@
|
||||
{% import 'bootstrap/wtf.html' as wtf %}
|
||||
|
||||
{% macro render_field(field) %}
|
||||
<tr>
|
||||
<td class="wtf-field">{{ field.label }}</td>
|
||||
<td class="wtf-field">{{ field(**kwargs)|safe }}
|
||||
{% if field.errors %}
|
||||
<ul class=errors>
|
||||
{% for error in field.errors %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endmacro %}
|
||||
|
||||
<div class="saisienote_etape1 form_placement">
|
||||
<form method=post>
|
||||
{{ form.evaluation_id }}
|
||||
{{ form.csrf_token }}
|
||||
<table class="tf">
|
||||
<tbody>
|
||||
{{ render_field(form.surveillants) }}
|
||||
{{ render_field(form.batiment) }}
|
||||
{{ render_field(form.salle) }}
|
||||
{{ render_field(form.nb_rangs) }}
|
||||
{{ render_field(form.etiquetage) }}
|
||||
{% if form.has_groups %}
|
||||
{{ render_field(form.groups, size=form.nb_groups) }}
|
||||
<!-- Tentative de recréer le choix des groupes sous forme de cases à cocher // demande à créer des champs wtf dynamiquement
|
||||
{% for partition in form.groups_tree %}
|
||||
<tr>
|
||||
{% if partition == 'Tous' %}
|
||||
<td rowspan="{{ form.nb_groups }}">Groupes</td>
|
||||
{% endif %}
|
||||
<td>{{ partition }}</td>
|
||||
<td>
|
||||
{% for groupe in form.groups_tree[partition] %}
|
||||
{{ groupe }}{{ form[form.groups_tree[partition][groupe]] }}
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
-->
|
||||
{% endif %}
|
||||
{{ render_field(form.file_format) }}
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<input id="gr_submit" type=submit value="Ok">
|
||||
<input id="gr_cancel" type=submit value="Annuler">
|
||||
</script>
|
||||
</form>
|
||||
<h3>Explications</h3>
|
||||
<ul>
|
||||
<li>préciser les surveillants et la localisation (bâtiment et salle) et indiquer la largeur de la salle (nombre
|
||||
de colonnes);</li>
|
||||
<li>deux types de placements sont possibles :
|
||||
<ul>
|
||||
<li>continue suppose que les tables ont toutes un numéro unique;</li>
|
||||
<li>coordonnées localise chaque table via un numéro de colonne et un numéro de ligne (ou rangée).</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Il est possible de choisir un ou plusieurs groupes (shift/ctrl click) ou de choisir 'tous'.</li>
|
||||
<li>Choisir le format du fichier résultat :
|
||||
<ul>
|
||||
<li>le format pdf consiste en un tableau précisant pour chaque étudiant la localisation de sa table;
|
||||
</li>
|
||||
<li>le format xls produit un classeur avec deux onglets:
|
||||
<ul>
|
||||
<li>le premier onglet donne une vue de la salle avec la localisation des étudiants et
|
||||
peut servir de feuille d'émargement;</li>
|
||||
<li>le second onglet est un tableau similaire à celui du fichier pdf;</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
12
app/templates/sidebar_dept.html
Normal file
12
app/templates/sidebar_dept.html
Normal file
@ -0,0 +1,12 @@
|
||||
<h2 class="insidebar">Dépt. {{ prefs["DeptName"] }}</h2>
|
||||
<a href="{{ url_for('scodoc.index') }}" class="sidebar">Accueil</a> <br/>
|
||||
{% if prefs["DeptIntranetURL"] %}
|
||||
<a href="{{ prefs["DeptIntranetURL"] }}" class="sidebar">
|
||||
{{ prefs["DeptIntranetTitle"] }}</a>
|
||||
{% endif %}
|
||||
<br/>
|
||||
|
||||
{#
|
||||
# Entreprises pas encore supporté en ScoDoc8
|
||||
# <br/><a href="%(ScoURL)s/Entreprises" class="sidebar">Entreprises</a> <br/>
|
||||
#}
|
@ -47,20 +47,18 @@ L'API de plus bas niveau est en gros:
|
||||
"""
|
||||
|
||||
import calendar
|
||||
import cgi
|
||||
import datetime
|
||||
import dateutil
|
||||
import dateutil.parser
|
||||
import re
|
||||
import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
|
||||
import string
|
||||
import time
|
||||
import urllib
|
||||
from xml.etree import ElementTree
|
||||
|
||||
import flask
|
||||
from flask import g
|
||||
from flask import g, request
|
||||
from flask import url_for
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
|
||||
from app.decorators import (
|
||||
scodoc,
|
||||
@ -74,6 +72,7 @@ from app.decorators import (
|
||||
from app.views import absences_bp as bp
|
||||
|
||||
# ---------------
|
||||
from app.models.absences import BilletAbsence
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc import notesdb as ndb
|
||||
from app import log
|
||||
@ -123,11 +122,11 @@ def sco_publish(route, function, permission, methods=["GET"]):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def index_html(REQUEST=None):
|
||||
def index_html():
|
||||
"""Gestionnaire absences, page principale"""
|
||||
# crude portage from 1999 DTML
|
||||
sems = sco_formsemestre.do_formsemestre_list()
|
||||
authuser = REQUEST.AUTHENTICATED_USER
|
||||
authuser = current_user
|
||||
|
||||
H = [
|
||||
html_sco_header.sco_header(
|
||||
@ -164,7 +163,7 @@ def index_html(REQUEST=None):
|
||||
Saisie par semaine </span> - Choix du groupe:
|
||||
<input name="datelundi" type="hidden" value="x"/>
|
||||
"""
|
||||
% REQUEST.URL0,
|
||||
% request.base_url,
|
||||
sco_abs_views.formChoixSemestreGroupe(),
|
||||
"</p>",
|
||||
cal_select_week(),
|
||||
@ -270,7 +269,6 @@ def doSignaleAbsenceGrSemestre(
|
||||
dates="",
|
||||
etudids="",
|
||||
destination=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Enregistre absences aux dates indiquees (abslist et dates).
|
||||
dates est une liste de dates ISO (séparées par des ',').
|
||||
@ -295,10 +293,10 @@ def doSignaleAbsenceGrSemestre(
|
||||
|
||||
# 2- Ajoute les absences
|
||||
if abslist:
|
||||
sco_abs._add_abslist(abslist, REQUEST, moduleimpl_id)
|
||||
sco_abs.add_abslist(abslist, moduleimpl_id)
|
||||
return "Absences ajoutées"
|
||||
|
||||
return ""
|
||||
return ("", 204)
|
||||
|
||||
|
||||
# ------------ HTML Interfaces
|
||||
@ -307,14 +305,14 @@ def doSignaleAbsenceGrSemestre(
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def SignaleAbsenceGrHebdo(
|
||||
datelundi, group_ids=[], destination="", moduleimpl_id=None, REQUEST=None
|
||||
datelundi, group_ids=[], destination="", moduleimpl_id=None, formsemestre_id=None
|
||||
):
|
||||
"Saisie hebdomadaire des absences"
|
||||
if not moduleimpl_id:
|
||||
moduleimpl_id = None
|
||||
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(
|
||||
group_ids, moduleimpl_id=moduleimpl_id
|
||||
group_ids, moduleimpl_id=moduleimpl_id, formsemestre_id=formsemestre_id
|
||||
)
|
||||
if not groups_infos.members:
|
||||
return (
|
||||
@ -326,7 +324,7 @@ def SignaleAbsenceGrHebdo(
|
||||
base_url = "SignaleAbsenceGrHebdo?datelundi=%s&%s&destination=%s" % (
|
||||
datelundi,
|
||||
groups_infos.groups_query_args,
|
||||
six.moves.urllib.parse.quote(destination),
|
||||
urllib.parse.quote(destination),
|
||||
)
|
||||
|
||||
formsemestre_id = groups_infos.formsemestre_id
|
||||
@ -473,7 +471,6 @@ def SignaleAbsenceGrSemestre(
|
||||
group_ids=[], # list of groups to display
|
||||
nbweeks=4, # ne montre que les nbweeks dernieres semaines
|
||||
moduleimpl_id=None,
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Saisie des absences sur une journée sur un semestre (ou intervalle de dates) entier"""
|
||||
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
|
||||
@ -510,7 +507,7 @@ def SignaleAbsenceGrSemestre(
|
||||
datedebut,
|
||||
datefin,
|
||||
groups_infos.groups_query_args,
|
||||
six.moves.urllib.parse.quote(destination),
|
||||
urllib.parse.quote(destination),
|
||||
)
|
||||
)
|
||||
base_url = base_url_noweeks + "&nbweeks=%s" % nbweeks # sans le moduleimpl_id
|
||||
@ -810,7 +807,7 @@ def _gen_form_saisie_groupe(
|
||||
H.append('<input type="hidden" name="dates" value="%s"/>' % ",".join(dates))
|
||||
H.append(
|
||||
'<input type="hidden" name="destination" value="%s"/>'
|
||||
% six.moves.urllib.parse.quote(destination)
|
||||
% urllib.parse.quote(destination)
|
||||
)
|
||||
#
|
||||
# version pour formulaire avec AJAX (Yann LB)
|
||||
@ -843,7 +840,6 @@ def EtatAbsencesGr(
|
||||
fin="",
|
||||
with_boursier=True, # colonne boursier
|
||||
format="html",
|
||||
REQUEST=None,
|
||||
):
|
||||
"""Liste les absences de groupes"""
|
||||
datedebut = ndb.DateDMYtoISO(debut)
|
||||
@ -938,7 +934,7 @@ def EtatAbsencesGr(
|
||||
javascripts=["js/etud_info.js"],
|
||||
),
|
||||
html_title=html_sco_header.html_sem_header(
|
||||
REQUEST, "%s" % title, sem, with_page_header=False
|
||||
"%s" % title, sem, with_page_header=False
|
||||
)
|
||||
+ "<p>Période du %s au %s (nombre de <b>demi-journées</b>)<br/>" % (debut, fin),
|
||||
base_url="%s&formsemestre_id=%s&debut=%s&fin=%s"
|
||||
@ -964,9 +960,9 @@ ou entrez une date pour visualiser les absents un jour donné :
|
||||
<input type="submit" name="" value="visualiser les absences">
|
||||
</form></div>
|
||||
"""
|
||||
% (REQUEST.URL0, formsemestre_id, groups_infos.get_form_elem()),
|
||||
% (request.base_url, formsemestre_id, groups_infos.get_form_elem()),
|
||||
)
|
||||
return tab.make_page(format=format, REQUEST=REQUEST)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
@bp.route("/EtatAbsencesDate")
|
||||
@ -1062,7 +1058,6 @@ def AddBilletAbsence(
|
||||
code_nip=None,
|
||||
code_ine=None,
|
||||
justified=True,
|
||||
REQUEST=None,
|
||||
xml_reply=True,
|
||||
):
|
||||
"""Memorise un "billet"
|
||||
@ -1072,7 +1067,9 @@ def AddBilletAbsence(
|
||||
# check etudid
|
||||
etuds = sco_etud.get_etud_info(etudid=etudid, code_nip=code_nip, filled=True)
|
||||
if not etuds:
|
||||
return scu.log_unknown_etud(REQUEST=REQUEST)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
# check dates
|
||||
begin_date = dateutil.parser.isoparse(begin) # may raises ValueError
|
||||
@ -1096,13 +1093,10 @@ def AddBilletAbsence(
|
||||
)
|
||||
if xml_reply:
|
||||
# Renvoie le nouveau billet en XML
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
log("AddBilletAbsence: new billet_id=%s (%gs)" % (billet_id, time.time() - t0))
|
||||
return tab.make_page(REQUEST=REQUEST, format="xml")
|
||||
return tab.make_page(format="xml")
|
||||
else:
|
||||
return billet_id
|
||||
|
||||
@ -1111,7 +1105,7 @@ def AddBilletAbsence(
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsAddBillet)
|
||||
@scodoc7func
|
||||
def AddBilletAbsenceForm(etudid, REQUEST=None):
|
||||
def AddBilletAbsenceForm(etudid):
|
||||
"""Formulaire ajout billet (pour tests seulement, le vrai formulaire accessible aux etudiants
|
||||
étant sur le portail étudiant).
|
||||
"""
|
||||
@ -1122,8 +1116,8 @@ def AddBilletAbsenceForm(etudid, REQUEST=None):
|
||||
)
|
||||
]
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("etudid", {"input_type": "hidden"}),
|
||||
("begin", {"input_type": "date"}),
|
||||
@ -1223,17 +1217,18 @@ def _tableBillets(billets, etud=None, title=""):
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
|
||||
def listeBilletsEtud(etudid=False, format="html"):
|
||||
"""Liste billets pour un etudiant"""
|
||||
etuds = sco_etud.get_etud_info(filled=True, etudid=etudid)
|
||||
if not etuds:
|
||||
return scu.log_unknown_etud(format=format, REQUEST=REQUEST)
|
||||
sco_etud.log_unknown_etud()
|
||||
raise ScoValueError("étudiant inconnu")
|
||||
|
||||
etud = etuds[0]
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etudid": etud["etudid"]})
|
||||
tab = _tableBillets(billets, etud=etud)
|
||||
return tab.make_page(REQUEST=REQUEST, format=format)
|
||||
return tab.make_page(format=format)
|
||||
|
||||
|
||||
@bp.route(
|
||||
@ -1242,12 +1237,12 @@ def listeBilletsEtud(etudid=False, REQUEST=None, format="html"):
|
||||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def XMLgetBilletsEtud(etudid=False, REQUEST=None):
|
||||
def XMLgetBilletsEtud(etudid=False):
|
||||
"""Liste billets pour un etudiant"""
|
||||
if not sco_preferences.get_preference("handle_billets_abs"):
|
||||
return ""
|
||||
t0 = time.time()
|
||||
r = listeBilletsEtud(etudid, REQUEST=REQUEST, format="xml")
|
||||
r = listeBilletsEtud(etudid, format="xml")
|
||||
log("XMLgetBilletsEtud (%gs)" % (time.time() - t0))
|
||||
return r
|
||||
|
||||
@ -1256,10 +1251,17 @@ def XMLgetBilletsEtud(etudid=False, REQUEST=None):
|
||||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def listeBillets(REQUEST=None):
|
||||
def listeBillets():
|
||||
"""Page liste des billets non traités et formulaire recherche d'un billet"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"etat": 0})
|
||||
# utilise Flask, jointure avec departement de l'étudiant
|
||||
billets = (
|
||||
BilletAbsence.query.filter_by(etat=False)
|
||||
.join(BilletAbsence.etudiant, aliased=True)
|
||||
.filter_by(dept_id=g.scodoc_dept_id)
|
||||
)
|
||||
# reconverti en dict pour les fonctions scodoc7
|
||||
billets = [b.to_dict() for b in billets]
|
||||
#
|
||||
tab = _tableBillets(billets)
|
||||
T = tab.html()
|
||||
H = [
|
||||
@ -1268,8 +1270,8 @@ def listeBillets(REQUEST=None):
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(("billet_id", {"input_type": "text", "title": "Numéro du billet"}),),
|
||||
submitbutton=False,
|
||||
)
|
||||
@ -1281,11 +1283,11 @@ def listeBillets(REQUEST=None):
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/deleteBilletAbsence")
|
||||
@bp.route("/deleteBilletAbsence", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def deleteBilletAbsence(billet_id, REQUEST=None, dialog_confirmed=False):
|
||||
def deleteBilletAbsence(billet_id, dialog_confirmed=False):
|
||||
"""Supprime un billet."""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
@ -1307,7 +1309,7 @@ def deleteBilletAbsence(billet_id, REQUEST=None, dialog_confirmed=False):
|
||||
return flask.redirect("listeBillets?head_message=Billet%20supprimé")
|
||||
|
||||
|
||||
def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
def _ProcessBilletAbsence(billet, estjust, description):
|
||||
"""Traite un billet: ajoute absence(s) et éventuellement justificatifs,
|
||||
et change l'état du billet à 1.
|
||||
NB: actuellement, les heures ne sont utilisées que pour déterminer si matin et/ou après-midi.
|
||||
@ -1329,7 +1331,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
dates[0],
|
||||
0,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 1
|
||||
@ -1341,7 +1342,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
dates[-1],
|
||||
1,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 1
|
||||
@ -1353,7 +1353,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
jour,
|
||||
0,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
sco_abs.add_absence(
|
||||
@ -1361,7 +1360,6 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
jour,
|
||||
1,
|
||||
estjust,
|
||||
REQUEST,
|
||||
description=description,
|
||||
)
|
||||
n += 2
|
||||
@ -1372,11 +1370,11 @@ def _ProcessBilletAbsence(billet, estjust, description, REQUEST):
|
||||
return n
|
||||
|
||||
|
||||
@bp.route("/ProcessBilletAbsenceForm")
|
||||
@bp.route("/ProcessBilletAbsenceForm", methods=["POST", "GET"])
|
||||
@scodoc
|
||||
@permission_required(Permission.ScoAbsChange)
|
||||
@scodoc7func
|
||||
def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
||||
def ProcessBilletAbsenceForm(billet_id):
|
||||
"""Formulaire traitement d'un billet"""
|
||||
cnx = ndb.GetDBConnexion()
|
||||
billets = sco_abs.billet_absence_list(cnx, {"billet_id": billet_id})
|
||||
@ -1401,8 +1399,8 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
||||
]
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
request.base_url,
|
||||
scu.get_request_args(),
|
||||
(
|
||||
("billet_id", {"input_type": "hidden"}),
|
||||
(
|
||||
@ -1439,9 +1437,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
||||
elif tf[0] == -1:
|
||||
return flask.redirect(scu.ScoURL())
|
||||
else:
|
||||
n = _ProcessBilletAbsence(
|
||||
billet, tf[2]["estjust"], tf[2]["description"], REQUEST
|
||||
)
|
||||
n = _ProcessBilletAbsence(billet, tf[2]["estjust"], tf[2]["description"])
|
||||
if tf[2]["estjust"]:
|
||||
j = "justifiées"
|
||||
else:
|
||||
@ -1477,7 +1473,7 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None):
|
||||
@scodoc
|
||||
@permission_required_compat_scodoc7(Permission.ScoView)
|
||||
@scodoc7func
|
||||
def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
||||
def XMLgetAbsEtud(beg_date="", end_date=""):
|
||||
"""returns list of absences in date interval"""
|
||||
t0 = time.time()
|
||||
etuds = sco_etud.get_etud_info(filled=False)
|
||||
@ -1493,7 +1489,6 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
||||
|
||||
abs_list = sco_abs.list_abs_date(etud["etudid"], beg_date, end_date)
|
||||
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
doc = ElementTree.Element(
|
||||
"absences", etudid=str(etud["etudid"]), beg_date=beg_date, end_date=end_date
|
||||
)
|
||||
@ -1509,4 +1504,5 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None):
|
||||
)
|
||||
)
|
||||
log("XMLgetAbsEtud (%gs)" % (time.time() - t0))
|
||||
return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
data = sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING)
|
||||
return scu.send_file(data, mime=scu.XML_MIMETYPE, attached=False)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user