Skip to content

Commit 0cfd2b5

Browse files
committed
added polyp and episode completion filters to builder
1 parent b3c85a6 commit 0cfd2b5

File tree

3 files changed

+186
-51
lines changed

3 files changed

+186
-51
lines changed

utils/oracle/mock_selection_builder.py

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self, criteria_key, criteria_value, criteria_comparator=">="):
2626
self.criteria_value = criteria_value
2727
self.criteria_comparator = criteria_comparator
2828
self.sql_where = []
29+
self.sql_from = []
2930

3031
# Don't delete this method; it is used to inspect the SQL fragments
3132
def dump_sql(self):
@@ -35,26 +36,35 @@ def dump_sql(self):
3536
# Replace this with the one you want to test,
3637
# then use utils/oracle/test_subject_criteria_dev.py to run your scenarios
3738

38-
def _add_criteria_has_referral_date(self) -> None:
39+
def _add_criteria_has_diagnostic_test_containing_polyp(self) -> None:
3940
"""
40-
Adds a filter for the presence or timing of referral_date in the latest episode.
41-
Accepts values: yes, no, past, more_than_28_days_ago, within_the_last_28_days.
41+
Adds logic to filter based on whether a diagnostic test has a recorded polyp.
42+
'Yes' joins polyp tables; 'No' checks for absence via NOT EXISTS.
4243
"""
4344
try:
4445
value = self.criteria_value.strip().lower()
4546

46-
clause_map = {
47-
"yes": "ep.referral_date IS NOT NULL",
48-
"no": "ep.referral_date IS NULL",
49-
"past": "ep.referral_date < trunc(sysdate)",
50-
"more_than_28_days_ago": "(ep.referral_date + 28) < trunc(sysdate)",
51-
"within_the_last_28_days": "(ep.referral_date + 28) > trunc(sysdate)",
52-
}
53-
54-
if value not in clause_map:
55-
raise ValueError(f"Unknown referral date condition: {value}")
56-
57-
self.sql_where.append(f"AND {clause_map[value]}")
47+
if value == "yes":
48+
self.sql_from.append(
49+
"INNER JOIN external_tests_t ext ON ep.subject_epis_id = ext.subject_epis_id\n"
50+
"INNER JOIN ds_colonoscopy_t dsc ON ext.ext_test_id = dsc.ext_test_id\n"
51+
"INNER JOIN ds_polyp_t dst ON ext.ext_test_id = dst.ext_test_id"
52+
)
53+
self.sql_where.append(
54+
"AND ext.void = 'N'\n"
55+
"AND dst.deleted_flag = 'N'"
56+
)
57+
elif value == "no":
58+
self.sql_where.append(
59+
"""AND NOT EXISTS (
60+
SELECT 'ext'
61+
FROM external_tests_t ext
62+
LEFT JOIN ds_polyp_t dst ON ext.ext_test_id = dst.ext_test_id
63+
WHERE ext.subject_epis_id = ep.subject_epis_id
64+
)"""
65+
)
66+
else:
67+
raise ValueError(f"Unknown value for diagnostic test containing polyp: {value}")
5868

5969
except Exception:
6070
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)

utils/oracle/subject_selection_query_builder.py

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,8 +1107,159 @@ def _add_criteria_has_referral_date(self) -> None:
11071107

