This commit is contained in:
Emmanuel Viennet 2021-11-28 10:13:18 +01:00
commit 47c1a75bb0
14 changed files with 149 additions and 51 deletions

View File

@ -152,10 +152,13 @@ class ItemSuiviTag(db.Model):
# Association tag <-> module # Association tag <-> module
itemsuivi_tags_assoc = db.Table( itemsuivi_tags_assoc = db.Table(
"itemsuivi_tags_assoc", "itemsuivi_tags_assoc",
db.Column("tag_id", db.Integer, db.ForeignKey("itemsuivi_tags.id")), db.Column(
db.Column("itemsuivi_id", db.Integer, db.ForeignKey("itemsuivi.id")), "tag_id", db.Integer, db.ForeignKey("itemsuivi_tags.id", ondelete="CASCADE")
),
db.Column(
"itemsuivi_id", db.Integer, db.ForeignKey("itemsuivi.id", ondelete="CASCADE")
),
) )
# ON DELETE CASCADE ?
class EtudAnnotation(db.Model): class EtudAnnotation(db.Model):

View File

@ -114,10 +114,11 @@ class UniteEns(db.Model):
ou à défaut le met à 1. ou à défaut le met à 1.
""" """
if self.semestre_idx is None: if self.semestre_idx is None:
if self.modules: module = self.modules.first()
self.semestre_idx = self.modules[0].semestre_id if module is None:
else:
self.semestre_idx = 1 self.semestre_idx = 1
else:
self.semestre_idx = module.semestre_id
db.session.add(self) db.session.add(self)
db.session.commit() db.session.commit()

View File

@ -4,6 +4,7 @@
""" """
from app import db, log from app import db, log
from app.scodoc import bonus_sport from app.scodoc import bonus_sport
from app.scodoc.sco_exceptions import ScoValueError
class ScoPreference(db.Model): class ScoPreference(db.Model):
@ -94,7 +95,7 @@ class ScoDocSiteConfig(db.Model):
"""returns bonus func with specified name. """returns bonus func with specified name.
If name not specified, return the configured function. If name not specified, return the configured function.
None if no bonus function configured. None if no bonus function configured.
Raises NameError if func_name not found in module bonus_sport. Raises ScoValueError if func_name not found in module bonus_sport.
""" """
if func_name is None: if func_name is None:
c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first() c = ScoDocSiteConfig.query.filter_by(name=cls.BONUS_SPORT).first()
@ -103,7 +104,13 @@ class ScoDocSiteConfig(db.Model):
func_name = c.value func_name = c.value
if func_name == "": # pas de bonus défini if func_name == "": # pas de bonus défini
return None return None
return getattr(bonus_sport, func_name) try:
return getattr(bonus_sport, func_name)
except AttributeError:
raise ScoValueError(
f"""Fonction de calcul maison inexistante: {func_name}.
(contacter votre administrateur local)."""
)
@classmethod @classmethod
def get_bonus_sport_func_names(cls): def get_bonus_sport_func_names(cls):

View File

@ -498,7 +498,7 @@ class GenTable(object):
headline = [] headline = []
return "\n".join( return "\n".join(
[ [
self.text_fields_separator.join([x for x in line]) self.text_fields_separator.join([str(x) for x in line])
for line in headline + self.get_data_list() for line in headline + self.get_data_list()
] ]
) )

View File

