From 4b2d5f01a4eb3ed4e64e22912751f277ae055760 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 11:07:53 +0200 Subject: [PATCH 01/47] backup scripts for ScoDoc 9 --- misc/backup_db2 => tools/backups/backup_db9 | 19 +++++++++---------- {misc => tools/backups}/backup_rotation.sh | 2 +- .../backups}/backup_to_remote_server.sh | 18 +++++++++--------- 3 files changed, 19 insertions(+), 20 deletions(-) rename misc/backup_db2 => tools/backups/backup_db9 (66%) rename {misc => tools/backups}/backup_rotation.sh (99%) rename {misc => tools/backups}/backup_to_remote_server.sh (85%) diff --git a/misc/backup_db2 b/tools/backups/backup_db9 similarity index 66% rename from misc/backup_db2 rename to tools/backups/backup_db9 index 77928770..ca760ea8 100755 --- a/misc/backup_db2 +++ b/tools/backups/backup_db9 @@ -1,29 +1,28 @@ #!/bin/bash -# usage: backup_db2 dbname +# usage: backup_db9 dbname # Dump une base postgresql, et garde plusieurs dumps dans le passe # (configurable dans le script backup_rotation.sh) # Les dumps sont compresses (gzip). # -# E. Viennet pour ScoDoc, 2014 +# E. Viennet pour ScoDoc, 2014, 2021 pour ScoDoc 9 # (ce script est meilleur que l'ancien backup-db, car l'espace entre # deux sauvegardes dépend de leur anciennete) # # # Note: pour restaurer un backup (en supprimant la base existante !): # -# 0- Arreter scodoc: /etc/init.d/scodoc stop (ou systemctl stop scodoc) +# 0- Arreter scodoc: systemctl stop scodoc # -# Puis en tant qu'utilisateur postgres: su postgres -# 1- supprimer la base existante si elle existe: dropdb SCOXXX +# Puis en tant qu'utilisateur scodoc: su scodoc +# 1- supprimer la base existante si elle existe: dropdb SCODOC # # 2- recreer la base, vide: createdb -E UTF-8 SCOXXX -# (nom de la base: SCOXXX ou XXX=departement) -# -# 3- pg_restore -d SCOXXX SCOXXX_pgdump +# /opt/scodoc/tools/create_database.sh SCODOC +# 3- pg_restore -d SCODOC SCODOC_pgdump # # Revenir a l'utilisateur root: exit -# 4- Relancer scodoc: /etc/init.d/scodoc start (ou systemctl start scodoc) +# 4- Relancer scodoc: systemctl start scodoc DBNAME=$1 DUMPBASE="$DBNAME"-BACKUPS @@ -50,4 +49,4 @@ pg_dump --format=t "$DBNAME" -f $DUMPFILE gzip $DUMPFILE # 3- Rotate backups: remove unneeded copies -/opt/scodoc/Products/ScoDoc/misc/backup_rotation.sh "$DUMPBASE" +/opt/scodoc/tools/backups/backup_rotation.sh "$DUMPBASE" diff --git a/misc/backup_rotation.sh b/tools/backups/backup_rotation.sh similarity index 99% rename from misc/backup_rotation.sh rename to tools/backups/backup_rotation.sh index 351a1e36..cfab628d 100755 --- a/misc/backup_rotation.sh +++ b/tools/backups/backup_rotation.sh @@ -1,6 +1,6 @@ #!/bin/bash # Backup rotation -# Usage example: backup_rotation.sh /var/lib/postgresql/BACKUP-SCOGEII +# Usage example: backup_rotation.sh /var/lib/postgresql/BACKUP-SCODOC # # This script is designed to run each hour # diff --git a/misc/backup_to_remote_server.sh b/tools/backups/backup_to_remote_server.sh similarity index 85% rename from misc/backup_to_remote_server.sh rename to tools/backups/backup_to_remote_server.sh index 2509ab6a..8638e1bd 100644 --- a/misc/backup_to_remote_server.sh +++ b/tools/backups/backup_to_remote_server.sh @@ -11,7 +11,7 @@ # # A adapter a vos besoins. Utilisation a vos risques et perils. # -# E. Viennet, 2002 +# E. Viennet, 2002, 2021 # Installation: # 1- Installer rsync: @@ -30,8 +30,8 @@ logfile=/var/log/rsynclog # log sur serveur scodoc # A qui envoyer un mail en cas d'erreur de la sauvegarde: SUPERVISORMAIL=emmanuel.viennet@example.com -CALLER=`basename $0` -MACHINE=`hostname -s` +CALLER=$(basename $0) +MACHINE=$(hostname -s) # ----------------------------------------------------- @@ -40,7 +40,7 @@ MACHINE=`hostname -s` # ---------------------------------- terminate() { - dateTest=`date` + dateTest=$(date) mail -s "Attention: Probleme sauvegarde ScoDoc" $SUPERVISORMAIL <> $logfile 2>&1 - echo "starting at" `date` >> $logfile 2>&1 - rsync -vaze ssh --delete --rsync-path=/usr/bin/rsync $srcdir $remotehost":"$destdir >> $logfile 2>&1 + echo "--------------- mirroring $MACHINE:$srcdir to $remotehost:$destdir" >> $logfile 2>&1 + echo "starting at $(date)" >> $logfile 2>&1 + rsync -vaze ssh --delete --rsync-path=/usr/bin/rsync "$srcdir" "$remotehost:$destdir" >> $logfile 2>&1 if [ $? -ne 0 ] then echo Error in rsync: code=$? terminate fi - echo "ending at" `date` >> $logfile 2>&1 + echo "ending at $(date)" >> $logfile 2>&1 echo "---------------" >> $logfile 2>&1 } From badf83cc7fa17b51a22d2bef5c7fa6bc240de2b6 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 15:59:06 +0200 Subject: [PATCH 02/47] fix migration adresses + cosmetique --- README.md | 8 +++++++- app/__init__.py | 4 ++-- app/auth/forms.py | 20 +++++++++---------- app/auth/routes.py | 4 ++-- app/scodoc/html_sidebar.py | 4 ++-- app/scodoc/sco_users.py | 1 + app/static/css/scodoc.css | 3 ++- app/templates/about.html | 25 +++++++++++++++++++++++ app/templates/auth/login.html | 11 ++++++++--- app/templates/scodoc.html | 11 ++++++----- app/views/scodoc.py | 12 ++++++++++++ app/views/scolar.py | 37 ----------------------------------- sco_version.py | 4 ++-- tools/import_scodoc7_dept.py | 1 + 14 files changed, 80 insertions(+), 65 deletions(-) create mode 100644 app/templates/about.html diff --git a/README.md b/README.md index 12d2bbca..b1117729 100644 --- a/README.md +++ b/README.md @@ -148,9 +148,15 @@ Mémo pour développeurs: séquence re-création d'une base: flask import-scodoc7-users flask import-scodoc7-dept STID SCOSTID +Si la base utilisée pour les dev n'est plus en phase avec les scripts de +migration, utiliser les commandes `flask db history`et `flask db stamp`pour se +positionner à la bonne étape. + # Paquet debian 11 -Les scripts associés au paquet Debian (.deb) sont dans `tools/debian`. +Les scripts associés au paquet Debian (.deb) sont dans `tools/debian`. Le plus +important est `postinst`qui se charge de configurer le système (install ou +upgrade de scodoc9). La préparation d'une release se fait à l'aide du script `tools/build_release.sh`. diff --git a/app/__init__.py b/app/__init__.py index a913f57e..33fbd23a 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -32,7 +32,7 @@ db = SQLAlchemy() migrate = Migrate(compare_type=True) login = LoginManager() login.login_view = "auth.login" -login.login_message = "Please log in to access this page." +login.login_message = "Identifiez-vous pour accéder à cette page." mail = Mail() bootstrap = Bootstrap() moment = Moment() @@ -353,4 +353,4 @@ from app.scodoc import sco_cache # click.echo( # "Warning: user database not initialized !\n (use: flask user-db-init)" # ) -# admin = None \ No newline at end of file +# admin = None diff --git a/app/auth/forms.py b/app/auth/forms.py index e3747817..143f6554 100644 --- a/app/auth/forms.py +++ b/app/auth/forms.py @@ -16,20 +16,20 @@ _l = _ class LoginForm(FlaskForm): - user_name = StringField(_l("Username"), validators=[DataRequired()]) - password = PasswordField(_l("Password"), validators=[DataRequired()]) - remember_me = BooleanField(_l("Remember Me")) - submit = SubmitField(_l("Sign In")) + user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()]) + password = PasswordField(_l("Mot de passe"), validators=[DataRequired()]) + remember_me = BooleanField(_l("mémoriser la connexion")) + submit = SubmitField(_l("Suivant")) class UserCreationForm(FlaskForm): - user_name = StringField(_l("Username"), validators=[DataRequired()]) + user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()]) email = StringField(_l("Email"), validators=[DataRequired(), Email()]) - password = PasswordField(_l("Password"), validators=[DataRequired()]) + password = PasswordField(_l("Mot de passe"), validators=[DataRequired()]) password2 = PasswordField( - _l("Repeat Password"), validators=[DataRequired(), EqualTo("password")] + _l("Répéter"), validators=[DataRequired(), EqualTo("password")] ) - submit = SubmitField(_l("Register")) + submit = SubmitField(_l("Inscrire")) def validate_user_name(self, user_name): user = User.query.filter_by(user_name=user_name.data).first() @@ -48,9 +48,9 @@ class ResetPasswordRequestForm(FlaskForm): class ResetPasswordForm(FlaskForm): - password = PasswordField(_l("Password"), validators=[DataRequired()]) + password = PasswordField(_l("Mot de passe"), validators=[DataRequired()]) password2 = PasswordField( - _l("Repeat Password"), validators=[DataRequired(), EqualTo("password")] + _l("Répéter"), validators=[DataRequired(), EqualTo("password")] ) submit = SubmitField(_l("Request Password Reset")) diff --git a/app/auth/routes.py b/app/auth/routes.py index 7b1712f0..b4d107c2 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -38,7 +38,7 @@ def login(): user = User.query.filter_by(user_name=form.user_name.data).first() if user is None or not user.check_password(form.password.data): current_app.logger.info("login: invalid (%s)", form.user_name.data) - flash(_("Invalid user name or password")) + flash(_("Nom ou mot de passe invalide")) return redirect(url_for("auth.login")) login_user(user, remember=form.remember_me.data) current_app.logger.info("login: success (%s)", form.user_name.data) @@ -95,7 +95,7 @@ def reset_password_request(): current_app.logger.info( "reset_password_request: for unkown user '{}'".format(form.email.data) ) - flash(_("Check your email for the instructions to reset your password")) + flash(_("Voir les instructions enoyez par mail")) return redirect(url_for("auth.login")) return render_template( "auth/reset_password_request.html", title=_("Reset Password"), form=form diff --git a/app/scodoc/html_sidebar.py b/app/scodoc/html_sidebar.py index 5ec2ae26..dc0b1295 100644 --- a/app/scodoc/html_sidebar.py +++ b/app/scodoc/html_sidebar.py @@ -152,10 +152,10 @@ def sidebar(): # Logo H.append( f""" diff --git a/app/scodoc/sco_users.py b/app/scodoc/sco_users.py index f7eee673..53437cbe 100644 --- a/app/scodoc/sco_users.py +++ b/app/scodoc/sco_users.py @@ -258,6 +258,7 @@ def user_info(user_name_or_id=None, user=None): info = u.to_dict() else: info = None + user_name = "inconnu" else: info = user.to_dict() user_name = user.user_name diff --git a/app/static/css/scodoc.css b/app/static/css/scodoc.css index 47019d94..ef9df0da 100644 --- a/app/static/css/scodoc.css +++ b/app/static/css/scodoc.css @@ -2614,7 +2614,8 @@ div.maindiv { margin: 1em; } ul.main { - list-style-type: square; + list-style-type: square; + margin-top: 1em; } ul.main li { diff --git a/app/templates/about.html b/app/templates/about.html new file mode 100644 index 00000000..2dc50691 --- /dev/null +++ b/app/templates/about.html @@ -0,0 +1,25 @@ +{% extends 'base.html' %} +{% import 'bootstrap/wtf.html' as wtf %} + +{% block app_content %} + +

