Skip to content

Commit 30f3d56

Browse files
Addressing PR comments
1 parent fdea09a commit 30f3d56

17 files changed

+139
-87
lines changed

classes/analyser.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66
@dataclass
77
class Analyser:
88
"""
9-
Data class representing an analyser.
9+
This data class is used to store information about a specific analyser device.
10+
The attributes correspond to the columns in the analyser query results.
11+
Attributes:
12+
analyser_id (Optional[int]): The ID of the analyser.
13+
analyser_code (Optional[str]): The code of the analyser.
14+
hub_id (Optional[int]): The ID of the hub the analyser is connected to.
15+
analyser_type_id (Optional[int]): The type ID of the analyser.
16+
spoil_result_code (Optional[int]): The result code for spoilage.
17+
tech_fail_result_code (Optional[int]): The result code for technical failure.
18+
below_range_result_code (Optional[int]): The result code for below range.
19+
above_range_result_code (Optional[int]): The result code for above range.
1020
"""
1121

1222
analyser_id: Optional[int] = None
@@ -38,6 +48,7 @@ def from_dataframe_row(row: pd.Series) -> "Analyser":
3848
- analyser_code
3949
- hub_id
4050
- tk_analyser_type_id
51+
The other columns are obtained from the SQL query as so are not present in this method.
4152
4253
Returns:
4354
Analyser: The constructed Analyser object.

classes/analyser_result_code_type.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from typing import Optional, Dict
2+
from typing import Optional
33

44

55
class AnalyserResultCodeType(Enum):

classes/entities/kit_service_management_entity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def from_record(
5555
device_id=record.device_id,
5656
test_kit_type=(
5757
str(record.test_kit_type) if record.test_kit_type is not None else None
58-
),
58+
), # test_kit_type is converted to a string as this is what is required in the database
5959
test_kit_name=record.test_kit_name,
6060
test_kit_status=record.test_kit_status,
6161
logged_by_hub=record.logged_by_hub,

classes/episode_result_type.py

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from typing import Optional, Dict
1+
from enum import Enum
2+
from typing import Optional
23

34

4-
class EpisodeResultType:
5+
class EpisodeResultType(Enum):
56
NO_RESULT = (20311, "No Result")
67
NORMAL = (20312, "Normal (No Abnormalities Found)")
78
LOW_RISK_ADENOMA = (20314, "Low-risk Adenoma")
@@ -37,80 +38,38 @@ class EpisodeResultType:
3738
NULL = (0, "Null")
3839
NOT_NULL = (0, "Not Null")
3940

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-
]
41+
def __init__(self, valid_value_id: int, description: str):
42+
self._id = valid_value_id
43+
self._description = description
6444

65-
_descriptions: Dict[str, "EpisodeResultType"] = {}
66-
_lowercase_descriptions: Dict[str, "EpisodeResultType"] = {}
67-
_valid_value_ids: Dict[int, "EpisodeResultType"] = {}
45+
@property
46+
def id(self) -> int:
47+
"""Return the valid value ID."""
48+
return self._id
6849

69-
def __init__(self, valid_value_id: int, description: str):
70-
self.valid_value_id = valid_value_id
71-
self.description = description
50+
@property
51+
def description(self) -> str:
52+
"""Return the description."""
53+
return self._description
7254

7355
@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
56+
def by_id(cls, valid_value_id: int) -> Optional["EpisodeResultType"]:
57+
"""Find an EpisodeResultType by its ID (returns first match if duplicates)."""
58+
return next((m for m in cls if m.id == valid_value_id), None)
8359

8460
@classmethod
8561
def by_description(cls, description: str) -> Optional["EpisodeResultType"]:
86-
cls._init_types()
87-
return cls._descriptions.get(description)
62+
"""Find an EpisodeResultType by its description (case-sensitive)."""
63+
return next((m for m in cls if m.description == description), None)
8864

8965
@classmethod
9066
def by_description_case_insensitive(
9167
cls, description: str
9268
) -> 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
69+
"""Find an EpisodeResultType by its description (case-insensitive)."""
70+
description = description.lower()
71+
return next((m for m in cls if m.description.lower() == description), None)
11472

115-
def __repr__(self):
116-
return f"EpisodeResultType({self.valid_value_id}, '{self.description}')"
73+
def __str__(self) -> str:
74+
"""Return a string representation of the EpisodeResultType."""
75+
return f"{self.name} ({self.id}: {self.description})"