11081108
except Exception:
11091109
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1110-
1111-
#TODO: Add methods below for other criteria keys as needed
1110+
1111+
# TODO: Add methods below for other criteria keys as needed
1112+
1113+
def _add_criteria_has_diagnosis_date(self) -> None:
1114+
"""
1115+
Adds a filter to check if the latest episode has a diagnosis_date set,
1116+
and whether it matches the subject's date of death if specified.
1117+
Accepts values: yes, no, yes_date_of_death
1118+
"""
1119+
try:
1120+
value = self.criteria_value.strip().lower()
1121+
1122+
if value == "yes":
1123+
self.sql_where.append("AND ep.diagnosis_date IS NOT NULL")
1124+
elif value == "no":
1125+
self.sql_where.append("AND ep.diagnosis_date IS NULL")
1126+
elif value == "yes_date_of_death":
1127+
self.sql_where.append("AND ep.diagnosis_date IS NOT NULL")
1128+
self.sql_where.append("AND ep.diagnosis_date = c.date_of_death")
1129+
else:
1130+
raise ValueError(f"Unknown condition for diagnosis date: {value}")
1131+
1132+
except Exception:
1133+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1134+
1135+
def _add_criteria_has_diagnostic_test(self, latest_episode_only: bool) -> None:
1136+
"""
1137+
Adds a filter checking if the subject has (or doesn't have) a diagnostic test.
1138+
Void tests are excluded. The `latest_episode_only` flag limits scope to the latest episode.
1139+
Accepts criteria value: "yes" or "no".
1140+
"""
1141+
try:
1142+
value = self.criteria_value.strip().lower()
1143+
1144+
if value == "no":
1145+
prefix = "AND NOT "
1146+
elif value == "yes":
1147+
prefix = "AND "
1148+
else:
1149+
raise ValueError(f"Invalid diagnostic test condition: {value}")
1150+
1151+
subquery = [
1152+
"EXISTS (",
1153+
" SELECT 1",
1154+
" FROM external_tests_t lesxt",
1155+
" WHERE lesxt.screening_subject_id = ss.screening_subject_id",
1156+
" AND lesxt.void = 'N'",
1157+
]
1158+
if latest_episode_only:
1159+
subquery.append(" AND lesxt.subject_epis_id = ep.subject_epis_id")
1160+
subquery.append(")")
1161+
1162+
self.sql_where.append(prefix + "\n".join(subquery))
1163+
1164+
except Exception:
1165+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1166+
1167+
def _add_criteria_diagnosis_date_reason(self) -> None:
1168+
"""
1169+
Adds a filter on ep.diagnosis_date_reason_id.
1170+
Supports symbolic matches (via ID) and special values: NULL, NOT_NULL.
1171+
"""
1172+
try:
1173+
value = self.criteria_value.strip().lower()
1174+
comparator = self.criteria_comparator
1175+
1176+
# Simulated DiagnosisDateReasonType
1177+
reason_map = {
1178+
"patient informed": 900,
1179+
"clinician notified": 901,
1180+
"screening outcome": 902,
1181+
"null": "NULL",
1182+
"not_null": "NOT NULL",
1183+
# Extend as needed
1184+
}
1185+
1186+
if value not in reason_map:
1187+
raise ValueError(f"Unknown diagnosis date reason: {value}")
1188+
1189+
resolved = reason_map[value]
1190+
if resolved in ("NULL", "NOT NULL"):
1191+
self.sql_where.append(f"AND ep.diagnosis_date_reason_id IS {resolved}")
1192+
else:
1193+
self.sql_where.append(
1194+
f"AND ep.diagnosis_date_reason_id {comparator} {resolved}"
1195+
)
1196+
1197+
except Exception:
1198+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1199+
1200+
def _add_criteria_latest_episode_completed_satisfactorily(self) -> None:
1201+
"""
1202+
Adds a filter to check whether the latest episode completed satisfactorily or not.
1203+
Checks for presence/absence of interruption events or disqualifying result codes.
1204+
"""
1205+
try:
1206+
value = self.criteria_value.strip().lower()
1207+
1208+
if value == "yes":
1209+
exists_prefix = "AND NOT EXISTS"
1210+
elif value == "no":
1211+
exists_prefix = "AND EXISTS"
1212+
else:
1213+
raise ValueError(f"Invalid completion flag: {value}")
1214+
1215+
self.sql_where.append(
1216+
f"""{exists_prefix} (
1217+
SELECT 'ev'
1218+
FROM ep_events_t ev
1219+
WHERE ev.subject_epis_id = ep.subject_epis_id
1220+
AND (
1221+
ev.event_status_id IN (11237, 20188)
1222+
OR ep.episode_result_id IN (605002, 605003, 605004, 605007)
1223+
)
1224+
)"""
1225+
)
1226+
1227+
except Exception:
1228+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
1229+
1230+
def _add_criteria_has_diagnostic_test_containing_polyp(self) -> None:
1231+
"""
1232+
Adds logic to filter based on whether a diagnostic test has a recorded polyp.
1233+
'Yes' joins polyp tables; 'No' checks for absence via NOT EXISTS.
1234+
"""
1235+
try:
1236+
value = self.criteria_value.strip().lower()
1237+
1238+
if value == "yes":
1239+
self.sql_from.append(
1240+
"INNER JOIN external_tests_t ext ON ep.subject_epis_id = ext.subject_epis_id\n"
1241+
"INNER JOIN ds_colonoscopy_t dsc ON ext.ext_test_id = dsc.ext_test_id\n"
1242+
"INNER JOIN ds_polyp_t dst ON ext.ext_test_id = dst.ext_test_id"
1243+
)
1244+
self.sql_where.append(
1245+
"AND ext.void = 'N'\n" "AND dst.deleted_flag = 'N'"
1246+
)
1247+
elif value == "no":
1248+
self.sql_where.append(
1249+
"""AND NOT EXISTS (
1250+
SELECT 'ext'
1251+
FROM external_tests_t ext
1252+
LEFT JOIN ds_polyp_t dst ON ext.ext_test_id = dst.ext_test_id
1253+
WHERE ext.subject_epis_id = ep.subject_epis_id
1254+
)"""
1255+
)
1256+
else:
1257+
raise ValueError(
1258+
f"Unknown value for diagnostic test containing polyp: {value}"
1259+
)
1260+
1261+
except Exception:
1262+
raise SelectionBuilderException(self.criteria_key_name, self.criteria_value)
11121263

