diff --git a/app/models/but_refcomp.py b/app/models/but_refcomp.py index 425ff192..84dd2e1c 100644 --- a/app/models/but_refcomp.py +++ b/app/models/but_refcomp.py @@ -53,7 +53,9 @@ class XMLModel: class ApcReferentielCompetences(db.Model, XMLModel): "Référentiel de compétence d'une spécialité" id = db.Column(db.Integer, primary_key=True) - dept_id = db.Column(db.Integer, db.ForeignKey("departement.id"), index=True) + dept_id = db.Column( + db.Integer, db.ForeignKey("departement.id", ondelete="CASCADE"), index=True + ) annexe = db.Column(db.Text()) # '1', '22', ... specialite = db.Column(db.Text()) # 'CJ', 'RT', 'INFO', ... specialite_long = db.Column( @@ -207,7 +209,9 @@ class ApcCompetence(db.Model, XMLModel): "Compétence" id = db.Column(db.Integer, primary_key=True) referentiel_id = db.Column( - db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False + db.Integer, + db.ForeignKey("apc_referentiel_competences.id", ondelete="CASCADE"), + nullable=False, ) # les compétences dans Orébut sont identifiées par leur id unique # (mais id_orebut n'est pas unique car le même ref. pourra être chargé dans plusieurs depts) @@ -276,7 +280,9 @@ class ApcSituationPro(db.Model, XMLModel): "Situation professionnelle" id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( - db.Integer, db.ForeignKey("apc_competence.id"), nullable=False + db.Integer, + db.ForeignKey("apc_competence.id", ondelete="CASCADE"), + nullable=False, ) libelle = db.Column(db.Text(), nullable=False) # aucun attribut (le text devient le libellé) @@ -288,7 +294,9 @@ class ApcComposanteEssentielle(db.Model, XMLModel): "Composante essentielle" id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( - db.Integer, db.ForeignKey("apc_competence.id"), nullable=False + db.Integer, + db.ForeignKey("apc_competence.id", ondelete="CASCADE"), + nullable=False, ) libelle = db.Column(db.Text(), nullable=False) @@ -306,7 +314,9 @@ class ApcNiveau(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) competence_id = db.Column( - db.Integer, db.ForeignKey("apc_competence.id"), nullable=False + db.Integer, + db.ForeignKey("apc_competence.id", ondelete="CASCADE"), + nullable=False, ) libelle = db.Column(db.Text(), nullable=False) annee = db.Column(db.Text(), nullable=False) # "BUT1", "BUT2", "BUT3" @@ -387,7 +397,7 @@ app_critiques_modules = db.Table( ), db.Column( "app_crit_id", - db.ForeignKey("apc_app_critique.id"), + db.ForeignKey("apc_app_critique.id", ondelete="CASCADE"), primary_key=True, ), ) @@ -396,7 +406,9 @@ app_critiques_modules = db.Table( class ApcAppCritique(db.Model, XMLModel): "Apprentissage Critique BUT" id = db.Column(db.Integer, primary_key=True) - niveau_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id"), nullable=False) + niveau_id = db.Column( + db.Integer, db.ForeignKey("apc_niveau.id", ondelete="CASCADE"), nullable=False + ) code = db.Column(db.Text(), nullable=False, index=True) libelle = db.Column(db.Text()) @@ -445,7 +457,10 @@ class ApcAppCritique(db.Model, XMLModel): parcours_modules = db.Table( "parcours_modules", db.Column( - "parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True + "parcours_id", + db.Integer, + db.ForeignKey("apc_parcours.id", ondelete="CASCADE"), + primary_key=True, ), db.Column( "module_id", @@ -459,7 +474,10 @@ parcours_modules = db.Table( parcours_formsemestre = db.Table( "parcours_formsemestre", db.Column( - "parcours_id", db.Integer, db.ForeignKey("apc_parcours.id"), primary_key=True + "parcours_id", + db.Integer, + db.ForeignKey("apc_parcours.id", ondelete="CASCADE"), + primary_key=True, ), db.Column( "formsemestre_id", @@ -475,7 +493,9 @@ class ApcParcours(db.Model, XMLModel): "Un parcours BUT" id = db.Column(db.Integer, primary_key=True) referentiel_id = db.Column( - db.Integer, db.ForeignKey("apc_referentiel_competences.id"), nullable=False + db.Integer, + db.ForeignKey("apc_referentiel_competences.id", ondelete="CASCADE"), + nullable=False, ) numero = db.Column(db.Integer) # ordre de présentation code = db.Column(db.Text(), nullable=False) @@ -516,7 +536,7 @@ class ApcParcours(db.Model, XMLModel): class ApcAnneeParcours(db.Model, XMLModel): id = db.Column(db.Integer, primary_key=True) parcours_id = db.Column( - db.Integer, db.ForeignKey("apc_parcours.id"), nullable=False + db.Integer, db.ForeignKey("apc_parcours.id", ondelete="CASCADE"), nullable=False ) ordre = db.Column(db.Integer) "numéro de l'année: 1, 2, 3" diff --git a/app/models/but_validations.py b/app/models/but_validations.py index ccef89cd..9625d1e4 100644 --- a/app/models/but_validations.py +++ b/app/models/but_validations.py @@ -46,7 +46,9 @@ class ApcValidationRCUE(db.Model): ue1_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False) ue2_id = db.Column(db.Integer, db.ForeignKey("notes_ue.id"), nullable=False) # optionnel, le parcours dans lequel se trouve la compétence: - parcours_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), nullable=True) + parcours_id = db.Column( + db.Integer, db.ForeignKey("apc_parcours.id", ondelete="set null"), nullable=True + ) date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) code = db.Column(db.String(CODE_STR_LEN), nullable=False, index=True) diff --git a/app/models/formations.py b/app/models/formations.py index d4aac9ad..8264fa81 100644 --- a/app/models/formations.py +++ b/app/models/formations.py @@ -48,7 +48,7 @@ class Formation(db.Model): # Optionnel, pour les formations type BUT referentiel_competence_id = db.Column( - db.Integer, db.ForeignKey("apc_referentiel_competences.id") + db.Integer, db.ForeignKey("apc_referentiel_competences.id", ondelete="SET NULL") ) ues = db.relationship("UniteEns", backref="formation", lazy="dynamic") formsemestres = db.relationship("FormSemestre", lazy="dynamic", backref="formation") diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index bafad116..f58a38b7 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -1037,7 +1037,9 @@ class FormSemestreInscription(db.Model): # Etape Apogée d'inscription (ajout 2020) etape = db.Column(db.String(APO_CODE_STR_LEN)) # Parcours (pour les BUT) - parcour_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), index=True) + parcour_id = db.Column( + db.Integer, db.ForeignKey("apc_parcours.id", ondelete="SET NULL"), index=True + ) parcour = db.relationship(ApcParcours) def __repr__(self): diff --git a/app/models/ues.py b/app/models/ues.py index 596e0bef..faa5df20 100644 --- a/app/models/ues.py +++ b/app/models/ues.py @@ -51,11 +51,15 @@ class UniteEns(db.Model): color = db.Column(db.Text()) # BUT - niveau_competence_id = db.Column(db.Integer, db.ForeignKey("apc_niveau.id")) + niveau_competence_id = db.Column( + db.Integer, db.ForeignKey("apc_niveau.id", ondelete="SET NULL") + ) niveau_competence = db.relationship("ApcNiveau", back_populates="ues") # Une ue appartient soit à tous les parcours (tronc commun), soit à un seul: - parcour_id = db.Column(db.Integer, db.ForeignKey("apc_parcours.id"), index=True) + parcour_id = db.Column( + db.Integer, db.ForeignKey("apc_parcours.id", ondelete="SET NULL"), index=True + ) parcour = db.relationship("ApcParcours", back_populates="ues") # relations diff --git a/app/models/validations.py b/app/models/validations.py index f0ec9749..20a4bb58 100644 --- a/app/models/validations.py +++ b/app/models/validations.py @@ -160,11 +160,11 @@ class ScolarEvent(db.Model): event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) formsemestre_id = db.Column( db.Integer, - db.ForeignKey("notes_formsemestre.id"), + db.ForeignKey("notes_formsemestre.id", ondelete="SET NULL"), ) ue_id = db.Column( db.Integer, - db.ForeignKey("notes_ue.id"), + db.ForeignKey("notes_ue.id", ondelete="SET NULL"), ) # 'CREATION', 'INSCRIPTION', 'DEMISSION', # 'AUT_RED', 'EXCLUS', 'VALID_UE', 'VALID_SEM' diff --git a/app/scodoc/sco_dept.py b/app/scodoc/sco_dept.py index 6fdf92e4..110a764c 100644 --- a/app/scodoc/sco_dept.py +++ b/app/scodoc/sco_dept.py @@ -317,7 +317,7 @@ def _style_sems(sems): ] = f""" data-oid="{sem['formsemestre_id']}" data-value="{sem['elt_sem_apo']}" """ -def delete_dept(dept_id: int): +def delete_dept(dept_id: int) -> str: """Suppression irréversible d'un département et de tous les objets rattachés""" assert isinstance(dept_id, int) @@ -361,6 +361,7 @@ def delete_dept(dept_id: int): ) reqs = [ + "delete from apc_referentiel_competences where dept_id = %(dept_id)s", "delete from identite where dept_id = %(dept_id)s", "delete from sco_prefs where dept_id = %(dept_id)s", "delete from notes_semset_formsemestre where formsemestre_id in (select id from formsemestres_temp)", @@ -392,8 +393,10 @@ def delete_dept(dept_id: int): ] for r in reqs: cursor.execute(r, {"dept_id": dept_id}) - except: + except Exception as e: cnx.rollback() + return str(e) finally: cnx.commit() app.clear_scodoc_cache() + return "" diff --git a/app/scodoc/sco_dump_db.py b/app/scodoc/sco_dump_db.py index 80a0b9c4..ad038ffe 100644 --- a/app/scodoc/sco_dump_db.py +++ b/app/scodoc/sco_dump_db.py @@ -28,8 +28,8 @@ """Dump base de données pour debug et support technique Le principe est le suivant: - 1- S'il existe une base en cours d'anonymisation, s'arrête et affiche un msg d'erreur l'utilisateur, - qui peut décider de la supprimer. + 1- S'il existe une base en cours d'anonymisation, s'arrête et affiche un msg + d'erreur à l'utilisateur, qui peut décider de la supprimer. 2- ScoDoc lance un script qui duplique la base (la copie de SCORT devient ANORT) - (si elle existe deja, s'arrête) @@ -49,9 +49,9 @@ pg_dump SCORT | psql ANORT import base64 import fcntl import os -import requests import subprocess -import traceback + +import requests from flask import g, request from flask_login import current_user @@ -59,9 +59,8 @@ from flask_login import current_user import app.scodoc.notesdb as ndb import app.scodoc.sco_utils as scu from app import log -from app.scodoc import sco_users -import sco_version from app.scodoc.sco_exceptions import ScoValueError +import sco_version SCO_DUMP_LOCK = "/tmp/scodump.lock" @@ -81,12 +80,10 @@ def sco_dump_and_send_db( try: x = open(SCO_DUMP_LOCK, "w+") fcntl.flock(x, fcntl.LOCK_EX | fcntl.LOCK_NB) - except (IOError, OSError): + except (IOError, OSError) as e: raise ScoValueError( - "Un envoi de la base " - + db_name - + " est déjà en cours, re-essayer plus tard" - ) + "Un envoi de la base {db_name} est déjà en cours, re-essayer plus tard" + ) from e try: # Drop if exists @@ -96,7 +93,7 @@ def sco_dump_and_send_db( _duplicate_db(db_name, ano_db_name) # Anonymisation - _anonymize_db(ano_db_name) + anonymize_db(ano_db_name) # Send r = _send_db(ano_db_name, message, request_url, traceback_str=traceback_str) @@ -116,44 +113,44 @@ def sco_dump_and_send_db( def _duplicate_db(db_name, ano_db_name): """Create new database, and copy old one into""" cmd = ["createdb", "-E", "UTF-8", ano_db_name] - log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd)) + log(f"sco_dump_and_send_db/_duplicate_db: {cmd}") try: _ = subprocess.check_output(cmd) except subprocess.CalledProcessError as e: - log("sco_dump_and_send_db: exception createdb {}".format(e)) + log(f"sco_dump_and_send_db: exception createdb {e}") raise ScoValueError( - "erreur lors de la creation de la base {}".format(ano_db_name) - ) + f"erreur lors de la creation de la base {ano_db_name}" + ) from e - cmd = "pg_dump {} | psql {}".format(db_name, ano_db_name) + cmd = f"pg_dump {db_name} | psql {ano_db_name}" log("sco_dump_and_send_db/_duplicate_db: {}".format(cmd)) try: _ = subprocess.check_output(cmd, shell=1) except subprocess.CalledProcessError as e: log("sco_dump_and_send_db: exception {}".format(e)) raise ScoValueError( - "erreur lors de la duplication de la base {} vers {}".format( - db_name, ano_db_name - ) - ) + f"erreur lors de la duplication de la base {db_name} vers {ano_db_name}" + ) from e -def _anonymize_db(ano_db_name): - """Anonymize a departement database""" +def anonymize_db(ano_db_name): + """Anonymize a ScoDoc database""" cmd = os.path.join(scu.SCO_TOOLS_DIR, "anonymize_db.py") - log("_anonymize_db: {}".format(cmd)) + log(f"anonymize_db: {cmd}") try: _ = subprocess.check_output([cmd, ano_db_name]) except subprocess.CalledProcessError as e: - log("sco_dump_and_send_db: exception in anonymisation: {}".format(e)) + log(f"sco_dump_and_send_db: exception in anonymisation: {e}") raise ScoValueError( - "erreur lors de l'anonymisation de la base {}".format(ano_db_name) - ) + f"erreur lors de l'anonymisation de la base {ano_db_name}" + ) from e def _get_scodoc_serial(): try: - with open(os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn")) as f: + with open( + os.path.join(scu.SCODOC_VERSION_DIR, "scodoc.sn"), encoding=scu.SCO_ENCODING + ) as f: return int(f.read()) except: return 0 diff --git a/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py b/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py new file mode 100644 index 00000000..0e8e15ff --- /dev/null +++ b/migrations/versions/d8288b7f0a3e_cascades_ref_comp.py @@ -0,0 +1,415 @@ +"""cascades ref. comp. + +Revision ID: d8288b7f0a3e +Revises: 5c7b208355df +Create Date: 2023-02-09 11:25:28.879434 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "d8288b7f0a3e" +down_revision = "5c7b208355df" +branch_labels = "" +depends_on = "" + + +def upgrade(): + # EVENTS + op.drop_constraint( + "scolar_events_formsemestre_id_fkey", "scolar_events", type_="foreignkey" + ) + op.drop_constraint("scolar_events_ue_id_fkey", "scolar_events", type_="foreignkey") + op.create_foreign_key( + "scolar_events_ue_id_fkey", + "scolar_events", + "notes_ue", + ["ue_id"], + ["id"], + ondelete="SET NULL", + ) + op.create_foreign_key( + "scolar_events_formsemestre_id_fkey", + "scolar_events", + "notes_formsemestre", + ["formsemestre_id"], + ["id"], + ondelete="SET NULL", + ) + # REF COMP + op.drop_constraint( + "apc_annee_parcours_parcours_id_fkey", "apc_annee_parcours", type_="foreignkey" + ) + op.create_foreign_key( + "apc_annee_parcours_parcours_id_fkey", + "apc_annee_parcours", + "apc_parcours", + ["parcours_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_app_critique_niveau_id_fkey", "apc_app_critique", type_="foreignkey" + ) + op.create_foreign_key( + "apc_app_critique_niveau_id_fkey", + "apc_app_critique", + "apc_niveau", + ["niveau_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_competence_referentiel_id_fkey", "apc_competence", type_="foreignkey" + ) + op.create_foreign_key( + "apc_competence_referentiel_id_fkey", + "apc_competence", + "apc_referentiel_competences", + ["referentiel_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_composante_essentielle_competence_id_fkey", + "apc_composante_essentielle", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_composante_essentielle_competence_id_fkey", + "apc_composante_essentielle", + "apc_competence", + ["competence_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_modules_acs_app_crit_id_fkey", "apc_modules_acs", type_="foreignkey" + ) + op.create_foreign_key( + "apc_modules_acs_app_crit_id_fkey", + "apc_modules_acs", + "apc_app_critique", + ["app_crit_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_niveau_competence_id_fkey", "apc_niveau", type_="foreignkey" + ) + op.create_foreign_key( + "apc_niveau_competence_id_fkey", + "apc_niveau", + "apc_competence", + ["competence_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_parcours_referentiel_id_fkey", "apc_parcours", type_="foreignkey" + ) + op.create_foreign_key( + "apc_parcours_referentiel_id_fkey", + "apc_parcours", + "apc_referentiel_competences", + ["referentiel_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_referentiel_competences_dept_id_fkey", + "apc_referentiel_competences", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_referentiel_competences_dept_id_fkey", + "apc_referentiel_competences", + "departement", + ["dept_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_situation_pro_competence_id_fkey", "apc_situation_pro", type_="foreignkey" + ) + op.create_foreign_key( + "apc_situation_pro_competence_id_fkey", + "apc_situation_pro", + "apc_competence", + ["competence_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "apc_validation_rcue_parcours_id_fkey", + "apc_validation_rcue", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_validation_rcue_parcours_id_fkey", + "apc_validation_rcue", + "apc_parcours", + ["parcours_id"], + ["id"], + ondelete="set null", + ) + op.drop_constraint( + "notes_formations_referentiel_competence_id_fkey", + "notes_formations", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formations_referentiel_competence_id_fkey", + "notes_formations", + "apc_referentiel_competences", + ["referentiel_competence_id"], + ["id"], + ondelete="SET NULL", + ) + op.drop_constraint( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + "apc_parcours", + ["parcour_id"], + ["id"], + ondelete="SET NULL", + ) + op.drop_constraint( + "notes_ue_niveau_competence_id_fkey", "notes_ue", type_="foreignkey" + ) + op.drop_constraint("notes_ue_parcour_id_fkey", "notes_ue", type_="foreignkey") + op.create_foreign_key( + "notes_ue_niveau_competence_id_fkey", + "notes_ue", + "apc_niveau", + ["niveau_competence_id"], + ["id"], + ondelete="SET NULL", + ) + op.create_foreign_key( + "notes_ue_parcour_id_fkey", + "notes_ue", + "apc_parcours", + ["parcour_id"], + ["id"], + ondelete="SET NULL", + ) + op.drop_constraint( + "parcours_formsemestre_parcours_id_fkey", + "parcours_formsemestre", + type_="foreignkey", + ) + op.create_foreign_key( + "parcours_formsemestre_parcours_id_fkey", + "parcours_formsemestre", + "apc_parcours", + ["parcours_id"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "parcours_modules_parcours_id_fkey", "parcours_modules", type_="foreignkey" + ) + op.create_foreign_key( + "parcours_modules_parcours_id_fkey", + "parcours_modules", + "apc_parcours", + ["parcours_id"], + ["id"], + ondelete="CASCADE", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + "parcours_modules_parcours_id_fkey", "parcours_modules", type_="foreignkey" + ) + op.create_foreign_key( + "parcours_modules_parcours_id_fkey", + "parcours_modules", + "apc_parcours", + ["parcours_id"], + ["id"], + ) + op.drop_constraint( + "parcours_formsemestre_parcours_id_fkey", + "parcours_formsemestre", + type_="foreignkey", + ) + op.create_foreign_key( + "parcours_formsemestre_parcours_id_fkey", + "parcours_formsemestre", + "apc_parcours", + ["parcours_id"], + ["id"], + ) + op.drop_constraint("notes_ue_parcour_id_fkey", "notes_ue", type_="foreignkey") + op.drop_constraint( + "notes_ue_niveau_competence_id_fkey", "notes_ue", type_="foreignkey" + ) + op.create_foreign_key( + "notes_ue_parcour_id_fkey", "notes_ue", "apc_parcours", ["parcour_id"], ["id"] + ) + op.create_foreign_key( + "notes_ue_niveau_competence_id_fkey", + "notes_ue", + "apc_niveau", + ["niveau_competence_id"], + ["id"], + ) + op.drop_constraint( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + "apc_parcours", + ["parcour_id"], + ["id"], + ) + op.drop_constraint( + "notes_formations_referentiel_competence_id_fkey", + "notes_formations", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formations_referentiel_competence_id_fkey", + "notes_formations", + "apc_referentiel_competences", + ["referentiel_competence_id"], + ["id"], + ) + op.drop_constraint( + "apc_validation_rcue_parcours_id_fkey", + "apc_validation_rcue", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_validation_rcue_parcours_id_fkey", + "apc_validation_rcue", + "apc_parcours", + ["parcours_id"], + ["id"], + ) + op.drop_constraint( + "apc_situation_pro_competence_id_fkey", "apc_situation_pro", type_="foreignkey" + ) + op.create_foreign_key( + "apc_situation_pro_competence_id_fkey", + "apc_situation_pro", + "apc_competence", + ["competence_id"], + ["id"], + ) + op.drop_constraint( + "apc_referentiel_competences_dept_id_fkey", + "apc_referentiel_competences", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_referentiel_competences_dept_id_fkey", + "apc_referentiel_competences", + "departement", + ["dept_id"], + ["id"], + ) + op.drop_constraint( + "apc_parcours_referentiel_id_fkey", "apc_parcours", type_="foreignkey" + ) + op.create_foreign_key( + "apc_parcours_referentiel_id_fkey", + "apc_parcours", + "apc_referentiel_competences", + ["referentiel_id"], + ["id"], + ) + op.drop_constraint( + "apc_niveau_competence_id_fkey", "apc_niveau", type_="foreignkey" + ) + op.create_foreign_key( + "apc_niveau_competence_id_fkey", + "apc_niveau", + "apc_competence", + ["competence_id"], + ["id"], + ) + op.drop_constraint( + "apc_modules_acs_app_crit_id_fkey", "apc_modules_acs", type_="foreignkey" + ) + op.create_foreign_key( + "apc_modules_acs_app_crit_id_fkey", + "apc_modules_acs", + "apc_app_critique", + ["app_crit_id"], + ["id"], + ) + op.drop_constraint( + "apc_composante_essentielle_competence_id_fkey", + "apc_composante_essentielle", + type_="foreignkey", + ) + op.create_foreign_key( + "apc_composante_essentielle_competence_id_fkey", + "apc_composante_essentielle", + "apc_competence", + ["competence_id"], + ["id"], + ) + op.drop_constraint( + "apc_competence_referentiel_id_fkey", "apc_competence", type_="foreignkey" + ) + op.create_foreign_key( + "apc_competence_referentiel_id_fkey", + "apc_competence", + "apc_referentiel_competences", + ["referentiel_id"], + ["id"], + ) + op.drop_constraint( + "apc_app_critique_niveau_id_fkey", "apc_app_critique", type_="foreignkey" + ) + op.create_foreign_key( + "apc_app_critique_niveau_id_fkey", + "apc_app_critique", + "apc_niveau", + ["niveau_id"], + ["id"], + ) + op.drop_constraint( + "apc_annee_parcours_parcours_id_fkey", "apc_annee_parcours", type_="foreignkey" + ) + op.create_foreign_key( + "apc_annee_parcours_parcours_id_fkey", + "apc_annee_parcours", + "apc_parcours", + ["parcours_id"], + ["id"], + ) + # EVENTS + op.drop_constraint("scolar_events_ue_id_fkey", "scolar_events", type_="foreignkey") + op.drop_constraint( + "scolar_events_formsemestre_id_fkey", "scolar_events", type_="foreignkey" + ) + op.create_foreign_key( + "scolar_events_ue_id_fkey", "scolar_events", "notes_ue", ["ue_id"], ["id"] + ) + op.create_foreign_key( + "scolar_events_formsemestre_id_fkey", + "scolar_events", + "notes_formsemestre", + ["formsemestre_id"], + ["id"], + ) + + # ### end Alembic commands ### diff --git a/scodoc.py b/scodoc.py index 12556c2c..ec197aa7 100755 --- a/scodoc.py +++ b/scodoc.py @@ -41,6 +41,7 @@ from app.models.but_refcomp import ( ) from app.models.but_validations import ApcValidationAnnee, ApcValidationRCUE from app.models.evaluations import Evaluation +from app.scodoc import sco_dump_db from app.scodoc.sco_logos import make_logo_local from app.scodoc.sco_permissions import Permission from app.views import notes, scolar @@ -131,6 +132,18 @@ def sco_db_init(erase=False): # sco-db-init initialize_scodoc_database(erase=erase) +@app.cli.command() +@click.argument("database") +def anonymize_db(database): # anonymize-db + """Anonymise la base de nom indiqué (et non pas la base courante!)""" + click.confirm( + f"L'anonymisation va affecter la base {database} et PERDRE beaucoup de données.\nContinuer ?", + abort=True, + ) + sco_dump_db.anonymize_db(database) + click.echo(f"Base {database} pseudonymisée") + + @app.cli.command() def user_db_clear(): """Erase all users and roles from the database !""" @@ -397,9 +410,11 @@ def delete_dept(dept, force=False): # delete-dept sys.stderr.write(f"Erreur: le departement {dept} n'existe pas !\n") return 2 elif d: - sco_dept.delete_dept(d.id) + msg = sco_dept.delete_dept(d.id) db.session.commit() - return 0 + if msg: + print(f"Erreur:\n {msg}") + return 0 if not msg else 1 @app.cli.command() diff --git a/tools/anonymize_db.py b/tools/anonymize_db.py index a6825098..2e5040a2 100755 --- a/tools/anonymize_db.py +++ b/tools/anonymize_db.py @@ -26,8 +26,6 @@ # ############################################################################## -# TODO à tester avec ScoDoc9, devrait fonctionner sans problème majeur ? - """Anonymize une base de données ScoDoc Runned as user "scodoc" with scodoc and postgresql up.