Skip to content

Commit 4e6031f

Browse files
committed
Added builder support for latest investigation dataset filter in latest episode selection
- Includes mapping class LatestEpisodeLatestInvestigationDataset - Supports colonoscopy, radiology, and incomplete dataset filters
1 parent 758c430 commit 4e6031f

File tree

4 files changed

+217
-53
lines changed

4 files changed

+217
-53
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
class LatestEpisodeLatestInvestigationDataset:
2+
"""
3+
Maps descriptive investigation filter criteria to internal constants.
4+
Used to drive investigation dataset filtering in latest episode.
5+
"""
6+
7+
NONE = "none"
8+
COLONOSCOPY_NEW = "colonoscopy_new"
9+
LIMITED_COLONOSCOPY_NEW = "limited_colonoscopy_new"
10+
FLEXIBLE_SIGMOIDOSCOPY_NEW = "flexible_sigmoidoscopy_new"
11+
CT_COLONOGRAPHY_NEW = "ct_colonography_new"
12+
ENDOSCOPY_INCOMPLETE = "endoscopy_incomplete"
13+
RADIOLOGY_INCOMPLETE = "radiology_incomplete"
14+
15+
_valid_values = {
16+
NONE,
17+
COLONOSCOPY_NEW,
18+
LIMITED_COLONOSCOPY_NEW,
19+
FLEXIBLE_SIGMOIDOSCOPY_NEW,
20+
CT_COLONOGRAPHY_NEW,
21+
ENDOSCOPY_INCOMPLETE,
22+
RADIOLOGY_INCOMPLETE,
23+
}
24+
25+
@classmethod
26+
def from_description(cls, description: str) -> str:
27+
key = description.strip().lower()
28+
if key not in cls._valid_values:
29+
raise ValueError(f"Unknown investigation dataset filter: '{description}'")
30+
return key

utils/oracle/mock_selection_builder.py

Lines changed: 75 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,30 @@
88

99

1010
# Add helper class stubs below
11-
class LatestEpisodeHasDataset:
12-
"""
13-
Maps dataset status descriptions to filter interpretations.
14-
"""
15-
16-
NO = "no"
17-
YES_INCOMPLETE = "yes_incomplete"
18-
YES_COMPLETE = "yes_complete"
19-
PAST = "past"
11+
class LatestEpisodeLatestInvestigationDataset:
12+
NONE = "none"
13+
COLONOSCOPY_NEW = "colonoscopy_new"
14+
LIMITED_COLONOSCOPY_NEW = "limited_colonoscopy_new"
15+
FLEXIBLE_SIGMOIDOSCOPY_NEW = "flexible_sigmoidoscopy_new"
16+
CT_COLONOGRAPHY_NEW = "ct_colonography_new"
17+
ENDOSCOPY_INCOMPLETE = "endoscopy_incomplete"
18+
RADIOLOGY_INCOMPLETE = "radiology_incomplete"
2019

2120
_mapping = {
22-
"no": NO,
23-
"yes_incomplete": YES_INCOMPLETE,
24-
"yes_complete": YES_COMPLETE,
25-
"past": PAST,
21+
"none": NONE,
22+
"colonoscopy_new": COLONOSCOPY_NEW,
23+
"limited_colonoscopy_new": LIMITED_COLONOSCOPY_NEW,
24+
"flexible_sigmoidoscopy_new": FLEXIBLE_SIGMOIDOSCOPY_NEW,
25+
"ct_colonography_new": CT_COLONOGRAPHY_NEW,
26+
"endoscopy_incomplete": ENDOSCOPY_INCOMPLETE,
27+
"radiology_incomplete": RADIOLOGY_INCOMPLETE,
2628
}
2729

2830
@classmethod
2931
def from_description(cls, description: str) -> str:
3032
key = description.strip().lower()
3133
if key not in cls._mapping:
32-
raise ValueError(f"Unknown dataset filter type: '{description}'")
34+
raise ValueError(f"Unknown investigation dataset filter: '{description}'")
3335
return cls._mapping[key]
3436

3537

@@ -96,49 +98,73 @@ def _dataset_source_for_criteria_key(self) -> dict:
9698
# Replace this with the one you want to test,
9799
# then use utils/oracle/test_subject_criteria_dev.py to run your scenarios
98100