@ -288,10 +288,11 @@ def formsemestre_inscr_passage(
footer = html_sco_header.sco_footer() footer = html_sco_header.sco_footer()
H = [header] H = [header]
if isinstance(etuds, str): if isinstance(etuds, str):
etuds = etuds.split(",") # vient du form de confirmation # list de strings, vient du form de confirmation
etuds = [int(x) for x in etuds.split(",") if x]
elif isinstance(etuds, int): elif isinstance(etuds, int):
etuds = [etuds] etuds = [etuds]
etuds = [int(x) for x in etuds]
auth_etuds_by_sem, inscrits, candidats = list_authorized_etuds_by_sem(sem) auth_etuds_by_sem, inscrits, candidats = list_authorized_etuds_by_sem(sem)
etuds_set = set(etuds) etuds_set = set(etuds)
candidats_set = set(candidats) candidats_set = set(candidats)

View File

@ -149,6 +149,10 @@ def ficheEtud(etudid=None):
authuser = current_user authuser = current_user
cnx = ndb.GetDBConnexion() cnx = ndb.GetDBConnexion()
if etudid: if etudid:
try: # pour les bookmarks avec d'anciens ids...
etudid = int(etudid)
except ValueError:
raise ScoValueError("id invalide !")
# la sidebar est differente s'il y a ou pas un etudid # la sidebar est differente s'il y a ou pas un etudid
# voir html_sidebar.sidebar() # voir html_sidebar.sidebar()
g.etudid = etudid g.etudid = etudid

View File

@ -493,6 +493,8 @@ def photos_generate_excel_sample(group_ids=[]):
def photos_import_files_form(group_ids=[]): def photos_import_files_form(group_ids=[]):
"""Formulaire pour importation photos""" """Formulaire pour importation photos"""
if not group_ids:
raise ScoValueError("paramètre manquant !")
groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids) groups_infos = sco_groups_view.DisplayedGroupsInfos(group_ids)
back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args back_url = "groups_view?%s&curtab=tab-photos" % groups_infos.groups_query_args

View File

@ -177,7 +177,7 @@ def formsemestre_list_saisies_notes(formsemestre_id, format="html"):
"""Table listant toutes les opérations de saisies de notes, dans toutes """Table listant toutes les opérations de saisies de notes, dans toutes
les évaluations du semestre. les évaluations du semestre.
""" """
sem = sco_formsemestre.get_formsemestre(formsemestre_id) sem = sco_formsemestre.get_formsemestre(formsemestre_id, raise_soft_exc=True)
r = ndb.SimpleDictFetch( r = ndb.SimpleDictFetch(
"""SELECT i.nom, code_nip, n.*, mod.titre, e.description, e.jour """SELECT i.nom, code_nip, n.*, mod.titre, e.description, e.jour
FROM notes_notes n, notes_evaluation e, notes_moduleimpl mi, FROM notes_notes n, notes_evaluation e, notes_moduleimpl mi,

View File

@ -45,6 +45,7 @@ from werkzeug.utils import redirect
from config import Config from config import Config
from app import api
from app import db from app import db
from app import models from app import models
from app.auth.models import User from app.auth.models import User
@ -661,6 +662,11 @@ def formsemestre_list(
kw can specify some conditions: examples: kw can specify some conditions: examples:
formsemestre_list( format='json', formation_id='F777') formsemestre_list( format='json', formation_id='F777')
""" """
try:
formsemestre_id = int(formsemestre_id) if formsemestre_id is not None else None
formation_id = int(formation_id) if formation_id is not None else None
except ValueError:
return api.errors.error_response(404, "invalid id")
# XAPI: new json api # XAPI: new json api
args = {} args = {}
L = locals() L = locals()
@ -1563,7 +1569,7 @@ def evaluation_delete(evaluation_id):
"""Form delete evaluation""" """Form delete evaluation"""
El = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id}) El = sco_evaluation_db.do_evaluation_list(args={"evaluation_id": evaluation_id})
if not El: if not El:
raise ValueError("Evalution inexistante ! (%s)" % evaluation_id) raise ScoValueError("Evalution inexistante ! (%s)" % evaluation_id)
E = El[0] E = El[0]
M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0] M = sco_moduleimpl.moduleimpl_list(moduleimpl_id=E["moduleimpl_id"])[0]
Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0] Mod = sco_edit_module.module_list(args={"module_id": M["module_id"]})[0]

View File

