|
| 1 | +from typing import Optional, Dict |
| 2 | + |
| 3 | + |
1 | 4 | class EpisodeResultType: |
2 | | - """ |
3 | | - Utility class for mapping episode result type descriptions to logical flags or valid value IDs. |
4 | | -
|
5 | | - This class provides: |
6 | | - - Logical flags for "null", "not_null", and "any_surveillance_non_participation". |
7 | | - - A mapping from descriptive result labels (e.g., "normal", "abnormal", "surveillance offered") to internal valid value IDs. |
8 | | - - A method to convert descriptions to flags or IDs. |
9 | | -
|
10 | | - Methods: |
11 | | - from_description(description: str) -> str | int: |
12 | | - Returns the logical flag or the valid value ID for a given description. |
13 | | - Raises ValueError if the description is not recognized. |
14 | | - """ |
15 | | - |
16 | | - NULL = "null" |
17 | | - NOT_NULL = "not_null" |
18 | | - ANY_SURVEILLANCE_NON_PARTICIPATION = "any_surveillance_non_participation" |
19 | | - |
20 | | - _label_to_id = { |
21 | | - "normal": 9501, |
22 | | - "abnormal": 9502, |
23 | | - "surveillance offered": 9503, |
24 | | - # Add real mappings as needed |
25 | | - } |
| 5 | + NO_RESULT = (20311, "No Result") |
| 6 | + NORMAL = (20312, "Normal (No Abnormalities Found)") |
| 7 | + LOW_RISK_ADENOMA = (20314, "Low-risk Adenoma") |
| 8 | + INTERMEDIATE_RISK_ADENOMA = (20315, "Intermediate-risk Adenoma") |
| 9 | + HIGH_RISK_ADENOMA = (20316, "High-risk Adenoma") |
| 10 | + CANCER_DETECTED = (20317, "Cancer Detected") |
| 11 | + ABNORMAL = (20313, "Abnormal") |
| 12 | + CANCER_NOT_CONFIRMED = (305001, "Cancer not confirmed") |
| 13 | + HIGH_RISK_FINDINGS = (305606, "High-risk findings") |
| 14 | + LNPCP = (305607, "LNPCP") |
| 15 | + BOWEL_SCOPE_NON_PARTICIPATION = (605002, "Bowel scope non-participation") |
| 16 | + FOBT_INADEQUATE_PARTICIPATION = (605003, "FOBt inadequate participation") |
| 17 | + DEFINITIVE_NORMAL_FOBT_OUTCOME = (605005, "Definitive normal FOBt outcome") |
| 18 | + DEFINITIVE_ABNORMAL_FOBT_OUTCOME = (605006, "Definitive abnormal FOBt outcome") |
| 19 | + HIGH_RISK_FINDINGS_SURVEILLANCE_NON_PARTICIPATION = ( |
| 20 | + 305619, |
| 21 | + "High-risk findings Surveillance non-participation", |
| 22 | + ) |
| 23 | + LNPCP_SURVEILLANCE_NON_PARTICIPATION = ( |
| 24 | + 305618, |
| 25 | + "LNPCP Surveillance non-participation", |
| 26 | + ) |
| 27 | + HIGH_RISK_SURVEILLANCE_NON_PARTICIPATION = ( |
| 28 | + 605004, |
| 29 | + "High-risk Surveillance non-participation", |
| 30 | + ) |
| 31 | + INTERMEDIATE_RISK_SURVEILLANCE_NON_PARTICIPATION = ( |
| 32 | + 605007, |
| 33 | + "Intermediate-risk Surveillance non-participation", |
| 34 | + ) |
| 35 | + LYNCH_NON_PARTICIPATION = (305688, "Lynch non-participation") |
| 36 | + ANY_SURVEILLANCE_NON_PARTICIPATION = (0, "(Any) Surveillance non-participation") |
| 37 | + NULL = (0, "Null") |
| 38 | + NOT_NULL = (0, "Not Null") |
| 39 | + |
| 40 | + _all_types = [ |
| 41 | + NO_RESULT, |
| 42 | + NORMAL, |
| 43 | + LOW_RISK_ADENOMA, |
| 44 | + INTERMEDIATE_RISK_ADENOMA, |
| 45 | + HIGH_RISK_ADENOMA, |
| 46 | + CANCER_DETECTED, |
| 47 | + ABNORMAL, |
| 48 | + CANCER_NOT_CONFIRMED, |
| 49 | + HIGH_RISK_FINDINGS, |
| 50 | + LNPCP, |
| 51 | + BOWEL_SCOPE_NON_PARTICIPATION, |
| 52 | + FOBT_INADEQUATE_PARTICIPATION, |
| 53 | + DEFINITIVE_NORMAL_FOBT_OUTCOME, |
| 54 | + DEFINITIVE_ABNORMAL_FOBT_OUTCOME, |
| 55 | + HIGH_RISK_FINDINGS_SURVEILLANCE_NON_PARTICIPATION, |
| 56 | + LNPCP_SURVEILLANCE_NON_PARTICIPATION, |
| 57 | + HIGH_RISK_SURVEILLANCE_NON_PARTICIPATION, |
| 58 | + INTERMEDIATE_RISK_SURVEILLANCE_NON_PARTICIPATION, |
| 59 | + LYNCH_NON_PARTICIPATION, |
| 60 | + ANY_SURVEILLANCE_NON_PARTICIPATION, |
| 61 | + NULL, |
| 62 | + NOT_NULL, |
| 63 | + ] |
| 64 | + |
| 65 | + _descriptions: Dict[str, "EpisodeResultType"] = {} |
| 66 | + _lowercase_descriptions: Dict[str, "EpisodeResultType"] = {} |
| 67 | + _valid_value_ids: Dict[int, "EpisodeResultType"] = {} |
| 68 | + |
| 69 | + def __init__(self, valid_value_id: int, description: str): |
| 70 | + self.valid_value_id = valid_value_id |
| 71 | + self.description = description |
| 72 | + |
| 73 | + @classmethod |
| 74 | + def _init_types(cls): |
| 75 | + if not cls._descriptions: |
| 76 | + for valid_value_id, description in cls._all_types: |
| 77 | + instance = cls(valid_value_id, description) |
| 78 | + cls._descriptions[description] = instance |
| 79 | + cls._lowercase_descriptions[description.lower()] = instance |
| 80 | + # Only map the first occurrence of each valid_value_id |
| 81 | + if valid_value_id not in cls._valid_value_ids: |
| 82 | + cls._valid_value_ids[valid_value_id] = instance |
26 | 83 |
|
27 | 84 | @classmethod |
28 | | - def from_description(cls, description: str): |
29 | | - """ |
30 | | - Returns the logical flag or the valid value ID for a given description. |
31 | | -
|
32 | | - Args: |
33 | | - description (str): The episode result type description. |
34 | | -
|
35 | | - Returns: |
36 | | - str | int: The logical flag or the valid value ID. |
37 | | -
|
38 | | - Raises: |
39 | | - ValueError: If the description is not recognized. |
40 | | - """ |
41 | | - key = description.strip().lower() |
42 | | - if key in {cls.NULL, cls.NOT_NULL, cls.ANY_SURVEILLANCE_NON_PARTICIPATION}: |
43 | | - return key |
44 | | - if key in cls._label_to_id: |
45 | | - return cls._label_to_id[key] |
46 | | - raise ValueError(f"Unknown episode result type: '{description}'") |
| 85 | + def by_description(cls, description: str) -> Optional["EpisodeResultType"]: |
| 86 | + cls._init_types() |
| 87 | + return cls._descriptions.get(description) |
| 88 | + |
| 89 | + @classmethod |
| 90 | + def by_description_case_insensitive( |
| 91 | + cls, description: str |
| 92 | + ) -> Optional["EpisodeResultType"]: |
| 93 | + cls._init_types() |
| 94 | + return cls._lowercase_descriptions.get(description.lower()) |
| 95 | + |
| 96 | + @classmethod |
| 97 | + def by_valid_value_id(cls, valid_value_id: int) -> Optional["EpisodeResultType"]: |
| 98 | + cls._init_types() |
| 99 | + return cls._valid_value_ids.get(valid_value_id) |
| 100 | + |
| 101 | + def get_id(self) -> int: |
| 102 | + return self.valid_value_id |
| 103 | + |
| 104 | + def get_description(self) -> str: |
| 105 | + return self.description |
| 106 | + |
| 107 | + def __eq__(self, other): |
| 108 | + if isinstance(other, EpisodeResultType): |
| 109 | + return ( |
| 110 | + self.valid_value_id == other.valid_value_id |
| 111 | + and self.description == other.description |
| 112 | + ) |
| 113 | + return False |
| 114 | + |
| 115 | + def __repr__(self): |
| 116 | + return f"EpisodeResultType({self.valid_value_id}, '{self.description}')" |
0 commit comments