@@ -325,9 +325,8 @@ def _add_variable_selection_criteria(
325325 SubjectSelectionCriteriaKey .LATEST_EPISODE_RECALL_SURVEILLANCE_TYPE
326326 ):
327327 self ._add_criteria_latest_episode_recall_surveillance_type ()
328- # TODO: Continue working on the case statements below, copying the Java code
329328 # ------------------------------------------------------------------------
330- # 🔄 Event & Workflow State Criteria
329+ # 🎯 Event Code & Status Inclusion Criteria
331330 # ------------------------------------------------------------------------
332331 case SubjectSelectionCriteriaKey .LATEST_EVENT_STATUS :
333332 self ._add_criteria_event_status ("ep.latest_event_status_id" )
@@ -922,6 +921,195 @@ def _add_criteria_latest_episode_recall_surveillance_type(self) -> None:
922921 except Exception :
923922 raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
924923
924+ def _add_criteria_event_status (self , column_name : str ) -> None :
925+ """
926+ Filters based on the event status code found in the specified column.
927+
928+ Extracts a code from the criteria value (e.g. "ES01 - Invitation Received"),
929+ and injects its corresponding event_status_id into SQL.
930+ """
931+ try :
932+ # Extract the code (e.g. "ES01") from the first word
933+ code = self .criteria_value .strip ().split ()[0 ].upper ()
934+ comparator = self .criteria_comparator
935+
936+ # Simulated EventStatusType registry
937+ event_status_code_map = {
938+ "ES01" : 600 ,
939+ "ES02" : 601 ,
940+ "ES03" : 602 ,
941+ "ES99" : 699 ,
942+ # ...update with real mappings
943+ }
944+
945+ if code not in event_status_code_map :
946+ raise ValueError (f"Unknown event status code: { code } " )
947+
948+ event_status_id = event_status_code_map [code ]
949+ self .sql_where .append (f"AND { column_name } { comparator } { event_status_id } " )
950+
951+ except Exception :
952+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
953+
954+ def _add_criteria_event_code_in_episode (self , event_is_included : bool ) -> None :
955+ """
956+ Adds a filter checking whether the given event code appears in the latest episode's event list.
957+ Uses EXISTS or NOT EXISTS depending on the flag.
958+ """
959+ try :
960+ code = self .criteria_value .strip ().split ()[0 ].upper ()
961+
962+ # Simulated EventCodeType registry
963+ event_code_map = {
964+ "EV101" : 701 ,
965+ "EV102" : 702 ,
966+ "EV900" : 799 ,
967+ # ...extend with real mappings
968+ }
969+
970+ if code not in event_code_map :
971+ raise ValueError (f"Unknown event code: { code } " )
972+
973+ event_code_id = event_code_map [code ]
974+
975+ exists_clause = "EXISTS" if event_is_included else "NOT EXISTS"
976+
977+ self .sql_where .append (
978+ f"""AND { exists_clause } (
979+ SELECT 'evc'
980+ FROM ep_events_t evc
981+ WHERE evc.event_code_id = { event_code_id }
982+ AND evc.subject_epis_id = ep.subject_epis_id
983+ )"""
984+ )
985+
986+ except Exception :
987+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
988+
989+ def _add_criteria_event_status_in_episode (self , event_is_included : bool ) -> None :
990+ """
991+ Adds a filter that checks whether the specified event status is present
992+ in the latest episode. Uses EXISTS or NOT EXISTS depending on the flag.
993+ """
994+ try :
995+ code = self .criteria_value .strip ().split ()[0 ].upper ()
996+
997+ # Simulated EventStatusType code-to-ID map
998+ event_status_code_map = {
999+ "ES01" : 600 ,
1000+ "ES02" : 601 ,
1001+ "ES03" : 602 ,
1002+ "ES99" : 699 ,
1003+ # Extend with actual mappings
1004+ }
1005+
1006+ if code not in event_status_code_map :
1007+ raise ValueError (f"Unknown event status code: { code } " )
1008+
1009+ status_id = event_status_code_map [code ]
1010+ exists_clause = "EXISTS" if event_is_included else "NOT EXISTS"
1011+
1012+ self .sql_where .append (
1013+ f"""AND { exists_clause } (
1014+ SELECT 'ev'
1015+ FROM ep_events_t ev
1016+ WHERE ev.event_status_id = { status_id }
1017+ AND ev.subject_epis_id = ep.subject_epis_id
1018+ )"""
1019+ )
1020+
1021+ except Exception :
1022+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
1023+
1024+ def _add_criteria_latest_episode_kit_class (self ) -> None :
1025+ """
1026+ Filters based on the test kit class of the latest episode using a nested IN clause.
1027+ Resolves from symbolic class name (e.g. 'FIT') to test class ID.
1028+ """
1029+ try :
1030+ value = self .criteria_value .upper ()
1031+ comparator = self .criteria_comparator
1032+
1033+ # Simulated TestKitClass enum
1034+ test_kit_class_map = {
1035+ "GFOBT" : 800 ,
1036+ "FIT" : 801 ,
1037+ # Extend as needed
1038+ }
1039+
1040+ if value not in test_kit_class_map :
1041+ raise ValueError (f"Unknown test kit class: { value } " )
1042+
1043+ kit_class_id = test_kit_class_map [value ]
1044+
1045+ self .sql_where .append (
1046+ f"""AND ep.tk_type_id IN (
1047+ SELECT tkt.tk_type_id
1048+ FROM tk_type_t tkt
1049+ WHERE tkt.tk_test_class_id { comparator } { kit_class_id }
1050+ )"""
1051+ )
1052+
1053+ except Exception :
1054+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
1055+
1056+ def _add_criteria_has_significant_kit_result (self ) -> None :
1057+ """
1058+ Adds a filter to check if the latest episode has a significant kit result.
1059+ Significant values: NORMAL, ABNORMAL, WEAK_POSITIVE.
1060+ Accepts criteriaValue: "yes" or "no".
1061+ """
1062+ try :
1063+ value = self .criteria_value .strip ().lower ()
1064+
1065+ if value == "yes" :
1066+ exists_clause = "EXISTS"
1067+ elif value == "no" :
1068+ exists_clause = "NOT EXISTS"
1069+ else :
1070+ raise ValueError (
1071+ f"Unknown response for significant kit result: { value } "
1072+ )
1073+
1074+ self .sql_where .append (
1075+ f"""AND { exists_clause } (
1076+ SELECT 'tks'
1077+ FROM tk_items_t tks
1078+ WHERE tks.screening_subject_id = ss.screening_subject_id
1079+ AND tks.logged_subject_epis_id = ep.subject_epis_id
1080+ AND tks.test_results IN ('NORMAL', 'ABNORMAL', 'WEAK_POSITIVE')
1081+ )"""
1082+ )
1083+
1084+ except Exception :
1085+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
1086+
1087+ def _add_criteria_has_referral_date (self ) -> None :
1088+ """
1089+ Adds a filter for the presence or timing of referral_date in the latest episode.
1090+ Accepts values: yes, no, past, more_than_28_days_ago, within_the_last_28_days.
1091+ """
1092+ try :
1093+ value = self .criteria_value .strip ().lower ()
1094+
1095+ clause_map = {
1096+ "yes" : "ep.referral_date IS NOT NULL" ,
1097+ "no" : "ep.referral_date IS NULL" ,
1098+ "past" : "ep.referral_date < trunc(sysdate)" ,
1099+ "more_than_28_days_ago" : "(ep.referral_date + 28) < trunc(sysdate)" ,
1100+ "within_the_last_28_days" : "(ep.referral_date + 28) > trunc(sysdate)" ,
1101+ }
1102+
1103+ if value not in clause_map :
1104+ raise ValueError (f"Unknown referral date condition: { value } " )
1105+
1106+ self .sql_where .append (f"AND { clause_map [value ]} " )
1107+
1108+ except Exception :
1109+ raise SelectionBuilderException (self .criteria_key_name , self .criteria_value )
1110+
1111+ #TODO: Add methods below for other criteria keys as needed
1112+
9251113 def _add_criteria_subject_hub_code (self , user : "User" ) -> None :
9261114 hub_code = None
9271115 try :
0 commit comments