@ -0,0 +1,34 @@
"""cascades sur itemsuivi
Revision ID: 39818df276aa
Revises: 1efe07413835
Create Date: 2021-11-27 19:03:09.329065
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '39818df276aa'
down_revision = '1efe07413835'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('itemsuivi_tags_assoc_tag_id_fkey', 'itemsuivi_tags_assoc', type_='foreignkey')
op.drop_constraint('itemsuivi_tags_assoc_itemsuivi_id_fkey', 'itemsuivi_tags_assoc', type_='foreignkey')
op.create_foreign_key(None, 'itemsuivi_tags_assoc', 'itemsuivi', ['itemsuivi_id'], ['id'], ondelete='CASCADE')
op.create_foreign_key(None, 'itemsuivi_tags_assoc', 'itemsuivi_tags', ['tag_id'], ['id'], ondelete='CASCADE')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'itemsuivi_tags_assoc', type_='foreignkey')
op.drop_constraint(None, 'itemsuivi_tags_assoc', type_='foreignkey')
op.create_foreign_key('itemsuivi_tags_assoc_itemsuivi_id_fkey', 'itemsuivi_tags_assoc', 'itemsuivi', ['itemsuivi_id'], ['id'])
op.create_foreign_key('itemsuivi_tags_assoc_tag_id_fkey', 'itemsuivi_tags_assoc', 'itemsuivi_tags', ['tag_id'], ['id'])
# ### end Alembic commands ###

View File

@ -11,7 +11,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic. # revision identifiers, used by Alembic.
revision = "ada0d1f3d84f" revision = "ada0d1f3d84f"
down_revision = "1efe07413835" down_revision = "39818df276aa"
branch_labels = None branch_labels = None
depends_on = None depends_on = None

View File

@ -81,7 +81,8 @@ def make_shell_context():
@app.cli.command() @app.cli.command()
def sco_db_init(): # sco-db-init @click.option("--erase/--no-erase", default=False)
def sco_db_init(erase=False): # sco-db-init
"""Initialize the database. """Initialize the database.
Starts from an existing database and create all Starts from an existing database and create all
the necessary SQL tables and functions. the necessary SQL tables and functions.
@ -91,7 +92,7 @@ def sco_db_init(): # sco-db-init
"""La variable SCODOC_ADMIN_MAIL n'est pas positionnée: vérifier votre .env""" """La variable SCODOC_ADMIN_MAIL n'est pas positionnée: vérifier votre .env"""
) )
return 100 return 100
initialize_scodoc_database() initialize_scodoc_database(erase=erase)
@app.cli.command() @app.cli.command()

View File

@ -120,7 +120,7 @@ then
echo echo
echo "Création des tables et du compte admin" echo "Création des tables et du compte admin"
echo echo
msg="Saisir le mot de passe de l'administrateur \(admin, via le web\):" msg="Saisir le mot de passe administrateur \(admin, via le web\):"
su -c "(cd /opt/scodoc; source venv/bin/activate; flask db upgrade; flask sco-db-init; echo; echo $msg; flask user-password admin)" "$SCODOC_USER" || die "Erreur: sco-db-init" su -c "(cd /opt/scodoc; source venv/bin/activate; flask db upgrade; flask sco-db-init; echo; echo $msg; flask user-password admin)" "$SCODOC_USER" || die "Erreur: sco-db-init"
echo echo
echo "Base initialisée et admin créé." echo "Base initialisée et admin créé."

View File

@ -30,41 +30,72 @@ usage() {
echo "Ce script doit être lancé en tant que root sur le nouveau" echo "Ce script doit être lancé en tant que root sur le nouveau"
echo "serveur ScoDoc 9 / Debian 11." echo "serveur ScoDoc 9 / Debian 11."
echo echo
echo "Usage: $0 archive" echo "Usage: $0 [-h] [-m] [-z] archive"
echo " ou $0 -m"
echo echo
echo " archive doit être un répertoire exporté via save_scodoc7_data.sh" echo " archive doit être un répertoire exporté via save_scodoc7_data.sh"
echo " sur le serveur ScoDoc 7" echo " sur le serveur ScoDoc 7"
echo " Avec l'option -m, effectue une migration \"en place\"" echo " Options:"
echo " avec import des données ScoDoc 7 qui étaient sur cette même" echo " -m"
echo " machine." echo " effectue une migration \"en place\""
echo " Dans ce cas, le répertoire /opt/scodoc DOIT avoir été renommé" echo " avec import des données ScoDoc 7 qui étaient sur cette même"
echo " /opt/scodoc7" echo " machine."
echo " AVANT l'installation de ScoDoc 9." echo " Dans ce cas, le répertoire /opt/scodoc DOIT avoir été renommé"
echo echo " /opt/scodoc7"
echo " AVANT l'installation de ScoDoc 9."
echo
echo " -z"
echo " efface la base existante, utilise le scodoc-data existant sans"
echo " l'effacer et tolère les fichiers manquants dans la source."
echo " Utilisée pour reprendre une migration interrompue."
exit 1 exit 1
} }
if [ ! $# -eq 1 ]
then INPLACE=0
RESTART=0
while getopts "hmz" opt; do
case "$opt" in
h)
usage usage
fi ;;
if [ "$1" == "-h" ] || [ "$1" == "--help" ] m)
then
usage
fi
if [ "$1" == "-m" ]
then
echo "Migration en place" echo "Migration en place"
INPLACE=1 INPLACE=1
SCODOC7_HOME=/opt/scodoc7 SCODOC7_HOME=/opt/scodoc7
# vérifie que ScoDoc7 est bien arrêté: # vérifie que ScoDoc7 est bien arrêté:
systemctl is-active scodoc >& /dev/null && systemctl stop scodoc systemctl is-active scodoc >& /dev/null && systemctl stop scodoc
else ;;
z)
echo "Mode reprise sur erreur"
RESTART=1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ "$INPLACE" = "0" ]
then
echo "Migration depuis archive $1" echo "Migration depuis archive $1"
INPLACE=0
SCODOC7_HOME="$1" # racine de l'archive importée SCODOC7_HOME="$1" # racine de l'archive importée
fi fi
# --- 0. En mode reprise, efface la base de données. En effet, les base d'origine ne sont pas
# effacées par le script de migration, et en cas d'erreur en cours d'import, il est plus
# sûr de repartir de zéro.
if [ "$RESTART" = "1" ]
then
echo "Efface la base existante"
su -c "(cd /opt/scodoc && source venv/bin/activate && flask sco-db-init --erase)" "$SCODOC_USER" || die "Erreur: sco-db-init"
fi
# --- 1. Vérifie qu'aucun des départements à importer n'existe déjà # --- 1. Vérifie qu'aucun des départements à importer n'existe déjà
check_existing_depts() { check_existing_depts() {
sco7_depts="" sco7_depts=""
@ -116,27 +147,33 @@ migrate_database_ownership() {
migrate_local_files() { migrate_local_files() {
echo "Déplacement des fichiers de configuration et des archives" echo "Déplacement des fichiers de configuration et des archives"
SCODOC_VAR_DIR_BACKUP="$SCODOC_VAR_DIR".bak SCODOC_VAR_DIR_BACKUP="$SCODOC_VAR_DIR".bak
if [ -e "$SCODOC_VAR_DIR_BACKUP" ] if [ "$RESTART" = "0" ] # ne le fait pas en mode "reprise"
then then
die "supprimer ou déplacer $SCODOC_VAR_DIR_BACKUP avant de continuer" if [ -e "$SCODOC_VAR_DIR_BACKUP" ]
then
die "supprimer ou déplacer $SCODOC_VAR_DIR_BACKUP avant de continuer"
fi
if [ -e "$SCODOC_VAR_DIR" ]
then
echo " renomme $SCODOC_VAR_DIR en $SCODOC_VAR_DIR_BACKUP"
mv "$SCODOC_VAR_DIR" "$SCODOC_VAR_DIR_BACKUP"
fi
mkdir "$SCODOC_VAR_DIR" || die "erreur creation repertoire"
fi fi
if [ -e "$SCODOC_VAR_DIR" ] if [ $(ls "${SCODOC7_HOME}/var/scodoc" | wc -l) -ne 0 ]
then then
echo " renomme $SCODOC_VAR_DIR en $SCODOC_VAR_DIR_BACKUP" echo " déplace ${SCODOC7_HOME}/var/scodoc/ dans $SCODOC_VAR_DIR..."
mv "$SCODOC_VAR_DIR" "$SCODOC_VAR_DIR_BACKUP" mv "${SCODOC7_HOME}"/var/scodoc/* "$SCODOC_VAR_DIR" || die "migrate_local_files failed"
fi fi
mkdir "$SCODOC_VAR_DIR" || die "erreur creation repertoire"
echo " déplace ${SCODOC7_HOME}/var/scodoc/ dans $SCODOC_VAR_DIR..."
mv "${SCODOC7_HOME}"/var/scodoc/* "$SCODOC_VAR_DIR" || die "migrate_local_files failed"
# Récupère le .env: normalement ./opt/scodoc/.env est un lien vers # Récupère le .env: normalement ./opt/scodoc/.env est un lien vers
# /opt/scodoc-data/.env # /opt/scodoc-data/.env
# sauf si installation non standard (developeurs) avec .env réelement dans /opt/scodoc # sauf si installation non standard (developeurs) avec .env réellement dans /opt/scodoc
if [ -L "$SCODOC_DIR"/.env ] if [ -L "$SCODOC_DIR"/.env ] && [ ! -e "$SCODOC_VAR_DIR"/.env ]
then then
cp -p "$SCODOC_VAR_DIR_BACKUP"/.env "$SCODOC_VAR_DIR" || die "fichier .env manquant dans l'ancien $SCODOC_VAR_DIR !" cp -p "$SCODOC_VAR_DIR_BACKUP"/.env "$SCODOC_VAR_DIR" || die "fichier .env manquant dans l'ancien $SCODOC_VAR_DIR !"
fi fi
# et les certificats # et les certificats
if [ -d "$SCODOC_VAR_DIR_BACKUP"/certs ] if [ -d "$SCODOC_VAR_DIR_BACKUP"/certs ] && [ ! -d "$SCODOC_VAR_DIR"/certs ]
then then
cp -rp "$SCODOC_VAR_DIR_BACKUP"/certs "$SCODOC_VAR_DIR" || die "erreur copie certs" cp -rp "$SCODOC_VAR_DIR_BACKUP"/certs "$SCODOC_VAR_DIR" || die "erreur copie certs"
fi fi
@ -144,8 +181,10 @@ migrate_local_files() {
old_logs_dest="$SCODOC_VAR_DIR/log/scodoc7" old_logs_dest="$SCODOC_VAR_DIR/log/scodoc7"
echo "Copie des anciens logs ScoDoc 7 dans $old_logs_dest" echo "Copie des anciens logs ScoDoc 7 dans $old_logs_dest"
mkdir -p "$old_logs_dest" || die "erreur creation $old_logs_dest" mkdir -p "$old_logs_dest" || die "erreur creation $old_logs_dest"
mv "${SCODOC7_HOME}"/log/* "$old_logs_dest" || die "erreur mv" if [ $(ls "${SCODOC7_HOME}/log" | wc -l) -ne 0 ]
then
mv "${SCODOC7_HOME}"/log/* "$old_logs_dest" || die "erreur mv"
fi
# Le fichier de customization local: # Le fichier de customization local:
# peut être dans .../var/config/scodoc_local.py # peut être dans .../var/config/scodoc_local.py
# ou bien, sur les très anciennes installs, dans Products/ScoDoc/config/scodoc_config.py # ou bien, sur les très anciennes installs, dans Products/ScoDoc/config/scodoc_config.py