From b5410bb5c457de123e4bf2bdf455e09b8e94853b Mon Sep 17 00:00:00 2001 From: Sian Teesdale <43341988+sianteesdale@users.noreply.github.com> Date: Mon, 19 Oct 2026 10:42:44 +0100 Subject: [PATCH] Fix deleted entities bug for joint endpoints --- .../expectations/operations/dataset.py | 6 +- .../expectations/operations/test_dataset.py | 66 +++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/digital_land/expectations/operations/dataset.py b/digital_land/expectations/operations/dataset.py index 43e8e6dd..3ca15a7d 100644 --- a/digital_land/expectations/operations/dataset.py +++ b/digital_land/expectations/operations/dataset.py @@ -267,11 +267,11 @@ def count_deleted_entities( db_name, organisation_entity, active_only=False ) - # use resource list to get current entities - query = f"""select f.entity + # use resource list to get current entities, organisations sharing a resource + # (e.g. joint plans) each have their own entity for the same reference + query = f"""select distinct f.entity from fact_resource fe join fact f on fe.fact=f.fact join entity e on f.entity=e.entity where resource in ({','.join(f"'{x}'" for x in resource_list)}) - group by reference """ rows = conn.execute(query).fetchall() get_active_entities = [row[0] for row in rows] diff --git a/tests/integration/expectations/operations/test_dataset.py b/tests/integration/expectations/operations/test_dataset.py index 21a00387..2df41d95 100644 --- a/tests/integration/expectations/operations/test_dataset.py +++ b/tests/integration/expectations/operations/test_dataset.py @@ -404,6 +404,72 @@ def test_count_deleted_entities_org_isolation(dataset_path, mocker): assert "2001" not in details["entities"] +@pytest.mark.parametrize( + "organisation_entity, expected_deleted", + [(61, []), (304, ["3002"])], +) +def test_count_deleted_entities_shared_resource_with_same_references( + dataset_path, organisation_entity, expected_deleted +): + """ + Organisations sharing a resource (e.g. a joint plan published by BRO and SNO) + each have their own entity for the same reference. Every organisation's + entities in the shared resource must count as active, not just one per reference. + + Setup: org 61 and org 304 share res-shared, each with an entity for REF-A and + REF-B. Org 304 also has 3002, last seen in an ended resource, so is deleted. + """ + test_entity_data = pd.DataFrame.from_dict( + { + "entity": ["1001", "1002", "3001", "3003", "3002"], + "name": ["BRO A", "BRO B", "SNO A", "SNO B", "SNO old"], + "organisation_entity": [61, 61, 304, 304, 304], + "reference": ["REF-A", "REF-B", "REF-A", "REF-B", "REF-OLD"], + } + ) + + test_fact_resource_data = pd.DataFrame.from_dict( + { + "fact": ["fact-1", "fact-2", "fact-3", "fact-4", "fact-5"], + "resource": [ + "res-shared", + "res-shared", + "res-shared", + "res-shared", + "res-ended", + ], + "entry_number": ["1", "2", "1", "2", "1"], + } + ) + + test_fact_data = pd.DataFrame.from_dict( + { + "fact": ["fact-1", "fact-2", "fact-3", "fact-4", "fact-5"], + "entity": ["1001", "1002", "3001", "3003", "3002"], + "field": ["reference", "reference", "reference", "reference", "reference"], + "value": ["REF-A", "REF-B", "REF-A", "REF-B", "REF-OLD"], + } + ) + + with spatialite.connect(dataset_path) as conn: + test_entity_data.to_sql("entity", conn, if_exists="replace", index=False) + test_fact_resource_data.to_sql( + "fact_resource", conn, if_exists="replace", index=False + ) + test_fact_data.to_sql("fact", conn, if_exists="replace", index=False) + + passed, _, details = count_deleted_entities( + conn, + expected=0, + organisation_entity=organisation_entity, + resources_cache={61: ["res-shared"], 304: ["res-shared"]}, + all_resources_cache={61: ["res-shared"], 304: ["res-shared", "res-ended"]}, + ) + + assert details["entities"] == expected_deleted + assert passed == (expected_deleted == []) + + def test_count_deleted_entities_ignores_entities_supplied_by_another_organisation( dataset_path, mocker ):