diff --git a/app/but/import_refcomp.py b/app/but/import_refcomp.py index 0f97cd95..ae44a34c 100644 --- a/app/but/import_refcomp.py +++ b/app/but/import_refcomp.py @@ -4,7 +4,6 @@ # See LICENSE ############################################################################## from xml.etree import ElementTree -from typing import TextIO import sqlalchemy diff --git a/app/comp/res_common.py b/app/comp/res_common.py index 5170875d..654a1ae3 100644 --- a/app/comp/res_common.py +++ b/app/comp/res_common.py @@ -70,6 +70,7 @@ class ResultatsSemestre(ResultatsCache): self.etud_moy_gen: pd.Series = None self.etud_moy_gen_ranks = {} self.etud_moy_gen_ranks_int = {} + self.moy_gen_rangs_by_group = None # virtual self.modimpl_inscr_df: pd.DataFrame = None "Inscriptions: row etudid, col modimlpl_id" self.modimpls_results: ModuleImplResults = None @@ -824,17 +825,25 @@ class ResultatsSemestre(ResultatsCache): self.formsemestre.id ) first_partition = True + col_order = 10 for partition in partitions: cid = f"part_{partition['partition_id']}" + rg_cid = cid + "_rg" # rang dans la partition titles[cid] = partition["partition_name"] if first_partition: klass = "partition" else: klass = "partition partition_aux" titles[f"_{cid}_class"] = klass - titles[f"_{cid}_col_order"] = 10 + titles[f"_{cid}_col_order"] = col_order + titles[f"_{rg_cid}_col_order"] = col_order + 1 + col_order += 2 + if partition["bul_show_rank"]: + titles[rg_cid] = f"Rg {partition['partition_name']}" + titles[f"_{rg_cid}_class"] = "partition_rangs" partition_etud_groups = partitions_etud_groups[partition["partition_id"]] for row in rows: + group = None # group (dict) de l'étudiant dans cette partition # dans NotesTableCompat, à revoir etud_etat = self.get_etud_etat(row["etudid"]) if etud_etat == "D": @@ -847,8 +856,17 @@ class ResultatsSemestre(ResultatsCache): group = partition_etud_groups.get(row["etudid"]) gr_name = group["group_name"] if group else "" if gr_name: - row[f"{cid}"] = gr_name + row[cid] = gr_name row[f"_{cid}_class"] = klass + # Rangs dans groupe + if ( + partition["bul_show_rank"] + and (group is not None) + and (group["id"] in self.moy_gen_rangs_by_group) + ): + rang = self.moy_gen_rangs_by_group[group["id"]][0] + row[rg_cid] = rang.get(row["etudid"], "") + first_partition = False def _recap_add_evaluations( diff --git a/app/models/absences.py b/app/models/absences.py index 830d46f9..405ea6bf 100644 --- a/app/models/absences.py +++ b/app/models/absences.py @@ -11,7 +11,9 @@ class Absence(db.Model): __tablename__ = "absences" id = db.Column(db.Integer, primary_key=True) - etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True) + etudid = db.Column( + db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE"), index=True + ) jour = db.Column(db.Date) estabs = db.Column(db.Boolean()) estjust = db.Column(db.Boolean()) @@ -50,7 +52,7 @@ class AbsenceNotification(db.Model): id = db.Column(db.Integer, primary_key=True) etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) notification_date = db.Column( db.DateTime(timezone=True), server_default=db.func.now() diff --git a/app/models/etudiants.py b/app/models/etudiants.py index 42ef1778..6c9504e2 100644 --- a/app/models/etudiants.py +++ b/app/models/etudiants.py @@ -436,7 +436,7 @@ class Adresse(db.Model): adresse_id = db.synonym("id") etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) email = db.Column(db.Text()) # mail institutionnel emailperso = db.Column(db.Text) # email personnel (exterieur) @@ -470,7 +470,7 @@ class Admission(db.Model): adm_id = db.synonym("id") etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) # Anciens champs de ScoDoc7, à revoir pour être plus générique et souple # notamment dans le cadre du bac 2021 @@ -540,7 +540,7 @@ class ItemSuivi(db.Model): itemsuivi_id = db.synonym("id") etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) item_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) situation = db.Column(db.Text) diff --git a/app/models/formsemestre.py b/app/models/formsemestre.py index 2f1f9259..90461c98 100644 --- a/app/models/formsemestre.py +++ b/app/models/formsemestre.py @@ -323,6 +323,25 @@ class FormSemestre(db.Model): return "" return ", ".join(sorted([etape.etape_apo for etape in self.etapes if etape])) + def regroupements_coherents_etud(self) -> list[tuple[UniteEns, UniteEns]]: + """Calcule la liste des regroupements cohérents d'UE impliquant ce + formsemestre. + Pour une année donnée: l'étudiant est inscrit dans ScoDoc soit dans le semestre + impair, soit pair, soit les deux (il est rare mais pas impossible d'avoir une + inscription seulement en semestre pair, par exemple suite à un transfert ou un + arrêt temporaire du cursus). + + 1. Déterminer l'*autre* formsemestre: semestre précédent ou suivant de la même + année, formation compatible (même référentiel de compétence) dans lequel + l'étudiant est inscrit. + + 2. Construire les couples d'UE (regroupements cohérents): apparier les UE qui + ont le même `ApcParcoursNiveauCompetence`. + """ + if not self.formation.is_apc(): + return [] + raise NotImplementedError() # XXX + def responsables_str(self, abbrev_prenom=True) -> str: """chaîne "J. Dupond, X. Martin" ou "Jacques Dupond, Xavier Martin" @@ -614,7 +633,9 @@ class FormSemestreInscription(db.Model): id = db.Column(db.Integer, primary_key=True) formsemestre_inscription_id = db.synonym("id") - etudid = db.Column(db.Integer, db.ForeignKey("identite.id"), index=True) + etudid = db.Column( + db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE"), index=True + ) formsemestre_id = db.Column( db.Integer, db.ForeignKey("notes_formsemestre.id"), @@ -634,11 +655,16 @@ class FormSemestreInscription(db.Model): ) # I inscrit, D demission en cours de semestre, DEF si "defaillant" etat = db.Column(db.String(CODE_STR_LEN), index=True) - # etape apogee d'inscription (experimental 2020) + # 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 = db.relationship(ApcParcours) def __repr__(self): - return f"<{self.__class__.__name__} {self.id} etudid={self.etudid} sem={self.formsemestre_id} etat={self.etat}>" + return f"""<{self.__class__.__name__} {self.id} etudid={self.etudid} sem={ + self.formsemestre_id} etat={self.etat} { + ('parcours='+str(self.parcour)) if self.parcour else ''}>""" class NotesSemSet(db.Model): diff --git a/app/models/groups.py b/app/models/groups.py index 4c64ad54..1d24b60c 100644 --- a/app/models/groups.py +++ b/app/models/groups.py @@ -106,7 +106,7 @@ class GroupDescr(db.Model): group_membership = db.Table( "group_membership", - db.Column("etudid", db.Integer, db.ForeignKey("identite.id")), + db.Column("etudid", db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE")), db.Column("group_id", db.Integer, db.ForeignKey("group_descr.id")), db.UniqueConstraint("etudid", "group_id"), ) @@ -116,5 +116,5 @@ group_membership = db.Table( # __tablename__ = "group_membership" # __table_args__ = (db.UniqueConstraint("etudid", "group_id"),) # id = db.Column(db.Integer, primary_key=True) -# etudid = db.Column(db.Integer, db.ForeignKey("identite.id")) +# etudid = db.Column(db.Integer, db.ForeignKey("identite.id", ondelete="CASCADE")) # group_id = db.Column(db.Integer, db.ForeignKey("group_descr.id")) diff --git a/app/models/notes.py b/app/models/notes.py index 6da4ef5d..f88f8728 100644 --- a/app/models/notes.py +++ b/app/models/notes.py @@ -17,7 +17,7 @@ class BulAppreciations(db.Model): date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), index=True, ) formsemestre_id = db.Column( @@ -36,7 +36,7 @@ class NotesNotes(db.Model): id = db.Column(db.Integer, primary_key=True) etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) evaluation_id = db.Column( db.Integer, db.ForeignKey("notes_evaluation.id"), index=True @@ -56,7 +56,7 @@ class NotesNotesLog(db.Model): etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) evaluation_id = db.Column( db.Integer, diff --git a/app/models/validations.py b/app/models/validations.py index 0bf487f3..64bdaef8 100644 --- a/app/models/validations.py +++ b/app/models/validations.py @@ -19,7 +19,7 @@ class ScolarFormSemestreValidation(db.Model): formsemestre_validation_id = db.synonym("id") etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), index=True, ) formsemestre_id = db.Column( @@ -66,7 +66,7 @@ class ScolarAutorisationInscription(db.Model): etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) formation_code = db.Column(db.String(SHORT_STR_LEN), nullable=False) # semestre ou on peut s'inscrire: @@ -86,7 +86,7 @@ class ScolarEvent(db.Model): event_id = db.synonym("id") etudid = db.Column( db.Integer, - db.ForeignKey("identite.id"), + db.ForeignKey("identite.id", ondelete="CASCADE"), ) event_date = db.Column(db.DateTime(timezone=True), server_default=db.func.now()) formsemestre_id = db.Column( diff --git a/app/scodoc/sco_edit_formation.py b/app/scodoc/sco_edit_formation.py index aabfaddc..64a7da11 100644 --- a/app/scodoc/sco_edit_formation.py +++ b/app/scodoc/sco_edit_formation.py @@ -244,7 +244,11 @@ def formation_edit(formation_id=None, create=False): return ( "\n".join(H) + tf_error_message( - "Valeurs incorrectes: il existe déjà une formation avec même titre, acronyme et version." + f"""Valeurs incorrectes: il existe déjà une formation avec même titre, + acronyme et version. + """ ) + tf[1] + html_sco_header.sco_footer() diff --git a/app/scodoc/sco_edit_module.py b/app/scodoc/sco_edit_module.py index 38126145..04a2cd97 100644 --- a/app/scodoc/sco_edit_module.py +++ b/app/scodoc/sco_edit_module.py @@ -352,7 +352,7 @@ def module_edit( title = f"""Création {object_name} dans la formation {formation.acronyme}""" else: - page_title = "Modification du module {module.code or module.titre or ''}" + page_title = f"Modification du module {module.code or module.titre or ''}" title = f"""Modification du module {module.code or ''} {module.titre or ''} (formation {formation.acronyme}, version {formation.version}) """ diff --git a/app/scodoc/sco_groups.py b/app/scodoc/sco_groups.py index e377dabe..8eae60e0 100644 --- a/app/scodoc/sco_groups.py +++ b/app/scodoc/sco_groups.py @@ -1009,7 +1009,7 @@ def edit_partition_form(formsemestre_id=None): """ ) - if formsemestre.formation.is_apc() and "Parcours" not in ( + if formsemestre.formation.is_apc() and scu.PARTITION_PARCOURS not in ( p["partition_name"] for p in partitions ): # propose création partition "Parcours" @@ -1068,9 +1068,7 @@ def partition_set_attr(partition_id, attr, value): partition[attr] = value partitionEditor.edit(cnx, partition) # invalid bulletin cache - sco_cache.invalidate_formsemestre( - pdfonly=True, formsemestre_id=partition["formsemestre_id"] - ) + sco_cache.invalidate_formsemestre(formsemestre_id=partition["formsemestre_id"]) return "enregistré" diff --git a/app/scodoc/sco_utils.py b/app/scodoc/sco_utils.py index c1e7dca2..e8c29585 100644 --- a/app/scodoc/sco_utils.py +++ b/app/scodoc/sco_utils.py @@ -111,6 +111,8 @@ MODULE_TYPE_NAMES = { None: "Module", } +PARTITION_PARCOURS = "Parcours" + MALUS_MAX = 20.0 MALUS_MIN = -20.0 diff --git a/app/static/js/table_recap.js b/app/static/js/table_recap.js index 36426b50..fd5068e6 100644 --- a/app/static/js/table_recap.js +++ b/app/static/js/table_recap.js @@ -15,13 +15,23 @@ $(function () { }, { name: "toggle_partitions", - text: "Toutes les partitions", + text: "Montrer groupes", action: function (e, dt, node, config) { let visible = dt.columns(".partition_aux").visible()[0]; dt.columns(".partition_aux").visible(!visible); - dt.buttons('toggle_partitions:name').text(visible ? "Toutes les partitions" : "Cacher les partitions"); + dt.buttons('toggle_partitions:name').text(visible ? "Montrer groupes" : "Cacher les groupes"); } - }]; + }, + { + name: "toggle_partitions_rangs", + text: "Rangs groupes", + action: function (e, dt, node, config) { + let rangs_visible = dt.columns(".partition_rangs").visible()[0]; + dt.columns(".partition_rangs").visible(!rangs_visible); + dt.buttons('toggle_partitions_rangs:name').text(rangs_visible ? "Rangs groupes" : "Cacher rangs groupes"); + } + }, + ]; if (!$('table.table_recap').hasClass("jury")) { buttons.push( $('table.table_recap').hasClass("apc") ? @@ -95,7 +105,7 @@ $(function () { "columnDefs": [ { // cache les codes, le détail de l'identité, les groupes, les colonnes admission et les vides - targets: ["codes", "identite_detail", "partition_aux", "admission", "col_empty"], + targets: ["codes", "identite_detail", "partition_aux", "partition_rangs", "admission", "col_empty"], visible: false, }, { diff --git a/app/templates/config_codes_decisions.html b/app/templates/config_codes_decisions.html index 0c2f32b2..5f92aa8d 100644 --- a/app/templates/config_codes_decisions.html +++ b/app/templates/config_codes_decisions.html @@ -6,12 +6,12 @@
-