11131264
def _add_criteria_subject_hub_code(self, user: "User") -> None:
11141265
hub_code = None

utils/oracle/test_subject_criteria_dev.py

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,44 +30,18 @@
3030
# === Example usage ===
3131
# Replace the examples below with the method you want to test
3232

33-
# === Test: LATEST_EPISODE_HAS_REFERRAL_DATE — yes ===
33+
# === Test: HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP — yes ===
3434
builder = MockSelectionBuilder(
35-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_REFERRAL_DATE, "yes"
35+
SubjectSelectionCriteriaKey.HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP, "yes"
3636
)
37-
builder._add_criteria_has_referral_date()
38-
print("=== LATEST_EPISODE_HAS_REFERRAL_DATE — yes ===")
37+
builder._add_criteria_has_diagnostic_test_containing_polyp()
38+
print("=== HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP — yes ===")
3939
print(builder.dump_sql(), end="\n\n")
4040

41-
# === Test: LATEST_EPISODE_HAS_REFERRAL_DATE — no ===
41+
# === Test: HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP — no ===
4242
builder = MockSelectionBuilder(
43-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_REFERRAL_DATE, "no"
43+
SubjectSelectionCriteriaKey.HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP, "no"
4444
)
45-
builder._add_criteria_has_referral_date()
46-
print("=== LATEST_EPISODE_HAS_REFERRAL_DATE — no ===")
47-
print(builder.dump_sql(), end="\n\n")
48-
49-
# === Test: LATEST_EPISODE_HAS_REFERRAL_DATE — past ===
50-
builder = MockSelectionBuilder(
51-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_REFERRAL_DATE, "past"
52-
)
53-
builder._add_criteria_has_referral_date()
54-
print("=== LATEST_EPISODE_HAS_REFERRAL_DATE — past ===")
55-
print(builder.dump_sql(), end="\n\n")
56-
57-
# === Test: LATEST_EPISODE_HAS_REFERRAL_DATE — more_than_28_days_ago ===
58-
builder = MockSelectionBuilder(
59-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_REFERRAL_DATE,
60-
"more_than_28_days_ago",
61-
)
62-
builder._add_criteria_has_referral_date()
63-
print("=== LATEST_EPISODE_HAS_REFERRAL_DATE — more_than_28_days_ago ===")
64-
print(builder.dump_sql(), end="\n\n")
65-
66-
# === Test: LATEST_EPISODE_HAS_REFERRAL_DATE — within_the_last_28_days ===
67-
builder = MockSelectionBuilder(
68-
SubjectSelectionCriteriaKey.LATEST_EPISODE_HAS_REFERRAL_DATE,
69-
"within_the_last_28_days",
70-
)
71-
builder._add_criteria_has_referral_date()
72-
print("=== LATEST_EPISODE_HAS_REFERRAL_DATE — within_the_last_28_days ===")
45+
builder._add_criteria_has_diagnostic_test_containing_polyp()
46+
print("=== HAS_DIAGNOSTIC_TEST_CONTAINING_POLYP — no ===")
7347
print(builder.dump_sql(), end="\n\n")

0 commit comments

Comments
 (0)