From c1a9264edc0c0de075a68c8f9cc6ee80892946f6 Mon Sep 17 00:00:00 2001 From: John Davis Date: Thu, 5 Sep 2024 21:23:23 -0400 Subject: [PATCH] Add scripts to fix association null values --- .../model/scripts/association_table_fixer.py | 87 +++++++++++++++++++ .../db/fix_group_role_association_nulls.py | 12 ++- .../db/fix_user_group_association_nulls.py | 12 ++- scripts/db/fix_user_role_association_nulls.py | 12 ++- 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 lib/galaxy/model/scripts/association_table_fixer.py mode change 100644 => 100755 scripts/db/fix_group_role_association_nulls.py mode change 100644 => 100755 scripts/db/fix_user_group_association_nulls.py mode change 100644 => 100755 scripts/db/fix_user_role_association_nulls.py diff --git a/lib/galaxy/model/scripts/association_table_fixer.py b/lib/galaxy/model/scripts/association_table_fixer.py new file mode 100644 index 000000000000..a8a713f2be60 --- /dev/null +++ b/lib/galaxy/model/scripts/association_table_fixer.py @@ -0,0 +1,87 @@ +import os +import sys +from abc import ( + ABC, + abstractmethod, +) + +from sqlalchemy import ( + create_engine, + delete, + func, + null, + or_, + select, +) + +from galaxy.model import ( + Base, + GroupRoleAssociation, + UserGroupAssociation, + UserRoleAssociation, +) +from galaxy.model.orm.scripts import get_config + + +class AssociationNullFix(ABC): + + def __init__(self, association_model: Base): + self.assoc_model = association_model + self.assoc_name = association_model.__tablename__ + self.where_clause = self.build_where_clause() + + def run(self): + config = get_config(sys.argv, use_argparse=False, cwd=os.getcwd()) + engine = create_engine(config["db_url"]) + + invalid_assocs = self.count_associations_with_nulls(engine) + if not invalid_assocs: + print(f"Your database does not contain invalid {self.assoc_name} records") + return + + print(f"Your database contains {invalid_assocs} invalid {self.assoc_name} records") + answer = input(f'Delete {invalid_assocs} invalid records? (type "yes" to confirm)\n') + if answer.lower() == "yes": + self.delete_associations_with_nulls(engine) + else: + print("Operation aborted") + + def count_associations_with_nulls(self, engine): + """ + Retrieve association records where one or both associated item ids are null. + """ + select_stmt = select(func.count()).where(self.where_clause) + with engine.connect() as conn: + return conn.scalar(select_stmt) + + def delete_associations_with_nulls(self, engine): + """ + Delete association records where one or both associated item ids are null. + """ + delete_stmt = delete(self.assoc_model).where(self.where_clause) + with engine.begin() as conn: + result = conn.execute(delete_stmt) + conn.commit() + print(f"{result.rowcount} invalid records have been deleted") + + @abstractmethod + def build_where_clause(self): + """Build where clause for filtering records containing nulls instead of associated item ids""" + + +class UserGroupAssociationNullFix(AssociationNullFix): + + def build_where_clause(self): + return or_(UserGroupAssociation.user_id == null(), UserGroupAssociation.group_id == null()) + + +class UserRoleAssociationNullFix(AssociationNullFix): + + def build_where_clause(self): + return or_(UserRoleAssociation.user_id == null(), UserRoleAssociation.role_id == null()) + + +class GroupRoleAssociationNullFix(AssociationNullFix): + + def build_where_clause(self): + return or_(GroupRoleAssociation.group_id == null(), GroupRoleAssociation.role_id == null()) diff --git a/scripts/db/fix_group_role_association_nulls.py b/scripts/db/fix_group_role_association_nulls.py old mode 100644 new mode 100755 index 464090415c47..c7aa85d3b0a0 --- a/scripts/db/fix_group_role_association_nulls.py +++ b/scripts/db/fix_group_role_association_nulls.py @@ -1 +1,11 @@ -# TODO +import os +import sys + +sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "lib"))) + +from galaxy.model import GroupRoleAssociation +from galaxy.model.scripts.association_table_fixer import GroupRoleAssociationNullFix + +if __name__ == "__main__": + assoc_fix = GroupRoleAssociationNullFix(GroupRoleAssociation) + assoc_fix.run() diff --git a/scripts/db/fix_user_group_association_nulls.py b/scripts/db/fix_user_group_association_nulls.py old mode 100644 new mode 100755 index 464090415c47..ef09e582a8f4 --- a/scripts/db/fix_user_group_association_nulls.py +++ b/scripts/db/fix_user_group_association_nulls.py @@ -1 +1,11 @@ -# TODO +import os +import sys + +sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "lib"))) + +from galaxy.model import UserGroupAssociation +from galaxy.model.scripts.association_table_fixer import UserGroupAssociationNullFix + +if __name__ == "__main__": + assoc_fix = UserGroupAssociationNullFix(UserGroupAssociation) + assoc_fix.run() diff --git a/scripts/db/fix_user_role_association_nulls.py b/scripts/db/fix_user_role_association_nulls.py old mode 100644 new mode 100755 index 464090415c47..a493d2b4f0dc --- a/scripts/db/fix_user_role_association_nulls.py +++ b/scripts/db/fix_user_role_association_nulls.py @@ -1 +1,11 @@ -# TODO +import os +import sys + +sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "lib"))) + +from galaxy.model import UserRoleAssociation +from galaxy.model.scripts.association_table_fixer import UserRoleAssociationNullFix + +if __name__ == "__main__": + assoc_fix = UserRoleAssociationNullFix(UserRoleAssociation) + assoc_fix.run()