Système de gestion scolarité

+ +

© Emmanuel Viennet 2021

+ +

Version {{ version }}

+ +

ScoDoc est un logiciel libre écrit en +Python. +Information et documentation sur scodoc.org. +

+ +

Dernières évolutions

+ +{{ news|safe }} + + + +{% endblock %} \ No newline at end of file diff --git a/app/templates/auth/login.html b/app/templates/auth/login.html index 6b775b73..2685383d 100644 --- a/app/templates/auth/login.html +++ b/app/templates/auth/login.html @@ -2,14 +2,19 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} -

Sign In

+

Connexion

{{ wtf.quick_form(form) }}

-Forgot Your Password? -Click to Reset It +En cas d'oubli de votre mot de passe +cliquez ici pour le réinitialiser.

+ + +

L'accès à ScoDoc est strictement réservé aux personnels de + l'établissement. Les étudiants n'y ont pas accès. Pour toute information, + contactez la personne responsable de votre établissement.

{% endblock %} \ No newline at end of file diff --git a/app/templates/scodoc.html b/app/templates/scodoc.html index f48ad0f4..c0e1eddd 100644 --- a/app/templates/scodoc.html +++ b/app/templates/scodoc.html @@ -2,7 +2,7 @@ {% import 'bootstrap/wtf.html' as wtf %} {% block app_content %} -

ScoDoc: gestion scolarité (version béta)

+

ScoDoc 9 - suivi scolarité

{% if not current_user.is_anonymous %}

Bonjour {{current_user.get_nomcomplet()}} @@ -24,10 +24,6 @@ {% endfor %} -

- Ceci est une version de test, - ne pas utiliser en production ! -

{% if current_user.is_authenticated %}
@@ -43,4 +39,9 @@

Charger la version mobile (expérimentale)

--> +
+ Service réservé aux personnels et enseignants, basé sur le logiciel libre + ScoDoc. +
+ {% endblock %} \ No newline at end of file diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 88aa7a1e..1a7e8537 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -131,6 +131,18 @@ def get_etud_dept(): return Departement.query.get(last_etud.dept_id).acronym +@bp.route("/ScoDoc/about") +@bp.route("/ScoDoc/Scolarite//about") +def about(scodoc_dept=None): + "version info" + return render_template( + "about.html", + version=scu.get_scodoc_version(), + news=sco_version.SCONEWS, + logo=scu.icontag("borgne_img"), + ) + + # ---- CONFIGURATION diff --git a/app/views/scolar.py b/app/views/scolar.py index f05b7748..6b7d6a4f 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -131,34 +131,6 @@ def sco_publish(route, function, permission, methods=["GET"]): # -------------------------------------------------------------------- -@bp.route("/about") -@scodoc -@permission_required(Permission.ScoView) -@scodoc7func -def about(): - "version info" - H = [ - """

Système de gestion scolarité

-

© Emmanuel Viennet 1997-2021

-

Version %s

- """ - % (scu.get_scodoc_version()) - ] - H.append( - '

Logiciel libre écrit en Python.

' - ) - H.append("

Dernières évolutions

" + sco_version.SCONEWS) - H.append( - '" - ) - d = "" - return ( - html_sco_header.sco_header() + "\n".join(H) + d + html_sco_header.sco_footer() - ) - - # -------------------------------------------------------------------- # # PREFERENCES @@ -311,15 +283,6 @@ def showEtudLog(etudid, format="html", REQUEST=None): # ---------- PAGE ACCUEIL (listes) -------------- -# @bp.route("/") -@bp.route("/kimo") -@scodoc -@permission_required(Permission.ScoView) -@scodoc7func -def kimo(REQUEST=None, showcodes=0, showsemtable=0): - import time - - return f"{time.time()} := {g.scodoc_dept}" @bp.route("/") diff --git a/sco_version.py b/sco_version.py index 412da12c..23f90d60 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,14 +1,14 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.13" +SCOVERSION = "9.0.14" SCONAME = "ScoDoc" SCONEWS = """