classes/kit_service_management_record.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def from_dataframe_row(row) -> "KitServiceManagementRecord":
6666
row (pd.Series): A row from a pandas DataFrame with columns matching the query.
6767
6868
Returns:
69-
KitServiceManagementRecord: The constructed object.
69+
KitServiceManagementRecord: A populated KitServiceManagementRecord object from the given DataFrame row.
7070
"""
7171

7272
def parse_decimal(value):

classes/repositories/analyser_repository.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,14 @@ def get_technical_fail_result_code(self, analyser_type_id: int) -> int:
6969
def get_result_code(
7070
self, analyser_type_id: int, result_code_type: "AnalyserResultCodeType"
7171
) -> int:
72-
""" """
72+
"""
73+
Gets the result code for the specified analyser type and result code type from the database.
74+
Args:
75+
analyser_type_id (int): The ID of the analyser type.
76+
result_code_type (AnalyserResultCodeType): The type of result code.
77+
Returns:
78+
int: The result code for the specified analyser type and result code type
79+
"""
7380
query = """
7481
SELECT
7582
tkte.tk_analyser_type_id,

classes/repositories/general_repository.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def run_database_transition(
1919
) -> None:
2020
"""
2121
Executes the PKG_EPISODE.p_set_episode_next_status stored procedure with the provided parameters.
22+
Args:
23+
database_transition_parameters (DatabaseTransitionParameters): The parameters for the database transition.
24+
Raises:
25+
oracledb.DatabaseError: If there is an error executing the database transition.
2226
"""
2327
logging.debug(
2428
f"Running database transition with transition_id = {database_transition_parameters.transition_id}"
@@ -47,6 +51,7 @@ def run_database_transition(
4751
conn.commit()
4852
logging.debug("Database transition executed successfully.")
4953
except Exception as e:
54+
logging.error("Database transition failed", exc_info=True)
5055
raise oracledb.DatabaseError(f"Error executing database transition: {e}")
5156
finally:
5257
self.oracle_db.disconnect_from_db(conn)

classes/repositories/kit_service_management_repository.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def _device_filter(self, device_id: Optional[str]) -> list[str]:
1919
Args:
2020
device_id (str): The device ID of the subject
2121
Returns:
22-
list[str]: A filter for device_id if it is provided, otherwise it returns an empty string
22+
list[str]: A filter for device_id if it is provided, otherwise it returns an empty list
2323
"""
2424
return ["AND kq.device_id = :device_id"] if device_id else []
2525

@@ -29,7 +29,7 @@ def _archived_filter(self, archived: Optional[bool]) -> list[str]:
2929
Args:
3030
archived (bool): Whether to include archived records
3131
Returns:
32-
list[str]: A filter for date_time_error_archived
32+
list[str]: An SQL filter clause to include or exclude archived records based on the archived flag.
3333
"""
3434
if archived is None:
3535
return []
@@ -42,7 +42,7 @@ def _hub_filter(self, issuing_hub_id: int, logged_hub_id: int) -> list[str]:
4242
issuing_hub_id (int): The ID of the issuing hub
4343
logged_hub_id (int): The ID of the logged hub
4444
Returns:
45-
list[str]: A filter for the hubs
45+
list[str]: An SQL filter clause for the hubs
4646
"""
4747
hub_conditions = []
4848
if logged_hub_id > -1:
@@ -60,7 +60,7 @@ def _status_filter(
6060
status (str): The status of the test kit
6161
if_error_has_id (bool): Whether to include records with an error ID
6262
Returns:
63-
list[str]: A filter for the statuses
63+
list[str]: An SQL filter clause for the statuses
6464
"""
6565
if not status:
6666
error_status = [

classes/repositories/user_repository.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def __init__(self):
1818
def general_query(self, role: "UserRoleType") -> pd.DataFrame:
1919
"""
2020
Gets the pio_id, org_id, role_id and org_code of a user
21-
2221
Args:
2322
role (UserRoleType): A UserRoleType object containing the necessary information to run the query
24-
2523
Returns:
2624
pd.DataFrame: A dataframe containing the pio_id, org_id, role_id and org_code of a user
25+
Raises:
26+
ValueError: If no user data is returned from the query / the dataframe is empty
2727
"""
2828
sql = """
2929
SELECT

classes/user.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,6 @@ def from_dataframe_row(self, row) -> "User":
147147
Returns:
148148
User: The constructed User object.
149149
"""
150-
from classes.organisation_complex import Organisation
151-
152150
organisation = (
153151
Organisation(new_id=row["org_id"], new_code=row["org_code"])
154152
if "org_id" in row and "org_code" in row

0 commit comments

Comments
 (0)