@@ -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
0 commit comments