Ces codes (ADM, AJ, ...) sont utilisés pour représenter les décisions de jury -et les validations de semestres ou d'UE. les valeurs indiquées ici sont utilisées -dans les exports Apogée. -

-

Ne les modifier que si vous savez ce que vous faites ! -

+

Ces codes (ADM, AJ, ...) sont utilisés pour représenter les décisions de jury + et les validations de semestres ou d'UE. + Les valeurs indiquées ici sont utilisées dans les exports Apogée. +

+

Ne les modifier que si vous savez ce que vous faites ! +

diff --git a/app/templates/pn/ue_infos.html b/app/templates/pn/ue_infos.html index c5a0f777..5a624558 100644 --- a/app/templates/pn/ue_infos.html +++ b/app/templates/pn/ue_infos.html @@ -15,7 +15,7 @@
  • Code: {{ue.ue_code}}
  • Type: {{ue.type}}
  • Externe: {{ "oui" if ue.is_external else "non" }}
  • -
  • Code Apogée: {{ue.code_apogee}}
  • +
  • Code Apogée: {{ue.code_apogee or "aucun"}}
  • Formation: diff --git a/app/views/scolar.py b/app/views/scolar.py index 4bc3f27f..8a07d489 100644 --- a/app/views/scolar.py +++ b/app/views/scolar.py @@ -915,13 +915,14 @@ sco_publish( @permission_required(Permission.ScoView) @scodoc7func def create_partition_parcours(formsemestre_id): - """Création d'une partitions nommée "Parcours" avec un groupe par parcours.""" + """Création d'une partitions nommée "Parcours" (PARTITION_PARCOURS) + avec un groupe par parcours.""" formsemestre = FormSemestre.query.get_or_404(formsemestre_id) - if "Parcours" in (p.partition_name for p in formsemestre.partitions): - flash("""Partition "Parcours" déjà existante""") + if scu.PARTITION_PARCOURS in (p.partition_name for p in formsemestre.partitions): + flash(f"""Partition "{scu.PARTITION_PARCOURS}" déjà existante""") else: partition_id = sco_groups.partition_create( - formsemestre_id, partition_name="Parcours", redirect=False + formsemestre_id, partition_name=scu.PARTITION_PARCOURS, redirect=False ) n = 0 for parcour in formsemestre.parcours: diff --git a/migrations/versions/a2771105c21c_parcours_inscriptions_casc.py b/migrations/versions/a2771105c21c_parcours_inscriptions_casc.py new file mode 100644 index 00000000..55d194e0 --- /dev/null +++ b/migrations/versions/a2771105c21c_parcours_inscriptions_casc.py @@ -0,0 +1,308 @@ +"""Formsemestre / parcours, Inscriptions aux parcours + cascades sur Identite + +Revision ID: a2771105c21c +Revises: 6002d7d366e5 +Create Date: 2022-05-25 20:32:06.868296 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = "a2771105c21c" +down_revision = "6002d7d366e5" +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "parcours_formsemestre", + sa.Column("parcours_id", sa.Integer(), nullable=False), + sa.Column("formsemestre_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint( + ["formsemestre_id"], ["notes_formsemestre.id"], ondelete="CASCADE" + ), + sa.ForeignKeyConstraint( + ["parcours_id"], + ["apc_parcours.id"], + ), + sa.PrimaryKeyConstraint("parcours_id", "formsemestre_id"), + ) + op.drop_constraint("absences_etudid_fkey", "absences", type_="foreignkey") + op.create_foreign_key( + "absences_etudid_fkey", + "absences", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "absences_notifications_etudid_fkey", + "absences_notifications", + type_="foreignkey", + ) + op.create_foreign_key( + "absences_notifications_etudid_fkey", + "absences_notifications", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint("admissions_etudid_fkey", "admissions", type_="foreignkey") + op.create_foreign_key( + "admissions_etudid_fkey", + "admissions", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint("adresse_etudid_fkey", "adresse", type_="foreignkey") + op.create_foreign_key( + "adresse_etudid_fkey", + "adresse", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "group_membership_etudid_fkey", "group_membership", type_="foreignkey" + ) + op.create_foreign_key( + "group_membership_etudid_fkey", + "group_membership", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint("itemsuivi_etudid_fkey", "itemsuivi", type_="foreignkey") + op.create_foreign_key( + "itemsuivi_etudid_fkey", + "itemsuivi", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "notes_appreciations_etudid_fkey", "notes_appreciations", type_="foreignkey" + ) + op.create_foreign_key( + "notes_appreciations_etudid_fkey", + "notes_appreciations", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + # INSCRIPTIONS + op.drop_constraint( + "notes_formsemestre_inscription_etudid_fkey", + "notes_formsemestre_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formsemestre_inscription_etudid_fkey", + "notes_formsemestre_inscription", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.add_column( + "notes_formsemestre_inscription", + sa.Column("parcour_id", sa.Integer(), nullable=True), + ) + op.create_index( + op.f("ix_notes_formsemestre_inscription_parcour_id"), + "notes_formsemestre_inscription", + ["parcour_id"], + unique=False, + ) + op.create_foreign_key( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + "apc_parcours", + ["parcour_id"], + ["id"], + ) + # --- + op.drop_constraint("notes_notes_etudid_fkey", "notes_notes", type_="foreignkey") + op.create_foreign_key( + "notes_notes_etudid_fkey", + "notes_notes", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "notes_notes_log_etudid_fkey", "notes_notes_log", type_="foreignkey" + ) + op.create_foreign_key( + "notes_notes_log_etudid_fkey", + "notes_notes_log", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "scolar_autorisation_inscription_etudid_fkey", + "scolar_autorisation_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "scolar_autorisation_inscription_etudid_fkey", + "scolar_autorisation_inscription", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint("scolar_events_etudid_fkey", "scolar_events", type_="foreignkey") + op.create_foreign_key( + "scolar_events_etudid_fkey", + "scolar_events", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + op.drop_constraint( + "scolar_formsemestre_validation_etudid_fkey", + "scolar_formsemestre_validation", + type_="foreignkey", + ) + op.create_foreign_key( + "scolar_formsemestre_validation_etudid_fkey", + "scolar_formsemestre_validation", + "identite", + ["etudid"], + ["id"], + ondelete="CASCADE", + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint( + "scolar_formsemestre_validation_etudid_fkey", + "scolar_formsemestre_validation", + type_="foreignkey", + ) + op.create_foreign_key( + "scolar_formsemestre_validation_etudid_fkey", + "scolar_formsemestre_validation", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint("scolar_events_etudid_fkey", "scolar_events", type_="foreignkey") + op.create_foreign_key( + "scolar_events_etudid_fkey", "scolar_events", "identite", ["etudid"], ["id"] + ) + op.drop_constraint( + "scolar_autorisation_inscription_etudid_fkey", + "scolar_autorisation_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "scolar_autorisation_inscription_etudid_fkey", + "scolar_autorisation_inscription", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint( + "notes_notes_log_etudid_fkey", "notes_notes_log", type_="foreignkey" + ) + op.create_foreign_key( + "notes_notes_log_etudid_fkey", "notes_notes_log", "identite", ["etudid"], ["id"] + ) + op.drop_constraint("notes_notes_etudid_fkey", "notes_notes", type_="foreignkey") + op.create_foreign_key( + "notes_notes_etudid_fkey", "notes_notes", "identite", ["etudid"], ["id"] + ) + # INSCRIPTIONS + op.drop_constraint( + "notes_formsemestre_inscription_etudid_fkey", + "notes_formsemestre_inscription", + type_="foreignkey", + ) + op.create_foreign_key( + "notes_formsemestre_inscription_etudid_fkey", + "notes_formsemestre_inscription", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint( + "notes_formsemestre_inscription_parcour_id_fkey", + "notes_formsemestre_inscription", + type_="foreignkey", + ) + op.drop_index( + op.f("ix_notes_formsemestre_inscription_parcour_id"), + table_name="notes_formsemestre_inscription", + ) + op.drop_column("notes_formsemestre_inscription", "parcour_id") + # -- + op.drop_constraint( + "notes_appreciations_etudid_fkey", "notes_appreciations", type_="foreignkey" + ) + op.create_foreign_key( + "notes_appreciations_etudid_fkey", + "notes_appreciations", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint("itemsuivi_etudid_fkey", "itemsuivi", type_="foreignkey") + op.create_foreign_key( + "itemsuivi_etudid_fkey", "itemsuivi", "identite", ["etudid"], ["id"] + ) + op.drop_constraint( + "group_membership_etudid_fkey", "group_membership", type_="foreignkey" + ) + op.create_foreign_key( + "group_membership_etudid_fkey", + "group_membership", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint("adresse_etudid_fkey", "adresse", type_="foreignkey") + op.create_foreign_key( + "adresse_etudid_fkey", "adresse", "identite", ["etudid"], ["id"] + ) + op.drop_constraint("admissions_etudid_fkey", "admissions", type_="foreignkey") + op.create_foreign_key( + "admissions_etudid_fkey", "admissions", "identite", ["etudid"], ["id"] + ) + op.drop_constraint( + "absences_notifications_etudid_fkey", + "absences_notifications", + type_="foreignkey", + ) + op.create_foreign_key( + "absences_notifications_etudid_fkey", + "absences_notifications", + "identite", + ["etudid"], + ["id"], + ) + op.drop_constraint("absences_etudid_fkey", "absences", type_="foreignkey") + op.create_foreign_key( + "absences_etudid_fkey", "absences", "identite", ["etudid"], ["id"] + ) + op.drop_table("parcours_formsemestre") + # ### end Alembic commands ### diff --git a/tests/unit/test_sco_basic.py b/tests/unit/test_sco_basic.py index a9f6b19a..a00ab66e 100644 --- a/tests/unit/test_sco_basic.py +++ b/tests/unit/test_sco_basic.py @@ -14,6 +14,7 @@ Au besoin, créer un base de test neuve: import random from flask import g +from app.models.formsemestre import FormSemestreInscription from config import TestConfig from tests.unit import sco_fake_gen @@ -85,6 +86,15 @@ def run_sco_basic(verbose=False): # --- Inscription des étudiants for etud in etuds: G.inscrit_etudiant(formsemestre_id, etud) + # Vérification incription semestre: + q = FormSemestreInscription.query.filter_by( + etudid=etuds[0].id, formsemestre_id=formsemestre_id + ) + assert q.count() == 1 + ins = q.first() + assert ins.etape == None + assert ins.etat == "I" + assert ins.parcour == None # --- Creation évaluation e = G.create_evaluation( @@ -217,3 +227,15 @@ def run_sco_basic(verbose=False): dec_ues = nt.get_etud_decision_ues(etud["etudid"]) for ue_id in dec_ues: assert dec_ues[ue_id]["code"] in {"ADM", "CMP"} + + # ---- Suppression étudiant, vérification inscription + # (permet de tester les cascades) + etud = etuds[0] + etudid = etud.id + db.session.delete(etud) + db.session.commit() + # Vérification incription semestre: + q = FormSemestreInscription.query.filter_by( + etudid=etudid, formsemestre_id=formsemestre_id + ) + assert q.count() == 0