diff --git a/builder/actions.py b/builder/actions.py index 086ae0be3..d1bd6a53b 100644 --- a/builder/actions.py +++ b/builder/actions.py @@ -130,8 +130,9 @@ def create_search(*, draft, term=None, code=None, codes): @transaction.atomic def delete_search(*, search): - # Grab the PK before we delete the instance + # Grab the PK and related version before we delete the instance search_pk = search.pk + version = search.version # Delete any codes that: # - only belong to this search @@ -139,10 +140,10 @@ def delete_search(*, search): # - are not descendants of an included code # Find all code objs that belong to this search and no others - search_only_code_objs = search.version.code_objs.annotate( + search_only_code_objs = version.code_objs.annotate( num_results=Count("results") ).filter(results__search=search, num_results=1) - codes_to_keep = get_codes_to_keep(search.version, search_only_code_objs) + codes_to_keep = get_codes_to_keep(version, search_only_code_objs) # Delete any code objs that belong to this search only, and are not in the codes_to_keep set search_only_code_objs.exclude(code__in=codes_to_keep).delete() @@ -150,6 +151,11 @@ def delete_search(*, search): # Delete the search search.delete() + # Update the cached hierarchy + from codelists.actions import cache_hierarchy # avoid circular imports + + cache_hierarchy(version=version) + logger.info("Deleted Search", search_pk=search_pk) diff --git a/codelists/actions.py b/codelists/actions.py index 208185bf7..6b6331db7 100644 --- a/codelists/actions.py +++ b/codelists/actions.py @@ -452,7 +452,7 @@ def _create_version_with_codes( for code, status in codeset.code_to_status.items() ) - cache_hierarchy(version=clv, hierarchy=hierarchy) + cache_hierarchy(version=clv) return clv diff --git a/codelists/models.py b/codelists/models.py index 2c2748ecb..6c6cc859f 100644 --- a/codelists/models.py +++ b/codelists/models.py @@ -476,24 +476,26 @@ def full_slug(self): def has_hierarchy(self): return self.coding_system.is_builder_compatible() - def calculate_hierarchy(self): + def calculate_hierarchy(self, coding_system=None): """Return Hierarchy of codes related to this CodelistVersion.""" if self.csv_data: - return self._calculate_old_style_hierarchy() + return self._calculate_old_style_hierarchy(coding_system=None) else: - return self._calculate_new_style_hierarchy() + return self._calculate_new_style_hierarchy(coding_system=None) - def _calculate_old_style_hierarchy(self): + def _calculate_old_style_hierarchy(self, coding_system): if not self.has_hierarchy: # If coding system does not define relationships, then we cannot build a # hierarchy, and so it's not clear what a hierarchy is for. return - return Hierarchy.from_codes(self.coding_system, self.codes) + return Hierarchy.from_codes(coding_system or self.coding_system, self.codes) - def _calculate_new_style_hierarchy(self): + def _calculate_new_style_hierarchy(self, coding_system): code_to_status = dict(self.code_objs.values_list("code", "status")) - return Hierarchy.from_codes(self.coding_system, list(code_to_status)) + return Hierarchy.from_codes( + coding_system or self.coding_system, list(code_to_status) + ) @cached_property def hierarchy(self): diff --git a/coding_systems/base/import_data_utils.py b/coding_systems/base/import_data_utils.py index 691e28bf7..86fa5f113 100644 --- a/coding_systems/base/import_data_utils.py +++ b/coding_systems/base/import_data_utils.py @@ -10,7 +10,6 @@ from tqdm import tqdm from codelists.coding_systems import CODING_SYSTEMS -from codelists.hierarchy import Hierarchy from codelists.models import CodelistVersion, Status from codelists.search import do_search from coding_systems.versioning.models import ( @@ -347,7 +346,7 @@ def _check_version_by_hierarchy(coding_system, version): # will have a cached hierarchy, built with the original coding system release. # We compare this to a hierarchy built from the same codes, but with the new release. - return version.hierarchy == Hierarchy.from_codes(coding_system, version.codes) + return version.hierarchy == version.calculate_hierarchy(coding_system=coding_system) def _check_version_by_search(coding_system, version): diff --git a/coding_systems/base/tests/test_import_data_utils.py b/coding_systems/base/tests/test_import_data_utils.py index 1ea1e8e09..2fdd96d1e 100644 --- a/coding_systems/base/tests/test_import_data_utils.py +++ b/coding_systems/base/tests/test_import_data_utils.py @@ -9,7 +9,10 @@ from codelists.coding_systems import CODING_SYSTEMS from codelists.hierarchy import Hierarchy from codelists.models import Status -from coding_systems.base.import_data_utils import update_codelist_version_compatibility +from coding_systems.base.import_data_utils import ( + _check_version_by_hierarchy, + update_codelist_version_compatibility, +) from coding_systems.base.tests.dynamic_db_classes import DynamicDatabaseTestCase from coding_systems.bnf.models import Concept from coding_systems.conftest import mock_migrate_coding_system @@ -367,3 +370,20 @@ def test_update_codelist_version_compatibility_is_order_insensitive( update_codelist_version_compatibility("bnf", self.bnf_release.database_alias) assert self.bnf_review_version_with_search.compatible_releases.exists() + + +class TestCodelistVersionCompatibilityRoundTrips( + BaseCodingSystemDynamicDatabaseTestCase +): + db_aliases = [ + "bnf_import-data_20221101", + ] + + @pytest.mark.usefixtures("_get_bnf_release", "_get_bnf_review_version_with_search") + def test_codelist_version_compatibility_round_trips( + self, + ): + assert _check_version_by_hierarchy( + self.bnf_review_version_with_search.coding_system, + self.bnf_review_version_with_search, + )