99-
def _add_criteria_latest_episode_has_dataset(self) -> None:
101+
def _add_criteria_latest_episode_latest_investigation_dataset(self) -> None:
100102
"""
101-
Filters based on presence or completion status of a dataset in the latest episode.
103+
Filters subjects based on their latest investigation dataset in their latest episode.
104+
Supports colonoscopy and radiology variations.
102105
"""
103106
try:
104107
self._add_join_to_latest_episode()
108+
value = LatestEpisodeLatestInvestigationDataset.from_description(self.criteria_value)
105109

106-
dataset_info = self._dataset_source_for_criteria_key()
107-
dataset_table = dataset_info["table"]
108-
alias = dataset_info["alias"]
109-
110-
clause = "AND EXISTS ( "
111-
value = self.criteria_value.strip().lower()
112-
status = LatestEpisodeHasDataset.from_description(value)
113-
filter_clause = ""
114-
115-
if status == LatestEpisodeHasDataset.NO:
116-
clause = "AND NOT EXISTS ( "
117-
elif status == LatestEpisodeHasDataset.YES_INCOMPLETE:
118-
filter_clause = f"AND {alias}.dataset_completed_date IS NULL"
119-
elif status == LatestEpisodeHasDataset.YES_COMPLETE:
120-
filter_clause = f"AND {alias}.dataset_completed_date IS NOT NULL"
121-
elif status == LatestEpisodeHasDataset.PAST:
122-
filter_clause = (
123-
f"AND TRUNC({alias}.dataset_completed_date) < TRUNC(SYSDATE)"
110+
if value == "none":
111+
self.sql_where.append(
112+
"AND NOT EXISTS (SELECT 'dsc1' FROM v_ds_colonoscopy dsc1 "
113+
"WHERE dsc1.episode_id = ep.subject_epis_id "
114+
"AND dsc1.confirmed_type_id = 16002)"
124115
)
125-
else:
126-
raise SelectionBuilderException(
127-
self.criteria_key_name, self.criteria_value
116+
elif value == "colonoscopy_new":
117+
self.sql_where.append(
118+
"AND EXISTS (SELECT 'dsc2' FROM v_ds_colonoscopy dsc2 "
119+
"WHERE dsc2.episode_id = ep.subject_epis_id "
120+
"AND dsc2.confirmed_type_id = 16002 "
121+
"AND dsc2.deleted_flag = 'N' "
122+
"AND dsc2.dataset_new_flag = 'Y')"
128123
)
129-
130-
self.sql_where.append(
131-
"".join(
132-
[
133-
clause,
134-
f"SELECT 1 FROM {dataset_table} {alias} ",
135-
f"WHERE {alias}.episode_id = ep.subject_epis_id ",
136-
f"AND {alias}.deleted_flag = 'N' ",
137-
filter_clause,
138-
")",
139-
]
124+
elif value == "limited_colonoscopy_new":
125+
self.sql_where.append(
126+
"AND EXISTS (SELECT 'dsc3' FROM v_ds_colonoscopy dsc3 "
127+
"WHERE dsc3.episode_id = ep.subject_epis_id "
128+
"AND dsc3.confirmed_type_id = 17996 "
129+
"AND dsc3.deleted_flag = 'N' "
130+
"AND dsc3.dataset_new_flag = 'Y')"
140131
)
141-
)
132+
elif value == "flexible_sigmoidoscopy_new":
133+
self.sql_where.append(
134+
"AND EXISTS (SELECT 'dsc4' FROM v_ds_colonoscopy dsc4 "
135+
"WHERE dsc4.episode_id = ep.subject_epis_id "
136+
"AND dsc4.confirmed_type_id = 16004 "
137+
"AND dsc4.deleted_flag = 'N' "
138+
"AND dsc4.dataset_new_flag = 'Y')"
139+
)
140+
elif value == "ct_colonography_new":
141+
self.sql_where.append(
142+
"AND EXISTS (SELECT 'dsr1' FROM v_ds_radiology dsr1 "
143+
"WHERE dsr1.episode_id = ep.subject_epis_id "
144+
"AND dsr1.confirmed_type_id = 16087 "
145+
"AND dsr1.deleted_flag = 'N' "
146+
"AND dsr1.dataset_new_flag = 'Y')"
147+
)
148+
elif value == "endoscopy_incomplete":
149+
self.sql_where.append(
150+
"AND EXISTS (SELECT 'dsei' FROM v_ds_colonoscopy dsei "
151+
"WHERE dsei.episode_id = ep.subject_epis_id "
152+
"AND dsei.deleted_flag = 'N' "
153+
"AND dsei.dataset_completed_flag = 'N' "
154+
"AND dsei.dataset_new_flag = 'N' "
155+
"AND dsei.confirmed_test_date >= TO_DATE('01/01/2020','dd/mm/yyyy'))"
156+
)
157+
elif value == "radiology_incomplete":
158+
self.sql_where.append(
159+
"AND EXISTS (SELECT 'dsri' FROM v_ds_radiology dsri "
160+
"WHERE dsri.episode_id = ep.subject_epis_id "
161+
"AND dsri.deleted_flag = 'N' "
162+
"AND dsri.dataset_completed_flag = 'N' "
163+
"AND dsri.dataset_new_flag = 'N' "
164+
"AND dsri.confirmed_test_date >= TO_DATE('01/01/2020','dd/mm/yyyy'))"
165+
)
166+
else:
167+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
142168

143169
except Exception:
144170
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)

utils/oracle/subject_selection_query_builder.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
)
3737
from classes.intended_extent_type import IntendedExtentType
3838
from classes.latest_episode_has_dataset import LatestEpisodeHasDataset
39+
from classes.latest_episode_latest_investigation_dataset import (
40+
LatestEpisodeLatestInvestigationDataset,
41+
)
3942

4043

4144
class SubjectSelectionQueryBuilder:
@@ -1888,6 +1891,81 @@ def _dataset_source_for_criteria_key(self) -> dict:
18881891
return {"table": "ds_mdt_t", "alias": "mdt"}
18891892
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
18901893

1894+
def _add_criteria_latest_episode_latest_investigation_dataset(self) -> None:
1895+
"""
1896+
Filters subjects based on their latest investigation dataset in their latest episode.
1897+
Supports colonoscopy and radiology variations.
1898+
"""
1899+
try:
1900+
self._add_join_to_latest_episode()
1901+
value = LatestEpisodeLatestInvestigationDataset.from_description(
1902+
self.criteria_value
1903+
)
1904+
1905+
if value == "none":
1906+
self.sql_where.append(
1907+
"AND NOT EXISTS (SELECT 'dsc1' FROM v_ds_colonoscopy dsc1 "
1908+
"WHERE dsc1.episode_id = ep.subject_epis_id "
1909+
"AND dsc1.confirmed_type_id = 16002)"
1910+
)
1911+
elif value == "colonoscopy_new":
1912+
self.sql_where.append(
1913+
"AND EXISTS (SELECT 'dsc2' FROM v_ds_colonoscopy dsc2 "
1914+
"WHERE dsc2.episode_id = ep.subject_epis_id "
1915+
"AND dsc2.confirmed_type_id = 16002 "
1916+
"AND dsc2.deleted_flag = 'N' "
1917+
"AND dsc2.dataset_new_flag = 'Y')"
1918+
)
1919+
elif value == "limited_colonoscopy_new":
1920+
self.sql_where.append(
1921+
"AND EXISTS (SELECT 'dsc3' FROM v_ds_colonoscopy dsc3 "
1922+
"WHERE dsc3.episode_id = ep.subject_epis_id "
1923+
"AND dsc3.confirmed_type_id = 17996 "
1924+
"AND dsc3.deleted_flag = 'N' "
1925+
"AND dsc3.dataset_new_flag = 'Y')"
1926+
)
1927+
elif value == "flexible_sigmoidoscopy_new":
1928+
self.sql_where.append(
1929+
"AND EXISTS (SELECT 'dsc4' FROM v_ds_colonoscopy dsc4 "
1930+
"WHERE dsc4.episode_id = ep.subject_epis_id "
1931+
"AND dsc4.confirmed_type_id = 16004 "
1932+
"AND dsc4.deleted_flag = 'N' "
1933+
"AND dsc4.dataset_new_flag = 'Y')"
1934+
)
1935+
elif value == "ct_colonography_new":
1936+
self.sql_where.append(
1937+
"AND EXISTS (SELECT 'dsr1' FROM v_ds_radiology dsr1 "
1938+
"WHERE dsr1.episode_id = ep.subject_epis_id "
1939+
"AND dsr1.confirmed_type_id = 16087 "
1940+
"AND dsr1.deleted_flag = 'N' "
1941+
"AND dsr1.dataset_new_flag = 'Y')"
1942+
)
1943+
elif value == "endoscopy_incomplete":
1944+
self.sql_where.append(
1945+
"AND EXISTS (SELECT 'dsei' FROM v_ds_colonoscopy dsei "
1946+
"WHERE dsei.episode_id = ep.subject_epis_id "
1947+
"AND dsei.deleted_flag = 'N' "
1948+
"AND dsei.dataset_completed_flag = 'N' "
1949+
"AND dsei.dataset_new_flag = 'N' "
1950+
"AND dsei.confirmed_test_date >= TO_DATE('01/01/2020','dd/mm/yyyy'))"
1951+
)
1952+
elif value == "radiology_incomplete":
1953+
self.sql_where.append(
1954+
"AND EXISTS (SELECT 'dsri' FROM v_ds_radiology dsri "
1955+
"WHERE dsri.episode_id = ep.subject_epis_id "
1956+
"AND dsri.deleted_flag = 'N' "
1957+
"AND dsri.dataset_completed_flag = 'N' "
1958+
"AND dsri.dataset_new_flag = 'N' "
1959+
"AND dsri.confirmed_test_date >= TO_DATE('01/01/2020','dd/mm/yyyy'))"
1960+
)
1961+
else:
1962+
raise SelectionBuilderException(
1963+
self.criteria_key_name, self.criteria_value
1964+
)
1965+
1966+
except Exception:
1967+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1968+
18911969
# ------------------------------------------------------------------------
18921970
# 🧬 CADS Clinical Dataset Filters
18931971
# ------------------------------------------------------------------------

utils/oracle/test_subject_criteria_dev.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,40 @@ def make_builder(key, value, index=0):
3737
return b
3838

3939

40-
# === Test: LATEST_EPISODE_HAS_CANCER_AUDIT_DATASET (yes_complete) ===
40+
# === Test: LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (none) ===
4141
b = make_builder(
42-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_CANCER_AUDIT_DATASET, "yes_complete"
42+
SubjectSelectionCriteriaKey.LATEST_EPISODE_LATEST_INVESTIGATION_DATASET, "none"
4343
)
44-
b._add_criteria_latest_episode_has_dataset()
45-
print("=== LATEST_EPISODE_HAS_CANCER_AUDIT_DATASET (yes_complete) ===")
44+
b._add_criteria_latest_episode_latest_investigation_dataset()
45+
print("=== LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (none) ===")
46+
print(b.dump_sql(), end="\n\n")
47+
48+
# === Test: LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (colonoscopy_new) ===
49+
b = make_builder(
50+
SubjectSelectionCriteriaKey.LATEST_EPISODE_LATEST_INVESTIGATION_DATASET,
51+
"colonoscopy_new",
52+
index=1,
53+
)
54+
b._add_criteria_latest_episode_latest_investigation_dataset()
55+
print("=== LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (colonoscopy_new) ===")
56+
print(b.dump_sql(), end="\n\n")
57+
58+
# === Test: LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (ct_colonography_new) ===
59+
b = make_builder(
60+
SubjectSelectionCriteriaKey.LATEST_EPISODE_LATEST_INVESTIGATION_DATASET,
61+
"ct_colonography_new",
62+
index=2,
63+
)
64+
b._add_criteria_latest_episode_latest_investigation_dataset()
65+
print("=== LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (ct_colonography_new) ===")
66+
print(b.dump_sql(), end="\n\n")
67+
68+
# === Test: LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (endoscopy_incomplete) ===
69+
b = make_builder(
70+
SubjectSelectionCriteriaKey.LATEST_EPISODE_LATEST_INVESTIGATION_DATASET,
71+
"endoscopy_incomplete",
72+
index=3,
73+
)
74+
b._add_criteria_latest_episode_latest_investigation_dataset()
75+
print("=== LATEST_EPISODE_LATEST_INVESTIGATION_DATASET (endoscopy_incomplete) ===")
4676
print(b.dump_sql(), end="\n\n")

0 commit comments

Comments
 (0)