diff --git a/README.md b/README.md index 10bf02c111..0383019518 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ En tant qu'utilisateur `scodoc`: Puis initialisation de l'appli: - flask user-db-init + flask db-init Et saisie du mot de passe `admin`: diff --git a/app/__init__.py b/app/__init__.py index e44391b27d..07160ecd23 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -21,7 +21,7 @@ from flask_bootstrap import Bootstrap from flask_moment import Moment from flask_caching import Cache -from config import Config +from config import DevConfig from app.scodoc import notesdb as ndb from app.scodoc import sco_cache @@ -47,11 +47,12 @@ def handle_sco_value_error(exc): return render_template("sco_value_error.html", exc=exc), 404 -def create_app(config_class=Config): - print("create_app") +def create_app(config_class=DevConfig): app = Flask(__name__, static_url_path="/ScoDoc/static", static_folder="static") app.logger.setLevel(logging.DEBUG) app.config.from_object(config_class) + + app.logger.info(f"create_app({config_class.__name__})") db.init_app(app) migrate.init_app(app, db) login.init_app(app) @@ -128,3 +129,88 @@ def create_app(config_class=Config): app.logger.info("ScoDoc8 startup") return app + + +def user_db_init(): + """Initialize the users database.""" + from app.auth.models import User, Role + + current_app.logger.info("Init User's db") + # Create roles: + Role.insert_roles() + current_app.logger.info("created initial roles") + # Ensure that admin exists + admin_mail = current_app.config.get("SCODOC_ADMIN_MAIL") + if admin_mail: + admin_user_name = current_app.config["SCODOC_ADMIN_LOGIN"] + user = User.query.filter_by(user_name=admin_user_name).first() + if not user: + user = User(user_name=admin_user_name, email=admin_mail) + try: + db.session.add(user) + db.session.commit() + except: + db.session.rollback() + raise + current_app.logger.info( + "created initial admin user, login: {u.user_name}, email: {u.email}".format( + u=user + ) + ) + + +def sco_db_init(): + """Initialize Sco database""" + from app import models + + current_app.logger.info("Init Sco db") + # Modalités: + models.NotesFormModalite.insert_modalites() + + +def initialize_scodoc_database(erase=False): + """Initialize the database. + Starts from an existing database and create all necessary + SQL tables and functions. + If erase is True, _erase_ all database content. + """ + from app import models + + # - our specific functions and sequences, not generated by SQLAlchemy + models.create_database_functions() + # - ERASE (the truncation sql function has been defined above) + if erase: + truncate_database() + # - Create all tables + db.create_all() + # - Insert initial roles and create super-admin user + user_db_init() + # - Insert some constant values (modalites, ...) + sco_db_init() + + +def truncate_database(): + """Erase content of all tables (including users !) from + the current database. + """ + # use a stored SQL function, see createtables.sql + try: + db.session.execute("SELECT truncate_tables('scodoc');") + db.session.commit() + except: + db.session.rollback() + raise + + +# admin_role = Role.query.filter_by(name="SuperAdmin").first() +# if admin_role: +# admin = ( +# User.query.join(UserRole) +# .filter((UserRole.user_id == User.id) & (UserRole.role_id == admin_role.id)) +# .first() +# ) +# else: +# click.echo( +# "Warning: user database not initialized !\n (use: flask user-db-init)" +# ) +# admin = None \ No newline at end of file diff --git a/app/models/__init__.py b/app/models/__init__.py index 3514f31067..40f9ec9240 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -8,7 +8,7 @@ CODE_STR_LEN = 16 # chaine pour les codes SHORT_STR_LEN = 32 # courtes chaine, eg acronymes APO_CODE_STR_LEN = 16 # nb de car max d'un code Apogée -from app.models.raw_sql_init import init_scodoc_database +from app.models.raw_sql_init import create_database_functions from app.models.absences import Absence, AbsenceNotification, BilletAbsence from app.models.entreprises import ( diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 5c1bcc39b7..53e5b66433 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -98,24 +98,30 @@ class NotesFormModalite(db.Model): def insert_modalites(): """Create default modalities""" numero = 0 - for (code, titre) in ( - (NotesFormModalite.DEFAULT_MODALITE, "Formation Initiale"), - ("FAP", "Apprentissage"), - ("FC", "Formation Continue"), - ("DEC", "Formation Décalées"), - ("LIC", "Licence"), - ("CPRO", "Contrats de Professionnalisation"), - ("DIST", "À distance"), - ("ETR", "À l'étranger"), - ("EXT", "Extérieur"), - ("OTHER", "Autres formations"), - ): - modalite = NotesFormModalite.query.filter_by(modalite=code).first() - if modalite is None: - modalite = NotesFormModalite(modalite=code, titre=titre, numero=numero) - db.session.add(modalite) - numero += 1 + try: + for (code, titre) in ( + (NotesFormModalite.DEFAULT_MODALITE, "Formation Initiale"), + ("FAP", "Apprentissage"), + ("FC", "Formation Continue"), + ("DEC", "Formation Décalées"), + ("LIC", "Licence"), + ("CPRO", "Contrats de Professionnalisation"), + ("DIST", "À distance"), + ("ETR", "À l'étranger"), + ("EXT", "Extérieur"), + ("OTHER", "Autres formations"), + ): + modalite = NotesFormModalite.query.filter_by(modalite=code).first() + if modalite is None: + modalite = NotesFormModalite( + modalite=code, titre=titre, numero=numero + ) + db.session.add(modalite) + numero += 1 db.session.commit() + except: + db.session.rollback() + raise class NotesFormsemestreUECoef(db.Model): diff --git a/app/models/raw_sql_init.py b/app/models/raw_sql_init.py index c06cc544fc..0e57c3aee3 100644 --- a/app/models/raw_sql_init.py +++ b/app/models/raw_sql_init.py @@ -8,7 +8,7 @@ using raw SQL from app import db -def init_scodoc_database(): +def create_database_functions(): """Create specific SQL functions and sequences""" # Important: toujours utiliser IF NOT EXISTS # car cette fonction peut être appelée plusieurs fois sur la même db diff --git a/config.py b/config.py index 096adcd28f..923b9e52b3 100755 --- a/config.py +++ b/config.py @@ -37,3 +37,28 @@ class Config: # STATIC_URL_PATH = "/ScoDoc/static" # static_folder = "stat" # SERVER_NAME = os.environ.get("SERVER_NAME") + + +class ProdConfig(Config): + FLASK_ENV = "production" + DEBUG = False + TESTING = False + SQLALCHEMY_DATABASE_URI = ( + os.environ.get("SCODOC_DATABASE_URI") or "postgresql:///SCODOC" + ) + + +class DevConfig(Config): + FLASK_ENV = "development" + DEBUG = True + TESTING = True + SQLALCHEMY_DATABASE_URI = ( + os.environ.get("SCODOC_DEV_DATABASE_URI") or "postgresql:///SCODOC_DEV" + ) + + +class TestConfig(DevConfig): + SQLALCHEMY_DATABASE_URI = ( + os.environ.get("SCODOC_TEST_DATABASE_URI") or "postgresql:///SCODOC_TEST" + ) + SERVER_NAME = "test.gr" diff --git a/scodoc.py b/scodoc.py index 6d2380cc23..0276364107 100755 --- a/scodoc.py +++ b/scodoc.py @@ -16,7 +16,7 @@ import sys import click import flask from flask.cli import with_appcontext -from app import create_app, cli, db +from app import create_app, cli, db, initialize_scodoc_database from app.auth.models import User, Role, UserRole from app import models @@ -24,9 +24,9 @@ from app import models from app.views import notes, scolar, absences import app.utils as utils -from config import Config +from config import DevConfig -app = create_app() +app = create_app(DevConfig) cli.register(app) @@ -36,19 +36,6 @@ def make_shell_context(): from app.scodoc import sco_utils as scu from flask_login import login_user, logout_user, current_user - admin_role = Role.query.filter_by(name="SuperAdmin").first() - if admin_role: - admin = ( - User.query.join(UserRole) - .filter((UserRole.user_id == User.id) & (UserRole.role_id == admin_role.id)) - .first() - ) - else: - click.echo( - "Warning: user database not initialized !\n (use: flask user-db-init)" - ) - admin = None - return { "db": db, "User": User, @@ -64,7 +51,6 @@ def make_shell_context(): "current_user": current_user, "login_user": login_user, "logout_user": logout_user, - "admin": admin, "ctx": app.test_request_context(), "models": models, } @@ -75,42 +61,36 @@ def make_shell_context(): @app.cli.command() -def user_db_init(): # user-db-init - """Initialize the users database.""" - click.echo("Init the db") - # Create roles: - Role.insert_roles() - click.echo("created initial roles") - # Ensure that admin exists - if Config.SCODOC_ADMIN_MAIL: - admin_user_name = Config.SCODOC_ADMIN_LOGIN - user = User.query.filter_by(user_name=admin_user_name).first() - if not user: - user = User(user_name=admin_user_name, email=Config.SCODOC_ADMIN_MAIL) - db.session.add(user) - db.session.commit() - click.echo( - "created initial admin user, login: {u.user_name}, email: {u.email}".format( - u=user - ) - ) - # Modalités: - models.NotesFormModalite.insert_modalites() +def db_init(): # db-init + """Initialize the database. + Starts from an existing database and create all + the necessary SQL tables and functions. + """ + initialize_scodoc_database() @app.cli.command() def user_db_clear(): - """Erase (drop) all tables of users database !""" + """Erase all users and roles from the database !""" click.echo("Erasing the users database !") _clear_users_db() def _clear_users_db(): """Erase (drop) all tables of users database !""" - click.confirm("This will erase all users.\nDo you want to continue?", abort=True) + click.confirm( + "This will erase all users and roles.\nAre you sure you want to continue?", + abort=True, + ) db.reflect() - db.drop_all() - db.session.commit() + try: + db.session.query(UserRole).delete() + db.session.query(User).delete() + db.session.query(User).delete() + db.session.commit() + except: + db.session.rollback() + raise @app.cli.command() @@ -165,6 +145,7 @@ def user_password(username, password=None): # user-password @click.argument("dept") def sco_delete_dept(dept): # sco-delete-dept "Delete existing departement" + raise NotImplementedError() if os.system('tools/delete_dept.sh -n "{}"'.format(dept)): sys.stderr.write("error deleting dept " + dept) return 1 @@ -175,6 +156,7 @@ def sco_delete_dept(dept): # sco-delete-dept @click.argument("dept") def sco_create_dept(dept): # sco-create-dept "Create new departement" + raise NotImplementedError() if os.system(f'tools/create_dept.sh -n "{dept}"'): sys.stderr.write(f"error creating dept {dept}\n") return 1 diff --git a/tests/conftest.py b/tests/conftest.py index 337b5afbb3..db9efe5efa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,40 +3,38 @@ import pytest from flask import g from flask_login import login_user, logout_user, current_user -import app as myapp -from app import db, create_app +from config import TestConfig +from app import db, create_app, initialize_scodoc_database from app import models -from app.auth.models import User, Role, Permission +from app.auth.models import User, Role, UserRole, Permission from app.scodoc import sco_bulletins_standard from app.scodoc import notesdb as ndb -def truncate_database(): - "Erase content of all tables from current dept database" - # use a stored SQL function, see createtables.sql - ndb.SimpleQuery("SELECT truncate_tables('scodoc');", {}) - - @pytest.fixture() def test_client(): # Setup - myapp.Config.TESTING = True - myapp.Config.SQLALCHEMY_DATABASE_URI = "postgresql://scodoc@localhost/SCOTEST00" - myapp.Config.SERVER_NAME = "test.gr" - apptest = create_app() + apptest = create_app(TestConfig) # Run tests: with apptest.test_client() as client: with apptest.app_context(): with apptest.test_request_context(): - models.init_scodoc_database() - db.create_all() - Role.insert_roles() - u = User(user_name="admin") - super_admin_role = Role.query.filter_by(name="SuperAdmin").first() - u.add_role(super_admin_role, "TEST00") - # u.set_password("admin") - login_user(u) - # Vérifie que l'utilisateur bach existe + # erase and reset database: + initialize_scodoc_database(erase=True) + # Loge l'utilisateur super-admin + admin_role = Role.query.filter_by(name="SuperAdmin").first() + assert admin_role + admin_user = ( + User.query.join(UserRole) + .filter( + (UserRole.user_id == User.id) + & (UserRole.role_id == admin_role.id) + ) + .first() + ) + assert admin_user + login_user(admin_user) + # Vérifie que l'utilisateur "bach" existe u = User.query.filter_by(user_name="bach").first() if u is None: u = User(user_name="bach") diff --git a/tools/create_database.sh b/tools/create_database.sh index fe1ff40329..1fd4599f0b 100755 --- a/tools/create_database.sh +++ b/tools/create_database.sh @@ -1,9 +1,7 @@ #!/bin/bash -# Create database for a ScoDoc departement -# This script must be executed as postgres super user -# -# $db_name is passed as an environment variable +# Create database for ScoDoc +# This script must be executed as user "scodoc" die() { echo @@ -11,16 +9,16 @@ die() { echo exit 1 } +[ $# = 1 ] || die "Usage $0 db_name" +db_name="$1" -source config.sh || die "config.sh not found, exiting" -source utils.sh || die "config.sh not found, exiting" +# Le répertoire de ce script: +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" -if [ "$db_name" == "" ] -then - echo "Error: env var db_name unset" - echo "(ce script ne doit pas être lancé directement !)" - exit 1 -fi +source "$SCRIPT_DIR"/config.sh || die "config.sh not found, exiting" +source "$SCRIPT_DIR"/utils.sh || die "config.sh not found, exiting" + +[ "$USER" = "$SCODOC_USER" ] || die "$0 must run as user $SCODOC_USER" # --- echo 'Creating postgresql database ' "$db_name" diff --git a/tools/create_dept.sh b/tools/create_dept.sh deleted file mode 100755 index 4d81ce449b..0000000000 --- a/tools/create_dept.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# -# ScoDoc: creation initiale d'un departement -# -# Ce script prend en charge la creation de la base de donnees -# et doit être lancé par l'utilisateur unix root dans le repertoire .../tools -# ^^^^^^^^^^^^^^^^^^^^^ -# E. Viennet, Juin 2008 -# - -set -eo pipefail - -# Le répertoire de ce script: -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -source "$SCRIPT_DIR/config.sh" -source "$SCRIPT_DIR/utils.sh" - -[ "$USER" = "$SCODOC_USER" ] || die "$0 must run as user $SCODOC_USER" - -cd "$SCRIPT_DIR" - -usage() { - echo "$0 [-n DEPT]" - echo "(default to interactive mode)" - exit 1 -} -[ $# = 0 ] || [ $# = 2 ] || usage - -if [ "$1" = "-n" ] -then - interactive=0 - if [ $# -lt 2 ] - then - usage - fi - DEPT=$2 -else - interactive=1 - echo -n "Nom du departement (un mot sans ponctuation, exemple \"Info\"): " - read -r DEPT -fi - -if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] -then - echo 'Nom de departement invalide !' - exit 2 -fi - -export DEPT - -db_name=SCO$(to_upper "$DEPT") -export db_name - -cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg - -if [ -e "$cfg_pathname" ] -then - echo 'Erreur: Il existe deja une configuration pour "'"$DEPT"'"' - exit 1 -fi - -# ----------------------- Create Dept database -# (créée en tant qu'utilisateur scodoc) -./create_database.sh - -# ----------------------- Initialize table database -# POSTGRES_USER == regular unix user (scodoc) -if [ "$interactive" = 1 ] -then - ./initialize_database.sh -else - ./initialize_database.sh > /dev/null 2>&1 -fi - - - -# ----------------------- Enregistre fichier config -echo "dbname=${db_name}" > "$cfg_pathname" - - -if [ "$interactive" = 1 ] -then - # ----------------------- - echo - echo " Departement $DEPT cree" - echo - echo " Attention: la base de donnees n'a pas de copies de sauvegarde" - echo "(XXX section à revoir en ScoDoc 8.1)" # #sco8 -fi diff --git a/tools/create_users_database.sh b/tools/create_users_database.sh deleted file mode 100755 index afcd8292e5..0000000000 --- a/tools/create_users_database.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Create USERS database for ScoDoc 8 -# This script must be executed as root -# -# $db_name is passed as an environment variable - -# Le répertoire de ce script: -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -source "$SCRIPT_DIR/config.sh" -source "$SCRIPT_DIR/utils.sh" - - -check_uid_root "$0" - -# 1--- CREATION UTILISATEUR POSTGRESQL -init_postgres_user - -# 2--- CREATION BASE UTILISATEURS -echo 'Creating postgresql database for users:' "$SCODOC_USER_DB" -su -c "createdb -E UTF-8 -p $POSTGRES_PORT -O $SCODOC_USER $SCODOC_USER_DB" "$POSTGRES_SUPERUSER" diff --git a/tools/delete_dept.sh b/tools/delete_dept.sh deleted file mode 100755 index 6ebaff9477..0000000000 --- a/tools/delete_dept.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash - -# -# ScoDoc: suppression d'un departement -# -# Ce script supprime la base de donnees ScoDoc d'un departement -# *** le departement doit au prealable avoir été supprime via l'interface web ! *** -# -# Ne fonctionne que pour les configurations "standards" (dbname=xxx) -# -# Il doit être lancé par l'utilisateur unix root dans le repertoire .../tools -# ^^^^^^^^^^^^^^^^^^^^^ -# E. Viennet, Sept 2008 -# - -# Le répertoire de ce script: -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" - -source "$SCRIPT_DIR/config.sh" -source "$SCRIPT_DIR/utils.sh" - -check_uid_root "$0" -usage() { - echo "$0 [-n DEPT]" - echo "(default to interactive mode)" - exit 1 -} -[ $# = 0 ] || [ $# = 2 ] || usage -if [ "$1" = "-n" ] -then - interactive=0 - if [ $# -lt 2 ] - then - usage - fi - DEPT=$2 -else - interactive=1 - echo - echo "Ce script supprime la base de donnees ScoDoc d'un departement" - echo - echo "Attention: le departement doit au prealable avoir ete supprime via l'interface web !" - echo "faites le AVANT d'executer ce script !!!" - echo - echo -n "Nom du departement a supprimer (un mot sans ponctuation, exemple \"Info\"): " - read -r DEPT -fi - -if [[ ! "$DEPT" =~ ^[A-Za-z0-9]+$ ]] -then - echo "Nom de departement invalide !" - exit 1 -fi -export DEPT - -cfg_pathname="${SCODOC_VAR_DIR}/config/depts/$DEPT".cfg - -if [ -e "$cfg_pathname" ] -then - # arret de ScoDoc - scodocctl stop - - # suppression de la base postgres - db_name=$(sed '/^dbname=*/!d; s///;q' < "$cfg_pathname") - if su -c "psql -lt" "$POSTGRES_SUPERUSER" | cut -d \| -f 1 | grep -wq "$db_name" - then - echo "Suppression de la base postgres $db_name ..." - su -c "dropdb $db_name" "$POSTGRES_SUPERUSER" || terminate "ne peux supprimer base de donnees $db_name" - else - echo "la base postgres $db_name n'existe pas." - fi - # suppression du fichier de config - /bin/rm -f "$cfg_pathname" || terminate "Ne peux supprimer $cfg_pathname" - # relance ScoDoc - if [ "$interactive" = 1 ] - then - echo -n "Demarrer le serveur ScoDoc ? (y/n) [n]" - read -r ans - if [ "$(norm_ans "$ans")" = 'Y' ] - then - scodocctl start - fi - fi - exit 0 -else - echo 'Attention: pas de configuration trouvee pour "'"$DEPT"'"' - echo " fichier cherché: $cfg_pathname" - echo " => ne fait rien." - exit 0 -fi diff --git a/tools/postupgrade-db.py b/tools/postupgrade-db.py deleted file mode 100755 index 613d5482d6..0000000000 --- a/tools/postupgrade-db.py +++ /dev/null @@ -1,1112 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -""" -ScoDoc post-upgrade script: databases housekeeping - -This script is runned by upgrade.sh after each SVN update. - -Runned as "scodoc" with ScoDoc shutted down and postgresql up. - - -Useful to update database schema (eg add new tables or columns to -existing scodoc instances). - -E. Viennet, june 2008, sept 2013 -""" - -from scodocutils import * - -for dept in get_depts(): - log("\nChecking database for dept %s" % dept) - cnx_string = None - try: - cnx_string = get_dept_cnx_str(dept) - cnx = psycopg2.connect(cnx_string) - except: - log("\n*** Error: departement %s not upgraded ! ***\n" % dept) - log('connexion string was "%s"' % cnx_string) - traceback.print_exc() - continue - cnx.set_session(autocommit=False) - cursor = cnx.cursor() - # Apply upgrades: - - # SVN 564 -> 565 - # add resp_can_edit to notes_formsemestre: - check_field( - cnx, - "notes_formsemestre", - "resp_can_edit", - [ - "alter table notes_formsemestre add column resp_can_edit int default 0", - "update notes_formsemestre set resp_can_edit=0", - ], - ) - - # SVN 580 -> 581 - # add resp_can_change_ens to notes_formsemestre: - check_field( - cnx, - "notes_formsemestre", - "resp_can_change_ens", - [ - "alter table notes_formsemestre add column resp_can_change_ens int default 1", - "update notes_formsemestre set resp_can_change_ens=1", - ], - ) - - # Fix bug ayant empeche la creation de la table - check_table( - cnx, - "admissions", - [ - """CREATE TABLE admissions ( - adm_id text DEFAULT notes_newid_etud('ADM'::text) NOT NULL, - etudid text NOT NULL, - annee integer, - bac text, - specialite text, - annee_bac integer, - math real, - physique real, - anglais real, - francais real, - rang integer, -- dans les voeux du candidat (inconnu avec APB) - qualite real, - rapporteur text, - decision text, - score real, - commentaire text, - nomlycee text, - villelycee text, - codepostallycee text, - codelycee text, - debouche text, -- situation APRES etre passe par chez nous (texte libre) - type_admission text, -- 'APB', 'APC-PC', 'CEF', 'Direct', '?' (autre) - boursier_prec integer default NULL, -- etait boursier dans le cycle precedent (lycee) ? - classement integer default NULL, -- classement par le jury d'admission (1 à N), global (pas celui d'APB si il y a des groupes) - apb_groupe text, -- code du groupe APB - apb_classement_gr integer default NULL -- classement (1..Ngr) par le jury dans le groupe APB -) WITH OIDS; -""" - ], - ) - - # SVN 651 - # Nouvelles donnees d'admission - check_field( - cnx, - "admissions", - "codelycee", - [ - "alter table admissions add column codelycee text", - ], - ) - check_field( - cnx, - "admissions", - "codepostallycee", - [ - "alter table admissions add column codepostallycee text", - ], - ) - - # New preferences system - check_field( - cnx, - "sco_prefs", - "formsemestre_id", - [ - "alter table sco_prefs add column pref_id text DEFAULT notes_newid('PREF'::text) UNIQUE NOT NULL", - "update sco_prefs set pref_id=oid", - "alter table sco_prefs add column formsemestre_id text default NULL", - "alter table sco_prefs drop CONSTRAINT sco_prefs_pkey", - "alter table sco_prefs add unique( name, formsemestre_id)", - # copie anciennes prefs: - "insert into sco_prefs (name, value, formsemestre_id) select 'left_margin', left_margin, formsemestre_id from notes_formsemestre_pagebulletin", - "insert into sco_prefs (name, value, formsemestre_id) select 'top_margin', top_margin, formsemestre_id from notes_formsemestre_pagebulletin", - "insert into sco_prefs (name, value, formsemestre_id) select 'right_margin', right_margin, formsemestre_id from notes_formsemestre_pagebulletin", - "insert into sco_prefs (name, value, formsemestre_id) select 'bottom_margin', bottom_margin, formsemestre_id from notes_formsemestre_pagebulletin", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_title', title, formsemestre_id from notes_formsemestre_pagebulletin", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_intro_mail', intro_mail, formsemestre_id from notes_formsemestre_pagebulletin", - "drop table notes_formsemestre_pagebulletin", - # anciens champs de formsemestre: - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_abs', gestion_absence, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column gestion_absence", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_decision', bul_show_decision, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_decision", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_uevalid', bul_show_uevalid, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_uevalid", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_codemodules', bul_show_codemodules, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_codemodules", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_rangs', bul_show_rangs, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_rangs", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_ue_rangs', bul_show_ue_rangs, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_ue_rangs", - "insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_mod_rangs', bul_show_mod_rangs, formsemestre_id from notes_formsemestre", - "alter table notes_formsemestre drop column bul_show_mod_rangs", - ], - ) - # fixed previous bug (misspelled pref) - cursor.execute( - "update sco_prefs set name = 'bul_show_codemodules' where name = 'bul_showcodemodules'" - ) - - # billets d'absences - if not sequence_exists(cnx, "notes_idgen_billets"): - log("creating sequence notes_idgen_billets") - cursor.execute("CREATE SEQUENCE notes_idgen_billets;") - - if not function_exists(cnx, "notes_newid_billet"): - log("creating function notes_newid_billet") - cursor.execute( - """CREATE FUNCTION notes_newid_billet( text ) returns text as ' - select $1 || to_char( nextval(''notes_idgen_billets''), ''FM999999999'' ) - as result; - ' language SQL;""" - ) - - check_table( - cnx, - "billet_absence", - [ - """CREATE TABLE billet_absence ( - billet_id text DEFAULT notes_newid_billet('B'::text) NOT NULL, - etudid text NOT NULL, - abs_begin timestamp with time zone, - abs_end timestamp with time zone, - description text, -- "raison" de l'absence - etat integer default 0 -- 0 new, 1 processed -) WITH OIDS; -""" - ], - ) - - # description absence - check_field( - cnx, - "absences", - "description", - ["alter table absences add column description text"], - ) - check_field( - cnx, - "absences", - "entry_date", - [ - "alter table absences add column entry_date timestamp with time zone DEFAULT now()" - ], - ) - check_field( - cnx, - "billet_absence", - "entry_date", - [ - "alter table billet_absence add column entry_date timestamp with time zone DEFAULT now()" - ], - ) - # Nouvelles preferences pour bulletins PDF: migre bul_show_chiefDept - cursor.execute( - "update sco_prefs set name = 'bul_show_sig_right' where name = 'bul_show_chiefDept'" - ) - # cursor.execute("insert into sco_prefs (name, value, formsemestre_id) select 'bul_show_sig_left', value, formsemestre_id from sco_prefs where name = 'bul_show_sig_right'") - # date et lieu naissance (pour IFAG Sofia) - check_field( - cnx, - "identite", - "date_naissance", - [ - "alter table identite add column date_naissance date", - "update identite set date_naissance=to_date(to_char( annee_naissance, 'FM9999') || '-01-01', 'YYYY-MM-DD')", - "alter table identite drop column annee_naissance", - ], - ) - check_field( - cnx, - "identite", - "lieu_naissance", - ["alter table identite add column lieu_naissance text"], - ) - # justification billets: - check_field( - cnx, - "billet_absence", - "justified", - [ - "alter table billet_absence add column justified integer default 0", - "update billet_absence set justified=0", - ], - ) - - # ----------------------- New groups - # 1- Create new tables - check_table( - cnx, - "partition", - [ - """CREATE TABLE partition( - partition_id text default notes_newid2('P') PRIMARY KEY, - formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id), - partition_name text, -- "TD", "TP", ... - compute_ranks integer default 1, -- calcul rang etudiants dans les groupes - numero SERIAL, -- ordre de presentation - UNIQUE(formsemestre_id,partition_name) -) WITH OIDS; -""" - ], - ) - check_table( - cnx, - "group_descr", - [ - """CREATE TABLE group_descr ( - group_id text default notes_newid2('G') PRIMARY KEY, - partition_id text REFERENCES partition(partition_id), - group_name text, -- "A", "C2", ... - UNIQUE(partition_id, group_name) -) WITH OIDS; -""" - ], - ) - check_table( - cnx, - "group_membership", - [ - """CREATE TABLE group_membership( - group_membership_id text default notes_newid2('GM') PRIMARY KEY, - etudid text REFERENCES identite(etudid), - group_id text REFERENCES group_descr(group_id), - UNIQUE(etudid, group_id) -) WITH OIDS; -""" - ], - ) - - # 2- For each sem, create 1 to 4 partitions: all, TD (if any), TP, TA - # Here we have to deal with plain SQL, nasty... - if field_exists(cnx, "notes_formsemestre_inscription", "groupetd"): - # Some very old stduents didn't have addresses: it's now mandatory - cursor.execute( - "insert into adresse (etudid) select etudid from identite i except select etudid from adresse" - ) - # - cursor.execute("SELECT formsemestre_id from notes_formsemestre") - formsemestre_ids = [x[0] for x in cursor.fetchall()] - for formsemestre_id in formsemestre_ids: - # create "all" partition (with empty name) - cursor.execute( - "INSERT into partition (formsemestre_id, compute_ranks) VALUES (%(formsemestre_id)s, 1)", - {"formsemestre_id": formsemestre_id}, - ) - cursor.execute( - "select partition_id from partition where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - partition_id = cursor.fetchone()[0] - # create group "all" (without name) - cursor.execute( - "INSERT into group_descr (partition_id) VALUES (%(pid)s)", - {"pid": partition_id}, - ) - cursor.execute( - "SELECT group_id from group_descr where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - group_id = cursor.fetchone()[0] - # inscrit etudiants: - cursor.execute( - "INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", - {"group_id": group_id, "formsemestre_id": formsemestre_id}, - ) - - # create TD, TP, TA - cursor.execute( - "SELECT distinct(groupetd) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - groupetds = [x[0] for x in cursor.fetchall() if x[0]] - if len(groupetds) > 1 or (len(groupetds) == 1 and groupetds[0] != "A"): - # TD : create partition - cursor.execute( - "SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - nomgroupetd = cursor.dictfetchone()["nomgroupetd"] - if ( - not nomgroupetd - ): # pas de nom ??? on invente un nom stupide et unique - nomgroupetd = "TD_" + str(time.time()).replace(".", "")[-3:] - cursor.execute( - "INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupetd)s)", - {"formsemestre_id": formsemestre_id, "nomgroupetd": nomgroupetd}, - ) - cursor.execute( - "select partition_id from partition where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - partition_id = cursor.fetchone()[0] - # create groups - for groupetd in groupetds: - cursor.execute( - "INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", - {"pid": partition_id, "group_name": groupetd}, - ) - cursor.execute( - "SELECT group_id from group_descr where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - group_id = cursor.fetchone()[0] - # inscrit les etudiants - cursor.execute( - "INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupetd=%(groupetd)s", - { - "group_id": group_id, - "formsemestre_id": formsemestre_id, - "groupetd": groupetd, - }, - ) - # TA - cursor.execute( - "SELECT distinct(groupeanglais) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - groupetds = [x[0] for x in cursor.fetchall() if x[0]] - if len(groupetds) > 0: - # TA : create partition - cursor.execute( - "SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - nomgroupetd = cursor.dictfetchone()["nomgroupeta"] - if ( - not nomgroupetd - ): # pas de nom ??? on invente un nom stupide et unique - nomgroupetd = "TA_" + str(time.time()).replace(".", "")[-3:] - cursor.execute( - "INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupeta)s)", - {"formsemestre_id": formsemestre_id, "nomgroupeta": nomgroupetd}, - ) - cursor.execute( - "select partition_id from partition where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - partition_id = cursor.fetchone()[0] - # create groups - for groupetd in groupetds: - cursor.execute( - "INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", - {"pid": partition_id, "group_name": groupetd}, - ) - cursor.execute( - "SELECT group_id from group_descr where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - group_id = cursor.fetchone()[0] - # inscrit les etudiants - cursor.execute( - "INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupeanglais=%(groupetd)s", - { - "group_id": group_id, - "formsemestre_id": formsemestre_id, - "groupetd": groupetd, - }, - ) - - # TP - cursor.execute( - "SELECT distinct(groupetp) from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - groupetds = [x[0] for x in cursor.fetchall() if x[0]] - if len(groupetds) > 0: - # TP : create partition - cursor.execute( - "SELECT * from notes_formsemestre where formsemestre_id=%(formsemestre_id)s", - {"formsemestre_id": formsemestre_id}, - ) - nomgroupetd = cursor.dictfetchone()["nomgroupetp"] - if ( - not nomgroupetd - ): # pas de nom ??? on invente un nom stupide et unique - nomgroupetd = "TP_" + str(time.time()).replace(".", "")[-3:] - cursor.execute( - "INSERT into partition (formsemestre_id, partition_name) VALUES (%(formsemestre_id)s,%(nomgroupeta)s)", - {"formsemestre_id": formsemestre_id, "nomgroupeta": nomgroupetd}, - ) - cursor.execute( - "select partition_id from partition where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - partition_id = cursor.fetchone()[0] - # create groups - for groupetd in groupetds: - cursor.execute( - "INSERT into group_descr (partition_id, group_name) VALUES (%(pid)s, %(group_name)s)", - {"pid": partition_id, "group_name": groupetd}, - ) - cursor.execute( - "SELECT group_id from group_descr where oid=%(oid)s", - {"oid": cursor.lastoid()}, - ) - group_id = cursor.fetchone()[0] - # inscrit les etudiants - cursor.execute( - "INSERT into group_membership (etudid, group_id) SELECT etudid, %(group_id)s from notes_formsemestre_inscription where formsemestre_id=%(formsemestre_id)s and groupetp=%(groupetd)s", - { - "group_id": group_id, - "formsemestre_id": formsemestre_id, - "groupetd": groupetd, - }, - ) - - # 3- Suppress obsolete fields - cursor.execute("""alter table notes_formsemestre drop column nomgroupetd""") - cursor.execute("""alter table notes_formsemestre drop column nomgroupetp""") - cursor.execute("""alter table notes_formsemestre drop column nomgroupeta""") - - cursor.execute( - """alter table notes_formsemestre_inscription drop column groupetd""" - ) - cursor.execute( - """alter table notes_formsemestre_inscription drop column groupetp""" - ) - cursor.execute( - """alter table notes_formsemestre_inscription drop column groupeanglais""" - ) - # ----------------------- /New groups - - # Add moy_ue to validations: - check_field( - cnx, - "scolar_formsemestre_validation", - "moy_ue", - [ - "alter table scolar_formsemestre_validation add column moy_ue real", - ], - ) - # Add photo_filename - check_field( - cnx, - "identite", - "photo_filename", - [ - "alter table identite add column photo_filename text", - ], - ) - # Add module's ECTS - check_field( - cnx, - "notes_modules", - "ects", - [ - "alter table notes_modules add column ects real", - ], - ) - # Add "statut" to identite (default to NULL) - check_field( - cnx, - "identite", - "statut", - [ - "alter table identite add column statut text", - ], - ) - # Add user-defined expressions - check_field( - cnx, - "notes_moduleimpl", - "computation_expr", - ["alter table notes_moduleimpl add column computation_expr text"], - ) - # Add semestre_id to scolar_formsemestre_validation - check_field( - cnx, - "scolar_formsemestre_validation", - "semestre_id", - ["alter table scolar_formsemestre_validation add column semestre_id int"], - ) - - # Add - check_table( - cnx, - "absences_notifications", - [ - """ - CREATE TABLE absences_notifications ( - etudid text NOT NULL, - notification_date timestamp with time zone DEFAULT now(), - email text NOT NULL, - nbabs integer, - nbabsjust integer - ) WITH OIDS; - """ - ], - ) - # rename old preference "send_mail_absence_to_chef" - cursor.execute( - "update sco_prefs set name = 'abs_notify_chief' where name = 'send_mail_absence_to_chef'" - ) - - check_table( - cnx, - "notes_formsemestre_ue_computation_expr", - [ - """ - CREATE TABLE notes_formsemestre_ue_computation_expr ( - notes_formsemestre_ue_computation_expr_id text default notes_newid('UEXPR') PRIMARY KEY, - formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id), - ue_id text REFERENCES notes_ue(ue_id), - computation_expr text, -- formule de calcul moyenne - UNIQUE(formsemestre_id, ue_id) - ) WITH OIDS; - """ - ], - ) - - # add moduleimpl_id to absences: - check_field( - cnx, - "absences", - "moduleimpl_id", - ["alter table absences add column moduleimpl_id text"], - ) - - # add type_parcours - check_field( - cnx, - "notes_formations", - "type_parcours", - [ - "alter table notes_formations add column type_parcours int DEFAULT 0", - "update notes_formations set type_parcours=0 where type_parcours is NULL", - ], - ) - - # add publish_incomplete - check_field( - cnx, - "notes_evaluation", - "publish_incomplete", - [ - "alter table notes_evaluation add column publish_incomplete int DEFAULT 0", - "update notes_evaluation set publish_incomplete=0 where publish_incomplete is NULL", - ], - ) - - # add ens_can_create_eval to notes_formsemestre: - check_field( - cnx, - "notes_formsemestre", - "ens_can_edit_eval", - [ - "alter table notes_formsemestre add column ens_can_edit_eval int default 0", - "update notes_formsemestre set ens_can_edit_eval=0", - ], - ) - - # add evaluation_type - check_field( - cnx, - "notes_evaluation", - "evaluation_type", - [ - "alter table notes_evaluation add column evaluation_type int DEFAULT 0", - "update notes_evaluation set evaluation_type=0 where evaluation_type is NULL", - ], - ) - - # add partition rank on bulletins - check_field( - cnx, - "partition", - "bul_show_rank", - [ - "alter table partition add column bul_show_rank int DEFAULT 0", - "update partition set bul_show_rank=0 where bul_show_rank is NULL", - ], - ) - # add formsemestre to abs notifications - check_field( - cnx, - "absences_notifications", - "formsemestre_id", - [ - "alter table absences_notifications add column formsemestre_id text DEFAULT NULL", - ], - ) - # Add "debouche" to admission - check_field( - cnx, - "admissions", - "debouche", - [ - "alter table admissions add column debouche text DEFAULT NULL", - # et en profite pour corrige le From par defaut des mails: - "update sco_prefs set value='noreply@univ-paris13.fr' where name='email_from_addr' and value='noreply'", - ], - ) - # Increase semestre indices - for i in range(5, 9): - cursor.execute( - "SELECT * from notes_semestres where semestre_id=%(i)s", {"i": i} - ) - r = cursor.fetchall() - if not r: - log("adding semestre_id %s" % i) - cursor.execute( - "INSERT INTO notes_semestres (semestre_id) VALUES (%(i)s)", {"i": i} - ) - # ECTS associes aux UE: - check_field( - cnx, - "notes_ue", - "ects", - [ - "alter table notes_ue add column ects float DEFAULT NULL", - ], - ) - # Numeros des evaluations: - check_field( - cnx, - "notes_evaluation", - "numero", - [ - "alter table notes_evaluation add column numero int DEFAULT 0", - ], - ) - # add nom_usuel to identite - check_field( - cnx, - "identite", - "nom_usuel", - [ - "alter table identite add column nom_usuel text DEFAULT NULL", - ], - ) - # add type_admission - check_field( - cnx, - "admissions", - "type_admission", - [ - "alter table admissions add column type_admission text DEFAULT NULL", - ], - ) - check_field( - cnx, - "admissions", - "boursier_prec", - [ - "alter table admissions add column boursier_prec integer default NULL", - ], - ) - # add modalites formation - check_table( - cnx, - "notes_form_modalites", - [ - """CREATE TABLE notes_form_modalites ( - form_modalite_id text default notes_newid('Md') PRIMARY KEY, - modalite text, -- la clef dans notes_formsemestre - titre text, -- le nom complet de la modalite pour les documents scodoc - numero SERIAL -- integer, ordre de presentation - );""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('', 'Autres formations');""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FI', 'Formation Initiale');""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FC', 'Formation Continue');""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('FAP', 'Apprentissage');""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('DEC', 'Formation Décalées');""", - """INSERT INTO notes_form_modalites (modalite, titre) VALUES ('LIC', 'Licence');""", - ], - ) - # Add code_specialite - check_field( - cnx, - "notes_formations", - "code_specialite", - [ - "alter table notes_formations add column code_specialite text default NULL", - ], - ) - # Fix modules without codes - cursor.execute( - "UPDATE notes_modules SET code = 'M_' || coalesce(upper(substring(titre from 1 for 2)), '') || '_' || coalesce(semestre_id,'0') where code is NULL;" - ) - - # Add ue.is_external - check_field( - cnx, - "notes_ue", - "is_external", - [ - "alter table notes_ue add column is_external integer default 0", - ], - ) - check_field( - cnx, - "scolar_formsemestre_validation", - "is_external", - [ - "alter table scolar_formsemestre_validation add column is_external integer default 0", - ], - ) - # Add codes apogee - check_field( - cnx, - "notes_ue", - "code_apogee", - [ - "alter table notes_ue add column code_apogee text UNIQUE", - ], - ) - check_field( - cnx, - "notes_modules", - "code_apogee", - [ - "alter table notes_modules add column code_apogee text UNIQUE", - ], - ) - check_field( - cnx, - "notes_formsemestre", - "elt_sem_apo", - [ - "alter table notes_formsemestre add column elt_sem_apo text", - ], - ) - check_field( - cnx, - "notes_formsemestre", - "elt_annee_apo", - [ - "alter table notes_formsemestre add column elt_annee_apo text", - ], - ) - # Classement admission - check_field( - cnx, - "admissions", - "classement", - [ - "alter table admissions add column classement integer default NULL", - ], - ) - # Supprime contraintes erronées sur codes Apogee: - if list_constraint(cnx, constraint_name="notes_ue_code_apogee_key"): - log("dropping buggy constraint on notes_ue_code_apogee") - cursor.execute( - "alter table notes_ue drop CONSTRAINT notes_ue_code_apogee_key;" - ) - if list_constraint(cnx, constraint_name="notes_modules_code_apogee_key"): - log("dropping buggy constraint on notes_modules_code_apogee") - cursor.execute( - "alter table notes_modules drop CONSTRAINT notes_modules_code_apogee_key;" - ) - - # SemSet: - check_table( - cnx, - "notes_semset", - [ - """CREATE TABLE notes_semset ( - semset_id text default notes_newid('NSS') PRIMARY KEY, - title text, - annee_scolaire int default NULL, -- 2016 - sem_id int default NULL -- 0, 1, 2 - ) WITH OIDS;""", - ], - ) - check_field( - cnx, - "notes_semset", - "annee_scolaire", - [ - "alter table notes_semset add column annee_scolaire integer default NULL", - ], - ) - check_table( - cnx, - "notes_semset_formsemestre", - [ - """CREATE TABLE notes_semset_formsemestre ( - formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, - semset_id text REFERENCES notes_semset (semset_id) ON DELETE CASCADE, - PRIMARY KEY (formsemestre_id, semset_id) - ) WITH OIDS;""", - ], - ) - - # ModuleTags - check_table( - cnx, - "notes_tags", - [ - """CREATE TABLE notes_tags ( - tag_id text default notes_newid('TAG') PRIMARY KEY, - title text UNIQUE NOT NULL - ) WITH OIDS;""", - ], - ) - check_table( - cnx, - "notes_modules_tags", - [ - """CREATE TABLE notes_modules_tags ( - tag_id text REFERENCES notes_tags(tag_id) ON DELETE CASCADE, - module_id text REFERENCES notes_modules(module_id) ON DELETE CASCADE, - PRIMARY KEY (tag_id, module_id) - ) WITH OIDS;""", - ], - ) - - # add show_in_lists on partition - check_field( - cnx, - "partition", - "show_in_lists", - [ - "alter table partition add column show_in_lists integer DEFAULT 1", - "update partition set show_in_lists=1 where show_in_lists is NULL", - ], - ) - # table codes etapes apogee semestre - check_table( - cnx, - "notes_formsemestre_etapes", - [ - """CREATE TABLE notes_formsemestre_etapes ( - formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, - etape_apo text NOT NULL -) WITH OIDS;""", - """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo FROM notes_formsemestre WHERE etape_apo is not NULL;""", - """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo2 FROM notes_formsemestre WHERE etape_apo2 is not NULL;""", - """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo3 FROM notes_formsemestre WHERE etape_apo3 is not NULL;""", - """INSERT into notes_formsemestre_etapes (formsemestre_id, etape_apo) SELECT formsemestre_id, etape_apo4 FROM notes_formsemestre WHERE etape_apo4 is not NULL;""", - """ALTER table notes_formsemestre DROP column etape_apo;""", - """ALTER table notes_formsemestre DROP column etape_apo2;""", - """ALTER table notes_formsemestre DROP column etape_apo3;""", - """ALTER table notes_formsemestre DROP column etape_apo4;""", - ], - ) - # Admission APB: groupe et classement dans groupe - check_field( - cnx, - "admissions", - "apb_groupe", - [ - "alter table admissions add column apb_groupe text default NULL", - ], - ) - check_field( - cnx, - "admissions", - "apb_classement_gr", - [ - "alter table admissions add column apb_classement_gr integer default NULL", - ], - ) - # Adresse mail perso - check_field( - cnx, - "adresse", - "emailperso", - [ - "alter table adresse add column emailperso text", - ], - ) - # Ajout de modalites supplementaires - cursor.execute("SELECT modalite from notes_form_modalites where modalite= 'CP'") - if not len(cursor.fetchall()): - log('adding modalite "CP"') - cursor.execute( - "INSERT INTO notes_form_modalites (modalite, titre) VALUES ('CP', 'Contrats de Professionnalisation');" - ) - # Un index oublié sur notes_notes: - if "notes_notes_evaluation_id_idx" not in list_table_index(cnx, "notes_notes"): - log("creating index on notes_notes") - cursor.execute( - "CREATE INDEX notes_notes_evaluation_id_idx ON notes_notes (evaluation_id)" - ) - - # boursier (ajout API nov 2017) - check_field( - cnx, "identite", "boursier", ["alter table identite add column boursier text"] - ) - # Suivi des anciens etudiants (debouche) - # cree table suivi et recopie ancien champs debouche de la table admission - check_table( - cnx, - "itemsuivi", - [ - """CREATE TABLE itemsuivi ( - itemsuivi_id text DEFAULT notes_newid('SUI'::text) PRIMARY KEY, - etudid text NOT NULL, - item_date date DEFAULT now(), - situation text - ) WITH OIDS;""", - """INSERT INTO itemsuivi (etudid, situation) - SELECT etudid, debouche FROM admissions WHERE debouche is not null; - """, - ], - ) - check_table( - cnx, - "itemsuivi_tags", - [ - """CREATE TABLE itemsuivi_tags ( - tag_id text default notes_newid('TG') PRIMARY KEY, - title text UNIQUE NOT NULL - ) WITH OIDS;""", - ], - ) - check_table( - cnx, - "itemsuivi_tags_assoc", - [ - """CREATE TABLE itemsuivi_tags_assoc ( - tag_id text REFERENCES itemsuivi_tags(tag_id) ON DELETE CASCADE, - itemsuivi_id text REFERENCES itemsuivi(itemsuivi_id) ON DELETE CASCADE, - PRIMARY KEY (tag_id, itemsuivi_id) - ) WITH OIDS;""", - ], - ) - - # Types de modules (pour malus) - check_field( - cnx, - "notes_modules", - "module_type", - [ - "alter table notes_modules add column module_type int", - ], - ) - - # Responsables de semestres - check_table( - cnx, - "notes_formsemestre_responsables", - [ - """CREATE TABLE notes_formsemestre_responsables ( - formsemestre_id text REFERENCES notes_formsemestre(formsemestre_id) ON DELETE CASCADE, - responsable_id text NOT NULL, - UNIQUE(formsemestre_id, responsable_id) - ) WITH OIDS;""", - """INSERT into notes_formsemestre_responsables (formsemestre_id, responsable_id) SELECT formsemestre_id, responsable_id FROM notes_formsemestre WHERE responsable_id is not NULL;""", - """ALTER table notes_formsemestre DROP column responsable_id;""", - ], - ) - # Fonction pour anonymisation: - if not function_exists(cnx, "random_text_md5"): - log("creating function random_text_md5") - # inspirée par https://www.simononsoftware.com/random-string-in-postgresql/ - cursor.execute( - """CREATE FUNCTION random_text_md5( integer ) returns text - LANGUAGE SQL - AS $$ - select upper( substring( (SELECT string_agg(md5(random()::TEXT), '') - FROM generate_series( - 1, - CEIL($1 / 32.)::integer) - ), 1, $1) ); - $$;""" - ) - # departement naissance (ajout fev 2020) - check_field( - cnx, - "identite", - "dept_naissance", - ["alter table identite add column dept_naissance text"], - ) - # Modalite semestres exterieurs - cursor.execute("SELECT modalite from notes_form_modalites where modalite= 'EXT'") - if not len(cursor.fetchall()): - log('adding modalite "EXT"') - cursor.execute( - "INSERT INTO notes_form_modalites (modalite, titre) VALUES ('EXT', 'Extérieur');" - ) - # Coefficients d'UE - check_field( - cnx, - "notes_ue", - "coefficient", - [ - "alter table notes_ue add column coefficient float", - # Initialise les coefficients égaux aux ECTS: - "update notes_ue set coefficient=ects", - # Force pref locale sur semestres existants: - """INSERT INTO sco_prefs (name, value, formsemestre_id) - SELECT DISTINCT 'use_ue_coefs', '0', formsemestre_id FROM notes_formsemestre - ON CONFLICT DO NOTHING - """, - ], - ) - # Etape d'inscription - check_field( - cnx, - "notes_formsemestre_inscription", - "etape", - ["alter table notes_formsemestre_inscription add column etape text"], - ) - # Migre identite.sexe vers identite.civilite: normalise valeurs: M, F, X - check_field( - cnx, - "identite", - "civilite", - [ - "UPDATE identite set sexe='M' where upper(sexe) = 'M' or upper(sexe) = 'M.' or upper(sexe) = 'MR' or upper(sexe) = 'H'", - "UPDATE identite set sexe='F' where upper(sexe) = 'F' or upper(sexe) = 'MME' or upper(sexe) = 'MLLE' or upper(sexe) = 'MELLE' or upper(sexe) = 'MLLE.' or upper(sexe) = 'F'", - "UPDATE identite set sexe='X' where sexe is NULL", - "ALTER TABLE identite RENAME COLUMN sexe TO civilite", - "ALTER TABLE identite ALTER COLUMN civilite SET NOT NULL", - "ALTER TABLE identite ADD CONSTRAINT civchk CHECK (civilite IN ('M', 'F', 'X'))", - ], - ) - - # Add here actions to performs after upgrades: - cnx.commit() - cnx.close() - - -# Base utilisateurs: -log("\nNOT Checking users database") -# cnx = psycopg2.connect(get_users_cnx_str()) -# cursor = cnx.cursor() -# check_field( -# cnx, -# "sco_users", -# "passwd_temp", -# [ -# "alter table sco_users add column passwd_temp int default 0", -# "update sco_users set passwd_temp=0", -# ], -# ) -# check_field( -# cnx, -# "sco_users", -# "status", -# ["alter table sco_users add column status text default NULL"], -# ) -# check_field( -# cnx, -# "sco_users", -# "date_expiration", -# [ -# "alter table sco_users add column date_expiration date", -# "update sco_users set status=NULL where status=''", # fix a bug in previous update... -# ], -# ) -# check_field( -# cnx, -# "sco_users", -# "login_edt", -# [ -# "alter table sco_users add column login_edt text default NULL", -# ], -# ) -# cnx.commit() -# cnx.close() - -# The end. -sys.exit(0)