Année 2021

    -
  • ScoDoc 9: nouvelle architecture logicielle
  • +
  • ScoDoc 9: nouvelle architecture logicielle (Flask/Python3/Debian 11)
  • Version mobile (en test)
  • Évaluations de type "deuxième session"
  • Gestion du genre neutre (pas d'affichage de la civilité)
  • diff --git a/tools/import_scodoc7_dept.py b/tools/import_scodoc7_dept.py index a2fe08ae..2178e285 100644 --- a/tools/import_scodoc7_dept.py +++ b/tools/import_scodoc7_dept.py @@ -380,6 +380,7 @@ def convert_object( "absences", "absences_notifications", "itemsuivi", # etudid n'était pas une clé + "adresse", # etudid n'était pas une clé }: # tables avec "fausses" clés # (l'object référencé a pu disparaitre) From 4bb79e685318143eea697d100706330694943364 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 19:05:19 +0200 Subject: [PATCH 03/47] =?UTF-8?q?Petits=20correctifs=20suite=20=C3=A0=20mi?= =?UTF-8?q?gration=20iutv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_find_etud.py | 2 +- app/views/scodoc.py | 27 +++++++++++++++++++++++++++ tools/import_scodoc7_dept.py | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/app/scodoc/sco_find_etud.py b/app/scodoc/sco_find_etud.py index 13426bea..77109da1 100644 --- a/app/scodoc/sco_find_etud.py +++ b/app/scodoc/sco_find_etud.py @@ -225,7 +225,7 @@ def search_etuds_infos(expnom=None, code_nip=None): else: code_nip = code_nip or expnom if code_nip: - etuds = sco_etud.etudident_list(cnx, args={"code_nip": code_nip}) + etuds = sco_etud.etudident_list(cnx, args={"code_nip": str(code_nip)}) else: etuds = [] sco_etud.fill_etuds_info(etuds) diff --git a/app/views/scodoc.py b/app/views/scodoc.py index 1a7e8537..5bfce95b 100644 --- a/app/views/scodoc.py +++ b/app/views/scodoc.py @@ -37,6 +37,7 @@ import flask from flask import abort, flash, url_for, redirect, render_template, send_file from flask import request from flask.app import Flask +import flask_login from flask_login.utils import login_required from flask_wtf import FlaskForm from flask_wtf.file import FileField, FileAllowed @@ -57,6 +58,7 @@ from app.scodoc import sco_utils as scu from app.decorators import ( admin_required, scodoc7func, + scodoc, permission_required_compat_scodoc7, ) from app.scodoc.sco_permissions import Permission @@ -131,6 +133,31 @@ def get_etud_dept(): return Departement.query.get(last_etud.dept_id).acronym +# Bricolage pour le portail IUTV avec ScoDoc 7: (DEPRECATED: NE PAS UTILISER !) +@bp.route( + "/ScoDoc/search_inscr_etud_by_nip", methods=["GET"] +) # pour compat anciens clients PHP +@scodoc +@scodoc7func +def search_inscr_etud_by_nip( + code_nip, REQUEST=None, format="json", __ac_name="", __ac_password="" +): + auth_ok = False + user_name = __ac_name + user_password = __ac_password + if user_name and user_password: + u = User.query.filter_by(user_name=user_name).first() + if u and u.check_password(user_password): + auth_ok = True + flask_login.login_user(u) + if not auth_ok: + abort(403) + else: + return sco_find_etud.search_inscr_etud_by_nip( + code_nip=code_nip, REQUEST=REQUEST, format=format + ) + + @bp.route("/ScoDoc/about") @bp.route("/ScoDoc/Scolarite//about") def about(scodoc_dept=None): diff --git a/tools/import_scodoc7_dept.py b/tools/import_scodoc7_dept.py index 2178e285..a9a727f5 100644 --- a/tools/import_scodoc7_dept.py +++ b/tools/import_scodoc7_dept.py @@ -381,6 +381,8 @@ def convert_object( "absences_notifications", "itemsuivi", # etudid n'était pas une clé "adresse", # etudid n'était pas une clé + "admissions", # idem + "scolar_events", }: # tables avec "fausses" clés # (l'object référencé a pu disparaitre) From 6880c5bf540e93ad593dcd28cfeb892ed7472cae Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 21:54:53 +0200 Subject: [PATCH 04/47] fix XMLgetAbsEtud --- app/views/absences.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/absences.py b/app/views/absences.py index 294856bd..5896287d 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -1490,7 +1490,7 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None): REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) doc = ElementTree.Element( - "absences", etudid=etud["etudid"], beg_date=beg_date, end_date=end_date + "absences", etudid=str(etud["etudid"]), beg_date=beg_date, end_date=end_date ) for a in Abs: if a["estabs"]: # ne donne pas les justifications si pas d'absence From 104c01ee299dfbd6576af10955cf49dd1a333dcb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 22:15:00 +0200 Subject: [PATCH 05/47] XMLgetFormsemestres --- app/views/notes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/views/notes.py b/app/views/notes.py index 77eea51e..da62ff0b 100644 --- a/app/views/notes.py +++ b/app/views/notes.py @@ -647,7 +647,11 @@ def XMLgetFormsemestres(etape_apo=None, formsemestre_id=None, REQUEST=None): REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE) doc = ElementTree.Element("formsemestrelist") for sem in sco_formsemestre.do_formsemestre_list(args=args): - doc.append("formsemestre", **sem) + for k in sem: + if isinstance(sem[k], int): + sem[k] = str(sem[k]) + sem_elt = ElementTree.Element("formsemestre", **sem) + doc.append(sem_elt) return sco_xml.XML_HEADER + ElementTree.tostring(doc).decode(scu.SCO_ENCODING) From 914c20bcfedf3e5331df98b7270e53b588e84d87 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 22:46:37 +0200 Subject: [PATCH 06/47] fix typo creation users --- app/views/users.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/views/users.py b/app/views/users.py index 557fbfb6..c246d500 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -209,7 +209,7 @@ def create_user_form(REQUEST, user_name=None, edit=0): }, ), ( - "passwd", + "password", { "title": "Mot de passe", "input_type": "password", @@ -219,7 +219,7 @@ def create_user_form(REQUEST, user_name=None, edit=0): }, ), ( - "passwd2", + "password2", { "title": "Confirmer mot de passe", "input_type": "password", @@ -392,11 +392,11 @@ def create_user_form(REQUEST, user_name=None, edit=0): return "\n".join(H) + "\n" + tf[1] + F - if edit: # modif utilisateur (mais pas passwd ni user_name !) + if edit: # modif utilisateur (mais pas password ni user_name !) if (not can_choose_dept) and "dept" in vals: del vals["dept"] - if "passwd" in vals: - del vals["passwd"] + if "password" in vals: + del vals["passwordd"] if "date_modif_passwd" in vals: del vals["date_modif_passwd"] if "user_name" in vals: @@ -442,13 +442,13 @@ def create_user_form(REQUEST, user_name=None, edit=0): ) return "\n".join(H) + msg + "\n" + tf[1] + F # check passwords - if vals["passwd"]: - if vals["passwd"] != vals["passwd2"]: + if vals["password"]: + if vals["password"] != vals["password2"]: msg = tf_error_message( """Les deux mots de passes ne correspondent pas !""" ) return "\n".join(H) + msg + "\n" + tf[1] + F - if not sco_users.is_valid_password(vals["passwd"]): + if not sco_users.is_valid_password(vals["password"]): msg = tf_error_message( """Mot de passe trop simple, recommencez !""" ) From 5c8db849d80a3db6a59c2808f7ff9197efbe6fce Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 22:52:13 +0200 Subject: [PATCH 07/47] version bump 9.0.16 --- sco_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sco_version.py b/sco_version.py index 23f90d60..88ad0a16 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.14" +SCOVERSION = "9.0.16" SCONAME = "ScoDoc" From 8f72e85e74823fcb955a293e822520a8c47412e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sat, 11 Sep 2021 23:42:01 +0200 Subject: [PATCH 08/47] =?UTF-8?q?=C3=A9vite=20d'=C3=A9craser=20la=20config?= =?UTF-8?q?=20nginx=20=C3=A0=20la=20msie=20=C3=A0=20jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/build_release.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/build_release.sh b/tools/build_release.sh index 35c2e379..cf7e61db 100644 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -89,7 +89,12 @@ fi # Puis on déplace les fichiers de config (nginx, systemd, ...) # nginx: mkdir -p "$slash"/etc/nginx/sites-available || die "can't mkdir nginx config" -cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/ || die "can't copy nginx config" +cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/scodoc9.nginx.distrib || die "can't copy nginx config" +# Evite d'écraser: il faudrait ici présenter un dialogue "fichier local modifié, ..." +if [ ! -e "$slash"/etc/nginx/sites-available/scodoc9.nginx ] +then + cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/ || die "can't copy nginx config" +fi # systemd mkdir -p "$slash"/etc/systemd/system/ || die "can't mkdir systemd config" cp -p "$SCODOC_DIR"/tools/etc/scodoc9.service "$slash"/etc/systemd/system/ || die "can't copy scodoc9.service" From c1b06b18b403836cdc10d890a97ac23de43da084 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Sun, 12 Sep 2021 23:06:23 +0200 Subject: [PATCH 09/47] petits bugs (abs) + exc messages --- app/__init__.py | 16 +++++++++++++++- app/scodoc/sco_abs.py | 5 ++++- app/views/absences.py | 7 ++++--- sco_version.py | 2 +- tools/build_release.sh | 6 ++++-- 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 33fbd23a..1f68d0f5 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -90,6 +90,20 @@ class RequestFormatter(logging.Formatter): return super().format(record) +class ScoSMTPHandler(SMTPHandler): + def getSubject(self, record: logging.LogRecord) -> str: + stack_summary = traceback.extract_tb(record.exc_info[2]) + frame_summary = stack_summary[-1] + subject = f"Sco Exc: {record.exc_info[0].__name__} in {frame_summary.name} {frame_summary.filename}" + # stack_summary.reverse() + # with open("/tmp/looooo", "wt") as f: + # for frame_summary in stack_summary: + # f.write( + # f"{record.exc_info[0].__name__} in {frame_summary.name} {frame_summary.filename}\n" + # ) + return subject + + def create_app(config_class=DevConfig): app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static") app.logger.setLevel(logging.DEBUG) @@ -150,7 +164,7 @@ def create_app(config_class=DevConfig): if app.config["MAIL_USE_TLS"]: secure = () host_name = socket.gethostname() - mail_handler = SMTPHandler( + mail_handler = ScoSMTPHandler( mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]), fromaddr="no-reply@" + app.config["MAIL_SERVER"], toaddrs=["exception@scodoc.org"], diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index a0fffc67..c318dd60 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -639,7 +639,10 @@ def add_absence( cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - "insert into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) values (%(etudid)s, %(jour)s, TRUE, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s )", + """ + INSERT into absences (etudid,jour,estabs,estjust,matin,description, moduleimpl_id) + VALUES (%(etudid)s, %(jour)s, true, %(estjust)s, %(matin)s, %(description)s, %(moduleimpl_id)s ) + """, vars(), ) logdb( diff --git a/app/views/absences.py b/app/views/absences.py index 5896287d..d944b84e 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -277,6 +277,7 @@ def doSignaleAbsenceGrSemestre( Efface les absences aux dates indiquées par dates, ou bien ajoute celles de abslist. """ + moduleimpl_id = moduleimpl_id or None if etudids: etudids = etudids.split(",") else: @@ -1486,13 +1487,13 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None): if not exp.match(end_date): raise ScoValueError("invalid date: %s" % end_date) - Abs = sco_abs.list_abs_date(etud["etudid"], beg_date, end_date) + 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 ) - for a in Abs: + for a in abs_list: if a["estabs"]: # ne donne pas les justifications si pas d'absence doc.append( ElementTree.Element( @@ -1500,7 +1501,7 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None): begin=a["begin"], end=a["end"], description=a["description"], - justified=a["estjust"], + justified=int(a["estjust"]), ) ) log("XMLgetAbsEtud (%gs)" % (time.time() - t0)) diff --git a/sco_version.py b/sco_version.py index 88ad0a16..5872639a 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.16" +SCOVERSION = "9.0.18" SCONAME = "ScoDoc" diff --git a/tools/build_release.sh b/tools/build_release.sh index cf7e61db..bd7dca38 100644 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -15,9 +15,11 @@ source "$SCRIPT_DIR/utils.sh" SCODOC_RELEASE=$(grep SCOVERSION "$SCRIPT_DIR/../sco_version.py" | awk '{ print substr($3, 2, length($3)-2) }') # Dernière release -GITEA_RELEASE_URL="https://scodoc.org/git/api/v1/repos/viennet/ScoDoc/releases?pre-release=true" +GITEA_RELEASE_URL="https://scodoc.org/git/api/v1/repos/viennet/ScoDoc/releases" # ?pre-release=true" -LAST_RELEASE_TAG=$(curl "$GITEA_RELEASE_URL" | jq ".[].tag_name" | sort | tail -1 | awk '{ print substr($1, 2, length($1)-2) }') +# suppose que les realse sont nommées 9.0.17, ne considère pas les caractères non numériques +LAST_RELEASE_TAG=$(curl "$GITEA_RELEASE_URL" | jq ".[].tag_name" | tr -d -c "0-9.\n" | sort --version-sort | tail -1) +# | awk '{ print substr($1, 2, length($1)-2) }') echo echo "Version détectée dans le source: $SCODOC_RELEASE" From da204f5df8a0602e5857b9e9244d5c81e65ac99c Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 09:54:53 +0200 Subject: [PATCH 10/47] better exceptions --- app/__init__.py | 21 +++++++++++---------- app/scodoc/sco_exceptions.py | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 1f68d0f5..cc09b792 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -12,7 +12,7 @@ from logging.handlers import SMTPHandler, WatchedFileHandler from flask import current_app, g, request from flask import Flask -from flask import abort, has_request_context +from flask import abort, has_request_context, jsonify from flask import render_template from flask.logging import default_handler from flask_sqlalchemy import SQLAlchemy @@ -24,7 +24,7 @@ from flask_moment import Moment from flask_caching import Cache import sqlalchemy -from app.scodoc.sco_exceptions import ScoValueError +from app.scodoc.sco_exceptions import ScoValueError, APIInvalidParams from config import DevConfig import sco_version @@ -56,6 +56,12 @@ def internal_server_error(e): return render_template("error_500.html", SCOVERSION=sco_version.SCOVERSION), 500 +def handle_invalid_usage(error): + response = jsonify(error.to_dict()) + response.status_code = error.status_code + return response + + def render_raw_html(template_filename: str, **args) -> str: """Load and render an HTML file _without_ using Flask Necessary for 503 error mesage, when DB is down and Flask may be broken. @@ -94,13 +100,8 @@ class ScoSMTPHandler(SMTPHandler): def getSubject(self, record: logging.LogRecord) -> str: stack_summary = traceback.extract_tb(record.exc_info[2]) frame_summary = stack_summary[-1] - subject = f"Sco Exc: {record.exc_info[0].__name__} in {frame_summary.name} {frame_summary.filename}" - # stack_summary.reverse() - # with open("/tmp/looooo", "wt") as f: - # for frame_summary in stack_summary: - # f.write( - # f"{record.exc_info[0].__name__} in {frame_summary.name} {frame_summary.filename}\n" - # ) + subject = f"ScoExc({sco_version.SCOVERSION}): {record.exc_info[0].__name__} in {frame_summary.name} {frame_summary.filename}" + return subject @@ -121,6 +122,7 @@ def create_app(config_class=DevConfig): 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) + app.register_error_handler(APIInvalidParams, handle_invalid_usage) from app.auth import bp as auth_bp @@ -168,7 +170,6 @@ def create_app(config_class=DevConfig): mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]), fromaddr="no-reply@" + app.config["MAIL_SERVER"], toaddrs=["exception@scodoc.org"], - subject="ScoDoc Exception from " + host_name, credentials=auth, secure=secure, ) diff --git a/app/scodoc/sco_exceptions.py b/app/scodoc/sco_exceptions.py index 12aa05a1..5ae9ace4 100644 --- a/app/scodoc/sco_exceptions.py +++ b/app/scodoc/sco_exceptions.py @@ -96,3 +96,20 @@ class ScoGenError(ScoException): class ScoInvalidDateError(ScoValueError): pass + + +# Pour les API JSON +class APIInvalidParams(Exception): + status_code = 400 + + def __init__(self, message, status_code=None, payload=None): + Exception.__init__(self) + self.message = message + if status_code is not None: + self.status_code = status_code + self.payload = payload + + def to_dict(self): + rv = dict(self.payload or ()) + rv["message"] = self.message + return rv From 3527d2bba1a9ef4d5ee20fdf4950c9ffdb73aca8 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 09:55:49 +0200 Subject: [PATCH 11/47] 2 bugs: XMLgetAbsEtud, doSignaleAbsenceGrSemestre --- app/views/absences.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/app/views/absences.py b/app/views/absences.py index d944b84e..1cc93f62 100644 --- a/app/views/absences.py +++ b/app/views/absences.py @@ -79,7 +79,7 @@ from app.scodoc import notesdb as ndb from app import log from app.scodoc.scolog import logdb from app.scodoc.sco_permissions import Permission -from app.scodoc.sco_exceptions import ScoValueError, ScoInvalidDateError +from app.scodoc.sco_exceptions import ScoValueError, APIInvalidParams from app.scodoc.TrivialFormulator import TrivialFormulator from app.scodoc.gen_tables import GenTable from app.scodoc import html_sco_header @@ -279,7 +279,7 @@ def doSignaleAbsenceGrSemestre( """ moduleimpl_id = moduleimpl_id or None if etudids: - etudids = etudids.split(",") + etudids = [int(x) for x in str(etudids).split(",")] else: etudids = [] if dates: @@ -1480,7 +1480,11 @@ def ProcessBilletAbsenceForm(billet_id, REQUEST=None): def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None): """returns list of absences in date interval""" t0 = time.time() - etud = sco_etud.get_etud_info(filled=False)[0] + etuds = sco_etud.get_etud_info(filled=False) + if not etuds: + raise APIInvalidParams("étudiant inconnu") + # raise ScoValueError("étudiant inconnu") + etud = etuds[0] exp = re.compile(r"^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])$") if not exp.match(beg_date): raise ScoValueError("invalid date: %s" % beg_date) @@ -1501,7 +1505,7 @@ def XMLgetAbsEtud(beg_date="", end_date="", REQUEST=None): begin=a["begin"], end=a["end"], description=a["description"], - justified=int(a["estjust"]), + justified=str(int(a["estjust"])), ) ) log("XMLgetAbsEtud (%gs)" % (time.time() - t0)) From e225197e1593802b54dc607b882ff114b34239f5 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 10:00:07 +0200 Subject: [PATCH 12/47] ameliore install nginx/scodoc9 --- tools/build_release.sh | 6 +----- tools/debian/postinst | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/build_release.sh b/tools/build_release.sh index bd7dca38..a3645b63 100644 --- a/tools/build_release.sh +++ b/tools/build_release.sh @@ -92,11 +92,7 @@ fi # nginx: mkdir -p "$slash"/etc/nginx/sites-available || die "can't mkdir nginx config" cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/scodoc9.nginx.distrib || die "can't copy nginx config" -# Evite d'écraser: il faudrait ici présenter un dialogue "fichier local modifié, ..." -if [ ! -e "$slash"/etc/nginx/sites-available/scodoc9.nginx ] -then - cp -p "$SCODOC_DIR"/tools/etc/scodoc9.nginx "$slash"/etc/nginx/sites-available/ || die "can't copy nginx config" -fi + # systemd mkdir -p "$slash"/etc/systemd/system/ || die "can't mkdir systemd config" cp -p "$SCODOC_DIR"/tools/etc/scodoc9.service "$slash"/etc/systemd/system/ || die "can't copy scodoc9.service" diff --git a/tools/debian/postinst b/tools/debian/postinst index 412c298a..3332779f 100755 --- a/tools/debian/postinst +++ b/tools/debian/postinst @@ -80,6 +80,11 @@ su -c "(cd $SCODOC_DIR && python3 -m venv venv)" "$SCODOC_USER" || die "Error cr su -c "(cd $SCODOC_DIR && source venv/bin/activate && pip install wheel && pip install -r requirements-3.9.txt)" "$SCODOC_USER" || die "Error installing python packages" # --- NGINX +# Evite d'écraser: il faudrait ici présenter un dialogue "fichier local modifié, ..." +if [ ! -e /etc/nginx/sites-available/scodoc9.nginx ] +then + cp -p /etc/nginx/sites-available/scodoc9.nginx.distrib /etc/nginx/sites-available/scodoc9.nginx || die "can't copy nginx config" +fi if [ ! -L /etc/nginx/sites-enabled/scodoc9.nginx ] then echo "Enabling scodoc9 in nginx" From 704e0d1055b2776ac546c2763c1d7e711859caaa Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 10:01:12 +0200 Subject: [PATCH 13/47] 9.0.19 --- sco_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sco_version.py b/sco_version.py index 5872639a..f1bee487 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.18" +SCOVERSION = "9.0.19" SCONAME = "ScoDoc" From 1e6c16ab3135f0375edf78e2ac74b83d195e13d3 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 10:06:25 +0200 Subject: [PATCH 14/47] 9.0.19 --- app/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/__init__.py b/app/__init__.py index cc09b792..ae4c0be1 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -170,6 +170,7 @@ def create_app(config_class=DevConfig): mailhost=(app.config["MAIL_SERVER"], app.config["MAIL_PORT"]), fromaddr="no-reply@" + app.config["MAIL_SERVER"], toaddrs=["exception@scodoc.org"], + subject="ScoDoc Exception", # unused see ScoSMTPHandler credentials=auth, secure=secure, ) From 2cfdeb58e5615873bfaf5892813c7a97598ef1ae Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 16:11:33 +0200 Subject: [PATCH 15/47] added cli edit-role --- scodoc.py | 58 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/scodoc.py b/scodoc.py index 3a085227..87cc9677 100755 --- a/scodoc.py +++ b/scodoc.py @@ -7,8 +7,6 @@ """ -from __future__ import print_function - import os from pprint import pprint as pp import sys @@ -16,14 +14,15 @@ import sys import click import flask from flask.cli import with_appcontext + from app import create_app, cli, db from app import initialize_scodoc_database from app import clear_scodoc_cache +from app import models from app.auth.models import User, Role, UserRole -from app import models from app.models import ScoPreference - +from app.scodoc.sco_permissions import Permission from app.views import notes, scolar, absences import tools @@ -45,6 +44,7 @@ def make_shell_context(): "User": User, "Role": Role, "UserRole": UserRole, + "Permission": Permission, "notes": notes, "scolar": scolar, "ndb": ndb, @@ -142,13 +142,59 @@ def user_password(username, password=None): # user-password return 1 u = User.query.filter_by(user_name=username).first() if not u: - sys.stderr.write("user_password: user {} does not exists".format(username)) + sys.stderr.write(f"user_password: user {username} does not exists\n") return 1 u.set_password(password) db.session.add(u) db.session.commit() - click.echo("changed password for user {}".format(u)) + click.echo(f"changed password for user {u}") + + +@app.cli.command() +@click.argument("rolename") +@click.option("-a", "--add", "addpermissionname") +@click.option("-r", "--remove", "removepermissionname") +def edit_role(rolename, addpermissionname=None, removepermissionname=None): # edit-role + """Add [-a] and/or remove [-r] a permission to/from a role. + In ScoDoc, permissions are not associated to users but to roles. + Each user has a set of roles in each departement. + + Example: `flask edit-role -a ScoEditApo Ens` + """ + if addpermissionname: + try: + perm_to_add = Permission.get_by_name(addpermissionname) + except KeyError: + sys.stderr.write( + f"edit_role: permission {addpermissionname} does not exists\n" + ) + return 1 + else: + perm_to_add = None + if removepermissionname: + try: + perm_to_remove = Permission.get_by_name(removepermissionname) + except KeyError: + sys.stderr.write( + f"edit_role: permission {removepermissionname} does not exists\n" + ) + return 1 + else: + perm_to_remove = None + role = Role.query.filter_by(name=rolename).first() + if not role: + sys.stderr.write(f"edit_role: role {rolename} does not exists\n") + return 1 + if perm_to_add: + role.add_permission(perm_to_add) + click.echo(f"adding permission {addpermissionname} to role {rolename}") + if perm_to_remove: + role.remove_permission(perm_to_remove) + click.echo(f"removing permission {removepermissionname} from role {rolename}") + if perm_to_add or perm_to_remove: + db.session.add(role) + db.session.commit() @app.cli.command() From d86a2dfc651f99483c28fb658fefc662e383d3d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 17:10:38 +0200 Subject: [PATCH 16/47] fixes --- app/auth/routes.py | 2 +- app/scodoc/sco_bulletins.py | 5 ++++- app/scodoc/sco_etud.py | 2 +- app/scodoc/sco_formsemestre.py | 5 +++++ app/scodoc/sco_permissions.py | 7 +++++++ app/scodoc/sco_utils.py | 5 ++--- sco_version.py | 2 +- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/app/auth/routes.py b/app/auth/routes.py index b4d107c2..8f01a0c1 100644 --- a/app/auth/routes.py +++ b/app/auth/routes.py @@ -95,7 +95,7 @@ def reset_password_request(): current_app.logger.info( "reset_password_request: for unkown user '{}'".format(form.email.data) ) - flash(_("Voir les instructions enoyez par mail")) + flash(_("Voir les instructions envoyées par mail")) return redirect(url_for("auth.login")) return render_template( "auth/reset_password_request.html", title=_("Reset Password"), form=form diff --git a/app/scodoc/sco_bulletins.py b/app/scodoc/sco_bulletins.py index ab50b6be..54e1d675 100644 --- a/app/scodoc/sco_bulletins.py +++ b/app/scodoc/sco_bulletins.py @@ -444,7 +444,10 @@ def _ue_mod_bulletin(etudid, formsemestre_id, ue_id, modimpls, nt, version): if mod["mod_moy_txt"][:2] == "NA": mod["mod_moy_txt"] = "-" if is_malus: - if mod_moy > 0: + if isinstance(mod_moy, str): + mod["mod_moy_txt"] = "-" + mod["mod_coef_txt"] = "-" + elif mod_moy > 0: mod["mod_moy_txt"] = scu.fmt_note(mod_moy) mod["mod_coef_txt"] = "Malus" elif mod_moy < 0: diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index c653c007..15fa188f 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -440,7 +440,7 @@ def notify_etud_change(email_addr, etud, before, after, subject): "Civilité: " + etud["civilite_str"], "Nom: " + etud["nom"], "Prénom: " + etud["prenom"], - "Etudid: " + etud["etudid"], + "Etudid: " + str(etud["etudid"]), "\n", "Changements effectués:", ] diff --git a/app/scodoc/sco_formsemestre.py b/app/scodoc/sco_formsemestre.py index 3360b847..3bda684a 100644 --- a/app/scodoc/sco_formsemestre.py +++ b/app/scodoc/sco_formsemestre.py @@ -27,6 +27,7 @@ """Operations de base sur les formsemestres """ +from app.scodoc.sco_exceptions import ScoValueError import time from operator import itemgetter @@ -93,6 +94,10 @@ _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""" + ) try: sem = do_formsemestre_list(args={"formsemestre_id": formsemestre_id})[0] return sem diff --git a/app/scodoc/sco_permissions.py b/app/scodoc/sco_permissions.py index 4eee09da..4529e767 100644 --- a/app/scodoc/sco_permissions.py +++ b/app/scodoc/sco_permissions.py @@ -45,13 +45,20 @@ class Permission(object): NBITS = 1 # maximum bits used (for formatting) ALL_PERMISSIONS = [-1] description = {} # { symbol : blah blah } + permission_by_name = {} # { symbol : int } @staticmethod def init_permissions(): for (perm, symbol, description) in _SCO_PERMISSIONS: setattr(Permission, symbol, perm) Permission.description[symbol] = description + Permission.permission_by_name[symbol] = perm Permission.NBITS = len(_SCO_PERMISSIONS) + @staticmethod + def get_by_name(permission_name: str) -> int: + """Return permission mode (integer bit field). May raise keyError.""" + return Permission.permission_by_name[permission_name] + Permission.init_permissions() diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index b897110f..fc5138cf 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -537,10 +537,9 @@ def sendXML(REQUEST, data, tagname=None, force_outer_xml_tag=True): if type(data) != list: data = [data] # always list-of-dicts if force_outer_xml_tag: - root_tagname = tagname + "_list" - data = [{root_tagname: data}] + data = [{tagname: data}] + tagname += "_list" doc = sco_xml.simple_dictlist2xml(data, tagname=tagname) - if REQUEST: REQUEST.RESPONSE.setHeader("content-type", XML_MIMETYPE) return doc diff --git a/sco_version.py b/sco_version.py index f1bee487..2f23bcd5 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.19" +SCOVERSION = "9.0.21" SCONAME = "ScoDoc" From e9d996be41f24252284c8a1d330e601723fe744a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 19:36:53 +0200 Subject: [PATCH 17/47] Fix: affiche nom enseignant et non uid dans historique saisies --- app/scodoc/sco_formsemestre_status.py | 3 ++- app/scodoc/sco_liste_notes.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 1e2862e3..9248ecfa 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -64,7 +64,7 @@ from app.scodoc.sco_formsemestre_custommenu import formsemestre_custommenu_html def _build_menu_stats(formsemestre_id): - "Définition du menu 'Statistiques' " + "Définition du menu 'Statistiques'" return [ { "title": "Statistiques...", @@ -495,6 +495,7 @@ def formsemestre_page_title(): if not formsemestre_id: return "" try: + formsemestre_id = int(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id).copy() except: log("can't find formsemestre_id %s" % formsemestre_id) diff --git a/app/scodoc/sco_liste_notes.py b/app/scodoc/sco_liste_notes.py index 9d96c77c..3478433a 100644 --- a/app/scodoc/sco_liste_notes.py +++ b/app/scodoc/sco_liste_notes.py @@ -50,6 +50,7 @@ from app.scodoc import sco_groups from app.scodoc import sco_moduleimpl from app.scodoc import sco_preferences from app.scodoc import sco_etud +from app.scodoc import sco_users import sco_version from app.scodoc.gen_tables import GenTable from app.scodoc.htmlutils import histogram_notes @@ -569,7 +570,7 @@ def _add_eval_columns( comment = "" explanation = "%s (%s) %s" % ( NotesDB[etudid]["date"].strftime("%d/%m/%y %Hh%M"), - NotesDB[etudid]["uid"], + sco_users.user_info(NotesDB[etudid]["uid"])["nomcomplet"], comment, ) else: From e98302070ab7c748d552763468766321569f401b Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 22:10:01 +0200 Subject: [PATCH 18/47] Fix bug: synchro apogee/dept --- app/models/formsemestre.py | 2 +- app/scodoc/sco_abs.py | 11 +++++------ app/scodoc/sco_abs_notification.py | 5 ++++- app/scodoc/sco_abs_views.py | 8 ++++++-- app/scodoc/sco_cache.py | 2 +- app/scodoc/sco_etud.py | 4 ++-- app/scodoc/sco_inscr_passage.py | 9 +++++++-- app/scodoc/sco_moduleimpl_inscriptions.py | 4 ++-- app/scodoc/sco_synchro_etuds.py | 14 ++++++++++++-- app/templates/error_503.html | 6 +++--- 10 files changed, 43 insertions(+), 22 deletions(-) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 148d9c87..06adb3be 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -19,7 +19,7 @@ class FormSemestre(db.Model): id = db.Column(db.Integer, primary_key=True) formsemestre_id = db.synonym("id") - # dept_id est aussi dans la formation, ajpouté ici pour + # dept_id est aussi dans la formation, ajouté ici pour # simplifier et accélérer les selects dans notesdb dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) formation_id = db.Column(db.Integer, db.ForeignKey("notes_formations.id")) diff --git a/app/scodoc/sco_abs.py b/app/scodoc/sco_abs.py index c318dd60..39d075bb 100644 --- a/app/scodoc/sco_abs.py +++ b/app/scodoc/sco_abs.py @@ -324,15 +324,14 @@ def list_abs_in_range(etudid, debut, fin, matin=None, moduleimpl_id=None, cursor cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - """ -SELECT DISTINCT A.JOUR, A.MATIN -FROM ABSENCES A -WHERE A.ETUDID = %(etudid)s - AND A.ESTABS""" + """SELECT DISTINCT A.JOUR, A.MATIN + FROM ABSENCES A + WHERE A.ETUDID = %(etudid)s + AND A.ESTABS""" + ismatin + modul + """ - AND A.JOUR BETWEEN %(debut)s AND %(fin)s + AND A.JOUR BETWEEN %(debut)s AND %(fin)s """, { "etudid": etudid, diff --git a/app/scodoc/sco_abs_notification.py b/app/scodoc/sco_abs_notification.py index 69f6518b..bf7c5e09 100644 --- a/app/scodoc/sco_abs_notification.py +++ b/app/scodoc/sco_abs_notification.py @@ -218,7 +218,10 @@ def user_nbdays_since_last_notif(email_addr, etudid): cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - """select * from absences_notifications where email = %(email_addr)s and etudid=%(etudid)s order by notification_date desc""", + """SELECT * FROM absences_notifications + WHERE email = %(email_addr)s and etudid=%(etudid)s + ORDER BY notification_date DESC + """, {"email_addr": email_addr, "etudid": etudid}, ) res = cursor.dictfetchone() diff --git a/app/scodoc/sco_abs_views.py b/app/scodoc/sco_abs_views.py index 213a4a3e..8ab6a9a0 100644 --- a/app/scodoc/sco_abs_views.py +++ b/app/scodoc/sco_abs_views.py @@ -628,14 +628,18 @@ def AnnuleAbsencesDatesNoJust(etudid, dates, moduleimpl_id=None): # supr les absences non justifiees for date in dates: cursor.execute( - "delete from absences where etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s", + """DELETE FROM absences + WHERE etudid=%(etudid)s and (not estjust) and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s + """, vars(), ) sco_abs.invalidate_abs_etud_date(etudid, date) # s'assure que les justificatifs ne sont pas "absents" for date in dates: cursor.execute( - "update absences set estabs=FALSE where etudid=%(etudid)s and jour=%(date)s and moduleimpl_id=%(moduleimpl_id)s", + """UPDATE absences SET estabs=FALSE + WHERE etudid=%(etudid)s AND jour=%(date)s AND moduleimpl_id=%(moduleimpl_id)s + """, vars(), ) if dates: diff --git a/app/scodoc/sco_cache.py b/app/scodoc/sco_cache.py index f92ba7bc..50539d88 100644 --- a/app/scodoc/sco_cache.py +++ b/app/scodoc/sco_cache.py @@ -100,7 +100,7 @@ class ScoDocCache: log("Error: cache set failed !") except: log("XXX CACHE Warning: error in set !!!") - + status = None return status @classmethod diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 15fa188f..80981b37 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -307,7 +307,7 @@ def check_nom_prenom(cnx, nom="", prenom="", etudid=None): # Don't allow some special cars (eg used in sql regexps) if scu.FORBIDDEN_CHARS_EXP.search(nom) or scu.FORBIDDEN_CHARS_EXP.search(prenom): return False, 0 - # Now count homonyms: + # Now count homonyms (dans tous les départements): cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) req = """SELECT id FROM identite @@ -896,7 +896,7 @@ def list_scolog(etudid): cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - "select * from scolog where etudid=%(etudid)s ORDER BY DATE DESC", + "SELECT * FROM scolog WHERE etudid=%(etudid)s ORDER BY DATE DESC", {"etudid": etudid}, ) return cursor.dictfetchall() diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index 60787c5f..4bcb5982 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -139,6 +139,7 @@ def list_etuds_from_sem(src, dst): def list_inscrits_date(sem): """Liste les etudiants inscrits dans n'importe quel semestre + du même département SAUF sem à la date de début de sem. """ cnx = ndb.GetDBConnexion() @@ -146,11 +147,15 @@ def list_inscrits_date(sem): sem["date_debut_iso"] = ndb.DateDMYtoISO(sem["date_debut"]) cursor.execute( """SELECT I.etudid - FROM notes_formsemestre_inscription I, notes_formsemestre S - WHERE I.formsemestre_id = S.id + FROM + notes_formsemestre_inscription ins, + notes_formsemestre S, + identite i + 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) """, sem, ) diff --git a/app/scodoc/sco_moduleimpl_inscriptions.py b/app/scodoc/sco_moduleimpl_inscriptions.py index ce1d972c..91650d48 100644 --- a/app/scodoc/sco_moduleimpl_inscriptions.py +++ b/app/scodoc/sco_moduleimpl_inscriptions.py @@ -529,10 +529,10 @@ def do_etud_desinscrit_ue(etudid, formsemestre_id, ue_id, REQUEST=None): cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) cursor.execute( - """DELETE FROM notes_moduleimpl_inscription + """DELETE FROM notes_moduleimpl_inscription WHERE moduleimpl_inscription_id IN ( SELECT i.moduleimpl_inscription_id FROM - notes_moduleimpl mi, notes_modules mod, + notes_moduleimpl mi, notes_modules mod, notes_formsemestre sem, notes_moduleimpl_inscription i WHERE sem.formsemestre_id = %(formsemestre_id)s AND mi.formsemestre_id = sem.formsemestre_id diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index b3a158f5..da7cc3bc 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -400,7 +400,10 @@ def list_synch(sem, anneeapogee=None): def key2etud(key, etud_apo=False): if not etud_apo: etudid = key2etudid[key] - etud = sco_etud.identite_list(cnx, {"etudid": etudid})[0] + etuds = sco_etud.identite_list(cnx, {"etudid": etudid}) + if not etud: # ? cela ne devrait pas arriver XXX + log(f"XXX key2etud etudid={{etudid}}, type {{type(etudid)}}") + etud = etuds[0] etud["inscrit"] = is_inscrit # checkbox state etud[ "datefinalisationinscription" @@ -508,7 +511,14 @@ def list_all(etudsapo_set): # d'interrogation par etudiant. cnx = ndb.GetDBConnexion() cursor = cnx.cursor(cursor_factory=ndb.ScoDocCursor) - cursor.execute("SELECT " + EKEY_SCO + ", id AS etudid FROM identite") + cursor.execute( + "SELECT " + + EKEY_SCO + + """, id AS etudid + FROM identite WHERE dept_id=%(dept_id) + """, + {"dept_id", g.scodoc_dept_id}, + ) key2etudid = dict([(x[0], x[1]) for x in cursor.fetchall()]) all_set = set(key2etudid.keys()) diff --git a/app/templates/error_503.html b/app/templates/error_503.html index ba0ef9e0..723a8717 100644 --- a/app/templates/error_503.html +++ b/app/templates/error_503.html @@ -24,9 +24,9 @@

    Si le problème persiste après intervention de votre équipe locale, contacter la liste "notes" notes@listes.univ-paris13.fr en - indiquant la version du logiciel (ScoDoc {SCOVERSION}) -
    (pour plus d'informations sur les listes de diffusion voir cette page). + indiquant la version du logiciel (ScoDoc {{SCOVERSION}}) +
    (pour plus d'informations sur les listes de diffusion + voir cette page).

    From e6ff4c14607dd3bfa4b437299967398e993cef2a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 23:06:42 +0200 Subject: [PATCH 19/47] =?UTF-8?q?cr=C3=A9ation=20roles=20en=20cli?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_permissions.py | 4 +-- app/views/users.py | 23 +++++++++---- scodoc.py | 64 +++++++++++++++++++++++++++++------ 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/app/scodoc/sco_permissions.py b/app/scodoc/sco_permissions.py index 4529e767..a1a06aba 100644 --- a/app/scodoc/sco_permissions.py +++ b/app/scodoc/sco_permissions.py @@ -57,8 +57,8 @@ class Permission(object): @staticmethod def get_by_name(permission_name: str) -> int: - """Return permission mode (integer bit field). May raise keyError.""" - return Permission.permission_by_name[permission_name] + """Return permission mode (integer bit field), or None if it doesn't exist.""" + return Permission.permission_by_name.get(permission_name) Permission.init_permissions() diff --git a/app/views/users.py b/app/views/users.py index c246d500..17e9987f 100644 --- a/app/views/users.py +++ b/app/views/users.py @@ -97,11 +97,12 @@ def user_info(user_name, format="json", REQUEST=None): @scodoc @permission_required(Permission.ScoUsersAdmin) @scodoc7func -def create_user_form(REQUEST, user_name=None, edit=0): - "form. creation ou edit utilisateur" +def create_user_form(REQUEST, user_name=None, edit=0, all_roles=1): + "form. création ou edition utilisateur" auth_dept = current_user.dept initvalues = {} edit = int(edit) + all_roles = int(all_roles) H = [html_sco_header.sco_header(bodyOnLoad="init_tf_form('')")] F = html_sco_header.sco_footer() if edit: @@ -120,11 +121,19 @@ def create_user_form(REQUEST, user_name=None, edit=0): H.append("""

    Vous êtes super administrateur !

    """) is_super_admin = True - # Les rôles standards créés à l'initialisation de ScoDoc: - standard_roles = [ - Role.get_named_role(r) for r in ("Ens", "Secr", "Admin", "RespPe") - ] - # Rôles pouvant etre attribués aux utilisateurs via ce dialogue: + if all_roles: + # tous sauf SuperAdmin + standard_roles = [ + r + for r in Role.query.all() + if r.permissions != Permission.ALL_PERMISSIONS[0] + ] + else: + # Les rôles standards créés à l'initialisation de ScoDoc: + standard_roles = [ + Role.get_named_role(r) for r in ("Ens", "Secr", "Admin", "RespPe") + ] + # Départements auxquels ont peut associer des rôles via ce dialogue: # si SuperAdmin, tous les rôles standards dans tous les départements # sinon, les départements dans lesquels l'utilisateur a le droit if is_super_admin: diff --git a/scodoc.py b/scodoc.py index 87cc9677..b5176c25 100755 --- a/scodoc.py +++ b/scodoc.py @@ -6,9 +6,8 @@ """ - -import os from pprint import pprint as pp +import re import sys import click @@ -151,6 +150,39 @@ def user_password(username, password=None): # user-password click.echo(f"changed password for user {u}") +@app.cli.command() +@click.argument("rolename") +@click.argument("permissions", nargs=-1) +def create_role(rolename, permissions): # create-role + """Create a new role""" + # Check rolename + if not re.match(r"^[a-zA-Z0-9]+$", rolename): + sys.stderr.write(f"create_role: invalid rolename {rolename}\n") + return 1 + # Check permissions + permission_list = [] + for permission_name in permissions: + perm = Permission.get_by_name(permission_name) + if not perm: + sys.stderr.write(f"create_role: invalid permission name {perm}\n") + sys.stderr.write( + f"\tavailable permissions: {', '.join([ name for name in Permission.permission_by_name])}.\n" + ) + return 1 + permission_list.append(perm) + + role = Role.query.filter_by(name=rolename).first() + if role: + sys.stderr.write(f"create_role: role {rolename} already exists\n") + return 1 + + role = Role(name=rolename) + for perm in permission_list: + role.add_permission(perm) + db.session.add(role) + db.session.commit() + + @app.cli.command() @click.argument("rolename") @click.option("-a", "--add", "addpermissionname") @@ -163,9 +195,8 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e Example: `flask edit-role -a ScoEditApo Ens` """ if addpermissionname: - try: - perm_to_add = Permission.get_by_name(addpermissionname) - except KeyError: + perm_to_add = Permission.get_by_name(addpermissionname) + if not perm_to_add: sys.stderr.write( f"edit_role: permission {addpermissionname} does not exists\n" ) @@ -173,9 +204,8 @@ def edit_role(rolename, addpermissionname=None, removepermissionname=None): # e else: perm_to_add = None if removepermissionname: - try: - perm_to_remove = Permission.get_by_name(removepermissionname) - except KeyError: + perm_to_remove = Permission.get_by_name(removepermissionname) + if not perm_to_remove: sys.stderr.write( f"edit_role: permission {removepermissionname} does not exists\n" ) @@ -237,7 +267,7 @@ def create_dept(dept): # create-dept @app.cli.command() @with_appcontext def import_scodoc7_users(): # import-scodoc7-users - """Import used defined in ScoDoc7 postgresql database into ScoDoc 9 + """Import users defined in ScoDoc7 postgresql database into ScoDoc 9 The old database SCOUSERS must be alive and readable by the current user. This script is typically run as unix user "scodoc". The original SCOUSERS database is left unmodified. @@ -263,7 +293,21 @@ def import_scodoc7_dept(dept: str, dept_db_name: str): # import-scodoc7-dept def clear_cache(): # clear-cache """Clear ScoDoc cache This cache (currently Redis) is persistent between invocation - and it may be necessary to clear it during developement or tests. + and it may be necessary to clear it during development or tests. """ clear_scodoc_cache() click.echo("Redis caches flushed.") + + +def recursive_help(cmd, parent=None): + ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent) + print(cmd.get_help(ctx)) + print() + commands = getattr(cmd, "commands", {}) + for sub in commands.values(): + recursive_help(sub, ctx) + + +@app.cli.command() +def dumphelp(): + recursive_help(app.cli) From 31224d853e3334df5c99e90c23ae45fbdbeb0cbb Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 23:16:02 +0200 Subject: [PATCH 20/47] typo --- app/scodoc/sco_synchro_etuds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index da7cc3bc..6087ef87 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -515,7 +515,7 @@ def list_all(etudsapo_set): "SELECT " + EKEY_SCO + """, id AS etudid - FROM identite WHERE dept_id=%(dept_id) + FROM identite WHERE dept_id=%(dept_id)s """, {"dept_id", g.scodoc_dept_id}, ) From cb3be8c80cb712baf735f3cf6053b102532f9f87 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Mon, 13 Sep 2021 23:36:56 +0200 Subject: [PATCH 21/47] =?UTF-8?q?DEUX=20typos=20bloquant=20la=20synchro=20?= =?UTF-8?q?Apogee=20que=20je=20venais=20de=20r=C3=A9parer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/scodoc/sco_synchro_etuds.py | 4 ++-- sco_version.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scodoc/sco_synchro_etuds.py b/app/scodoc/sco_synchro_etuds.py index 6087ef87..f3d928cb 100644 --- a/app/scodoc/sco_synchro_etuds.py +++ b/app/scodoc/sco_synchro_etuds.py @@ -401,7 +401,7 @@ def list_synch(sem, anneeapogee=None): if not etud_apo: etudid = key2etudid[key] etuds = sco_etud.identite_list(cnx, {"etudid": etudid}) - if not etud: # ? cela ne devrait pas arriver XXX + if not etuds: # ? cela ne devrait pas arriver XXX log(f"XXX key2etud etudid={{etudid}}, type {{type(etudid)}}") etud = etuds[0] etud["inscrit"] = is_inscrit # checkbox state @@ -517,7 +517,7 @@ def list_all(etudsapo_set): + """, id AS etudid FROM identite WHERE dept_id=%(dept_id)s """, - {"dept_id", g.scodoc_dept_id}, + {"dept_id": g.scodoc_dept_id}, ) key2etudid = dict([(x[0], x[1]) for x in cursor.fetchall()]) all_set = set(key2etudid.keys()) diff --git a/sco_version.py b/sco_version.py index 2f23bcd5..112f9f48 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.21" +SCOVERSION = "9.0.22" SCONAME = "ScoDoc" From 61db5426082c190617d36e98cc2b4ee33c704b1b Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Sep 2021 00:07:10 +0200 Subject: [PATCH 22/47] remis le favicon --- app/static/icons/favicon.ico | Bin 0 -> 1406 bytes sco_version.py | 2 +- tools/etc/scodoc9.nginx | 5 ++++- 3 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 app/static/icons/favicon.ico diff --git a/app/static/icons/favicon.ico b/app/static/icons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8838dfa11746c2dbbbb9c327ca2e6b5b5363ac1c GIT binary patch literal 1406 zcmZQzU<5(|0R|w+!H~hqz#zuJz@P!dKp_SNAO?wp0V9M$X4j_rGNc(ZFwC4eli}{2 z+YC{bk_;1TGZ{jS1sRIMZ5XPPJQ*0X84AO! z84mB+!tnq9e}>18A2ZaZ_%K{LcbXy7TaO{qLLA9~qs-9|7!3hhhkzUbz{rLQ80Db= zqK}PHiA`3KjZIu$jtL0l*cf@)_{7-QIOSpT!fYI@Qfv%xd1hr60TD?-QJB2E0-K}+ uJDao|BM<=f%X6{G$n&%Dz~zP5xaDQpl;C_c`_bJ4G6o4iwBaTx4PO9j#7@=# literal 0 HcmV?d00001 diff --git a/sco_version.py b/sco_version.py index 112f9f48..c8fce4a2 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.22" +SCOVERSION = "9.0.23" SCONAME = "ScoDoc" diff --git a/tools/etc/scodoc9.nginx b/tools/etc/scodoc9.nginx index 05f55bee..2cc4ef72 100644 --- a/tools/etc/scodoc9.nginx +++ b/tools/etc/scodoc9.nginx @@ -40,5 +40,8 @@ server { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - } + } + location /favicon.ico { + alias /opt/scodoc/app/static/icons/favicon.ico; + } } From 3496cc7bebc642fee587820b8aabc2910097f961 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Tue, 14 Sep 2021 12:21:22 +0200 Subject: [PATCH 23/47] fix envoi mail etud change --- app/scodoc/sco_etud.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/app/scodoc/sco_etud.py b/app/scodoc/sco_etud.py index 80981b37..46ef0aca 100644 --- a/app/scodoc/sco_etud.py +++ b/app/scodoc/sco_etud.py @@ -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 ", @@ -456,7 +450,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 From 08f651b73462a478c1d975c218d13783392a81fb Mon Sep 17 00:00:00 2001 From: jean-marie Place Date: Tue, 14 Sep 2021 22:45:42 +0200 Subject: [PATCH 24/47] fix export excel en neutralisant les formules comme chaine --- app/scodoc/sco_excel.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/scodoc/sco_excel.py b/app/scodoc/sco_excel.py index c9551b69..b5f9879e 100644 --- a/app/scodoc/sco_excel.py +++ b/app/scodoc/sco_excel.py @@ -224,6 +224,8 @@ class ScoExcelSheet: style -- style par défaut (dictionnaire cf. excel_make_style) de la feuille si non spécifié """ cell = WriteOnlyCell(self.ws, value or "") + if not (isinstance(value, int) or isinstance(value, float)): + cell.data_type = "s" # if style is not None and "fill" in style: # toto() if style is None: From 3fbda90a2fc708b2f2f01a89b42e4bce4cb8f158 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 00:33:30 +0200 Subject: [PATCH 25/47] Better logging. New log for exceptions: /opt/scodoc-data/log/scodoc_exc.log --- app/__init__.py | 54 +++++++++++++++++++++++++++++++----- app/scodoc/sco_utils.py | 13 ++++++++- app/templates/error_500.html | 2 +- config.py | 2 ++ tools/etc/scodoc-logrotate | 11 +++++++- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index ae4c0be1..327976b4 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -17,7 +17,7 @@ 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 @@ -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,31 @@ 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.remote_addr + record.http_referrer = request.referrer + record.http_method = request.method + if request.method == "GET": + record.http_params = str(request.args) + else: + record.http_params = "(post data not loggued)" + else: + record.url = None + record.remote_addr = None + record.http_referrer = None + record.http_method = None + record.http_params = None + record.sco_user = current_user return super().format(record) @@ -148,9 +173,16 @@ def create_app(config_class=DevConfig): absences_bp, url_prefix="/ScoDoc//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 +211,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 +220,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") diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index fc5138cf..f4efae92 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -53,7 +53,7 @@ from flask import g, current_app from PIL import Image as PILImage -from flask import g, url_for, request +from flask import g, url_for, request, make_response from config import Config from app import log @@ -558,6 +558,17 @@ def sendResult(REQUEST, data, name=None, format=None, force_outer_xml_tag=True): raise ValueError("invalid format: %s" % format) +def send_file(self, data, filename, suffix="", mime=None, attached=False): + if suffix: + filename += suffix + filename = make_filename(filename) + response = make_response(data) + response.headers["Content-Type"] = mime + if attached: + response.headers["Content-Disposition"] = 'attachment; filename="%s"' % filename + return response + + def get_scodoc_version(): "return a string identifying ScoDoc version" return sco_version.SCOVERSION diff --git a/app/templates/error_500.html b/app/templates/error_500.html index e093a6af..23963376 100644 --- a/app/templates/error_500.html +++ b/app/templates/error_500.html @@ -11,7 +11,7 @@ ou écrire la liste "notes" notes@listes.univ-paris13.fr en indiquant la version du logiciel
    - (plus d'informations sur les listes de diffusionvoir + (plus d'informations sur les listes de diffusion voir cette page).

    diff --git a/config.py b/config.py index 2e424a58..78e81711 100755 --- a/config.py +++ b/config.py @@ -30,6 +30,8 @@ class Config: SCODOC_DIR = os.environ.get("SCODOC_DIR", "/opt/scodoc") SCODOC_VAR_DIR = os.environ.get("SCODOC_VAR_DIR", "/opt/scodoc-data") SCODOC_LOG_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc.log") + # evite confusion avec le log nginx scodoc_error.log: + SCODOC_ERR_FILE = os.path.join(SCODOC_VAR_DIR, "log", "scodoc_exc.log") # MAX_CONTENT_LENGTH = 10 * 1024 * 1024 # Flask uploads diff --git a/tools/etc/scodoc-logrotate b/tools/etc/scodoc-logrotate index 59e94103..275679c1 100644 --- a/tools/etc/scodoc-logrotate +++ b/tools/etc/scodoc-logrotate @@ -1,4 +1,13 @@ -/opt/scodoc-datalog/scodoc.log { +/opt/scodoc-data/log/scodoc.log { + weekly + missingok + rotate 64 + compress + notifempty + dateext + create 0644 scodoc scodoc +} +/opt/scodoc-datalog/scodoc_exc.log { weekly missingok rotate 64 From 83174f2f5e978ab0b092a5e48fede25ac1670501 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 00:40:19 +0200 Subject: [PATCH 26/47] typo (synchro apo) --- app/scodoc/sco_inscr_passage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index 4bcb5982..d78aba93 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -155,7 +155,7 @@ def list_inscrits_date(sem): 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 ins.dept_id = %(dept_id)s """, sem, ) From 0f9b52bc9b8a0431866c0d5290da947f05361807 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 00:54:18 +0200 Subject: [PATCH 27/47] fix sco_inscr_passage --- app/scodoc/sco_inscr_passage.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index d78aba93..1635dec7 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -146,16 +146,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, + """SELECT ins.etudid + FROM + notes_formsemestre_inscription ins, notes_formsemestre S, - identite i 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)s + AND S.dept_id = %(dept_id)s """, sem, ) From 96f457260fbc89a4fa95c6a9dee0832d514e8c9f Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 00:55:26 +0200 Subject: [PATCH 28/47] version 9.0.25 --- sco_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sco_version.py b/sco_version.py index c8fce4a2..51a6dea7 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.23" +SCOVERSION = "9.0.24" SCONAME = "ScoDoc" From 8a16216d4bc7f34d63afed1dd7fc123e91fdba1a Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 15:19:08 +0200 Subject: [PATCH 29/47] =?UTF-8?q?fixes:=20lien=20params=20seulement=20pour?= =?UTF-8?q?=20admin,=20type=20passage=20=C3=A9tudiants,=20log=20sources=20?= =?UTF-8?q?ips?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/__init__.py | 5 ++++- app/scodoc/sco_inscr_passage.py | 2 +- app/scodoc/sco_preferences.py | 7 +++++-- app/scodoc/sco_saisie_notes.py | 17 +++++++++-------- sco_version.py | 2 +- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 327976b4..83b6c908 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -2,6 +2,7 @@ # pylint: disable=invalid-name import os +import re import socket import sys import time @@ -103,7 +104,9 @@ class LogExceptionFormatter(logging.Formatter): def format(self, record): if has_request_context(): record.url = request.url - record.remote_addr = request.remote_addr + 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": diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py index 1635dec7..04cd2dcd 100644 --- a/app/scodoc/sco_inscr_passage.py +++ b/app/scodoc/sco_inscr_passage.py @@ -149,7 +149,7 @@ def list_inscrits_date(sem): """SELECT ins.etudid FROM notes_formsemestre_inscription ins, - notes_formsemestre S, + notes_formsemestre S WHERE ins.formsemestre_id = S.id AND S.id != %(formsemestre_id)s AND S.date_debut <= %(date_debut_iso)s diff --git a/app/scodoc/sco_preferences.py b/app/scodoc/sco_preferences.py index 26ff4ec2..ccdc6764 100644 --- a/app/scodoc/sco_preferences.py +++ b/app/scodoc/sco_preferences.py @@ -112,6 +112,7 @@ get_base_preferences(formsemestre_id) """ import flask from flask import g, url_for +from flask_login import current_user from app.models import Departement from app.scodoc import sco_cache @@ -2022,7 +2023,9 @@ class BasePreferences(object): html_sco_header.sco_header(page_title="Préférences"), "

    Préférences globales pour %s

    " % scu.ScoURL(), f"""

    modification des logos du département (pour documents pdf)

    """, + }">modification des logos du département (pour documents pdf)

    """ + if current_user.is_administrator() + else "", """

    Ces paramètres s'appliquent par défaut à tous les semestres, sauf si ceux-ci définissent des valeurs spécifiques.

    Attention: cliquez sur "Enregistrer les modifications" en bas de page pour appliquer vos changements !

    """, @@ -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([""]) diff --git a/app/scodoc/sco_saisie_notes.py b/app/scodoc/sco_saisie_notes.py index faba5e2c..bca5722b 100644 --- a/app/scodoc/sco_saisie_notes.py +++ b/app/scodoc/sco_saisie_notes.py @@ -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 """, diff --git a/sco_version.py b/sco_version.py index 51a6dea7..a509e44c 100644 --- a/sco_version.py +++ b/sco_version.py @@ -1,7 +1,7 @@ # -*- mode: python -*- # -*- coding: utf-8 -*- -SCOVERSION = "9.0.24" +SCOVERSION = "9.0.25" SCONAME = "ScoDoc" From 49609fa657ec13af2f34538f7249aa0bd7e82142 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 19:20:41 +0200 Subject: [PATCH 30/47] harmless typo in migration script --- tools/restore_scodoc7_data.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/restore_scodoc7_data.sh b/tools/restore_scodoc7_data.sh index 613e449f..9ff24797 100755 --- a/tools/restore_scodoc7_data.sh +++ b/tools/restore_scodoc7_data.sh @@ -44,7 +44,7 @@ fi # Safety check echo "Ce script recharge les donnees de votre installation ScoDoc 7" echo "sur ce serveur pour migration vers ScoDoc 9." -echo "Ce fichier doit avoir ete cree par le script save_scodoc_data.sh, sur une machine ScoDoc 7." +echo "Ce fichier doit avoir ete cree par le script save_scodoc7_data.sh, sur une machine ScoDoc 7." echo echo -n "Voulez-vous poursuivre cette operation ? (y/n) [n]" read -r ans From 2ec2be423450e99c83eb56bddafa90ec088b3eee Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 20:24:44 +0200 Subject: [PATCH 31/47] fix link --- app/scodoc/sco_formsemestre_status.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scodoc/sco_formsemestre_status.py b/app/scodoc/sco_formsemestre_status.py index 9248ecfa..d1b5e8a8 100644 --- a/app/scodoc/sco_formsemestre_status.py +++ b/app/scodoc/sco_formsemestre_status.py @@ -914,10 +914,10 @@ def formsemestre_status_head(formsemestre_id=None, REQUEST=None, page_title=None html_sco_header.html_sem_header( REQUEST, page_title, sem, with_page_header=False, with_h2=False ), - """ + f"""
    Formation: - %(titre)s""" - % F, + {F['titre']}""", ] if sem["semestre_id"] >= 0: H.append(", %s %s" % (parcours.SESSION_NAME, sem["semestre_id"])) From a34dd656bed496cfbc778544b9da7b9344e461b0 Mon Sep 17 00:00:00 2001 From: Emmanuel Viennet Date: Wed, 15 Sep 2021 22:13:04 +0200 Subject: [PATCH 32/47] Change html header: xhtml -> html5 --- app/scodoc/html_sco_header.py | 75 +++++++++++++--------------------- app/scodoc/html_sidebar.py | 5 ++- app/scodoc/sco_saisie_notes.py | 8 ++-- 3 files changed, 35 insertions(+), 53 deletions(-) diff --git a/app/scodoc/html_sco_header.py b/app/scodoc/html_sco_header.py index 4944f6e4..c204e740 100644 --- a/app/scodoc/html_sco_header.py +++ b/app/scodoc/html_sco_header.py @@ -87,10 +87,6 @@ Problème de connexion (identifiant, mot de passe): contacter votre responsa ) -_TOP_LEVEL_CSS = """ - """ - _HTML_BEGIN = """ @@ -105,31 +101,30 @@ _HTML_BEGIN = """ - - - - + + + - - - + + + - + - + - - + + """ def scodoc_top_html_header(page_title="ScoDoc: bienvenue"): H = [ _HTML_BEGIN % {"page_title": page_title, "encoding": scu.SCO_ENCODING}, - _TOP_LEVEL_CSS, """""", scu.CUSTOM_HTML_HEADER_CNX, ] @@ -185,13 +180,10 @@ def sco_header( init_jquery = True H = [ - """ - - + """ + %(page_title)s - - @@ -206,9 +198,7 @@ def sco_header( ) if init_google_maps: # It may be necessary to add an API key: - H.append( - '' - ) + H.append('') # Feuilles de style additionnelles: for cssstyle in cssstyles: @@ -223,9 +213,9 @@ def sco_header( - - - + + + """ """ ) - H.append( - '' - ) + H.append('') # qTip if init_qtip: H.append( - '' + '' ) H.append( '' @@ -253,32 +241,25 @@ def sco_header( if init_jquery_ui: H.append( - '' - ) - # H.append('') - H.append( - '' + '' ) + # H.append('') + H.append('') if init_google_maps: H.append( - '' + '' ) if init_datatables: H.append( '' ) - H.append( - '' - ) + H.append('') # JS additionels for js in javascripts: - H.append( - """\n""" - % js - ) + H.append("""\n""" % js) H.append( - """