Skip to content

Commit 7193de9

Browse files
authored
Merge branch 'main' into Sandbox-configuration
2 parents acdf5c0 + ac259e0 commit 7193de9

12 files changed

+90
-12
lines changed

.github/pull_request_template.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
## Changes Made
88
<!-- List the key changes made in this PR -->
99

10-
-
11-
-
12-
-
10+
-
11+
-
12+
-
1313

1414
## Testing
1515
<!-- Describe how these changes were tested -->
@@ -27,4 +27,3 @@
2727

2828
## Additional Notes
2929
<!-- Add any other context about the PR here -->
30-

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,4 @@ scrap/
164164
.DS_Store
165165
.vscode/
166166
.ruff_cache/
167+
.python-version

healthchain/data_generators/cdsdatagenerator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def generate(
7575
constraints: Optional[list] = None,
7676
free_text_path: Optional[str] = None,
7777
column_name: Optional[str] = None,
78+
random_seed: Optional[int] = None,
7879
) -> BaseModel:
7980
"""
8081
Generates CDS data based on the current workflow, constraints, and optional free text data.
@@ -83,6 +84,7 @@ def generate(
8384
constraints (Optional[list]): A list of constraints to apply to the data generation.
8485
free_text_path (Optional[str]): The path to a CSV file containing free text data.
8586
column_name (Optional[str]): The column name in the CSV file to use for free text data.
87+
random_seed (Optional[int]): The random seed to use for reproducible data generation.
8688
8789
Returns:
8890
BaseModel: The generated CDS FHIR data.
@@ -95,7 +97,9 @@ def generate(
9597
for resource in self.mappings[self.workflow]:
9698
generator_name = resource["generator"]
9799
generator = self.fetch_generator(generator_name)
98-
result = generator.generate(constraints=constraints)
100+
result = generator.generate(
101+
constraints=constraints, random_seed=random_seed
102+
)
99103

100104
results.append(BundleEntry(resource=result))
101105

healthchain/data_generators/conditiongenerators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ def generate(
147147
subject_reference: Optional[str] = None,
148148
encounter_reference: Optional[str] = None,
149149
constraints: Optional[list] = None,
150+
random_seed: Optional[int] = None,
150151
):
152+
Faker.seed(random_seed)
151153
subject_reference = subject_reference or "Patient/123"
152154
encounter_reference = encounter_reference or "Encounter/123"
153155
code = generator_registry.get("SnomedCodeGenerator").generate(

healthchain/data_generators/encountergenerators.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,16 @@ class EncounterGenerator(BaseGenerator):
143143
A generator class for creating FHIR Encounter resources.
144144
145145
Methods:
146-
generate(constraints: Optional[list] = None) -> Encounter:
147-
Generates a FHIR Encounter resource with optional constraints.
146+
generate(constraints: Optional[list] = None, random_seed: Optional[int] = None) -> Encounter:
147+
Generates a FHIR Encounter resource with optional constraints and random_seed.
148148
"""
149149

150150
@staticmethod
151151
def generate(
152152
constraints: Optional[list] = None,
153+
random_seed: Optional[int] = None,
153154
) -> Encounter:
155+
Faker.seed(random_seed)
154156
patient_reference = "Patient/123"
155157
return Encounter(
156158
resourceType="Encounter",

healthchain/data_generators/medicationrequestgenerators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ class MedicationRequestGenerator(BaseGenerator):
4646
@staticmethod
4747
def generate(
4848
constraints: Optional[list] = None,
49+
random_seed: Optional[int] = None,
4950
):
51+
Faker.seed(random_seed)
5052
subject_reference = "Patient/123"
5153
encounter_reference = "Encounter/123"
5254
contained_medication = Medication(

healthchain/data_generators/patientgenerators.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ def generate():
116116
@register_generator
117117
class PatientGenerator(BaseGenerator):
118118
@staticmethod
119-
def generate(constraints: Optional[list] = None):
119+
def generate(
120+
constraints: Optional[list] = None,
121+
random_seed: Optional[int] = None,
122+
) -> Patient:
123+
Faker.seed(random_seed)
120124
return Patient(
121125
resourceType="Patient",
122126
id=generator_registry.get("IdGenerator").generate(),

healthchain/data_generators/proceduregenerators.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ def generate(
4242
subject_reference: Optional[str] = None,
4343
encounter_reference: Optional[str] = None,
4444
constraints: Optional[list] = None,
45+
random_seed: Optional[int] = None,
4546
):
47+
Faker.seed(random_seed)
4648
subject_reference = subject_reference or "Patient/123"
4749
encounter_reference = encounter_reference or "Encounter/123"
4850
code = generator_registry.get("ProcedureSnomedCodeGenerator").generate(

healthchain/models/data/concept.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from enum import Enum
2-
from pydantic import BaseModel, Field
2+
from pydantic import BaseModel, Field, field_validator
33
from typing import Optional, Dict, Union
44

55

@@ -21,6 +21,28 @@ class Quantity(DataType):
2121
value: Optional[Union[str, float]] = None
2222
unit: Optional[str] = None
2323

24+
@field_validator("value")
25+
@classmethod
26+
def validate_value(cls, value: Union[str, float]):
27+
if value is None:
28+
return None
29+
30+
if not isinstance(value, (str, float)):
31+
raise TypeError(
32+
f"Value CANNOT be a {type(value)} object. Must be float or string in float format."
33+
)
34+
35+
try:
36+
return float(value)
37+
38+
except ValueError:
39+
raise ValueError(f"Invalid value '{value}' . Must be a float Number.")
40+
41+
except OverflowError:
42+
raise OverflowError(
43+
"Invalid value . Value is too large resulting in overflow."
44+
)
45+
2446

2547
class Range(DataType):
2648
low: Optional[Quantity] = None

poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_cdaannotator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ def test_extract_medications(cda_annotator):
6868
assert medications[0].route.code_system_name == "NCI Thesaurus"
6969
assert medications[0].route.display_name == "Oral"
7070

71-
assert medications[0].frequency.period.value == ".5"
71+
assert medications[0].frequency.period.value == 0.5
7272
assert medications[0].frequency.period.unit == "d"
7373
assert medications[0].frequency.institution_specified
7474

7575
assert medications[0].duration.low is None
76-
assert medications[0].duration.high.value == "20221020"
76+
assert medications[0].duration.high.value == 20221020
7777

7878
assert medications[0].precondition == {
7979
"@typeCode": "PRCN",

tests/test_quantity_class.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import pytest
2+
from healthchain.models.data.concept import Quantity
3+
from pydantic import ValidationError
4+
5+
6+
# Valid Cases
7+
def test_valid():
8+
valid_floats = [1.0, 0.1, 4.5, 5.99999, 12455.321, 33, 1234, None]
9+
for num in valid_floats:
10+
q = Quantity(value=num, unit="mg")
11+
assert q.value == num
12+
13+
14+
def test_valid_string():
15+
valid_strings = ["100", "100.000001", ".1", "1.", ".123", "1234.", "123989"]
16+
for string in valid_strings:
17+
q = Quantity(value=string, unit="mg")
18+
assert q.value == float(string)
19+
20+
21+
# Invalid Cases
22+
def test_invalid_strings():
23+
invalid_strings = [
24+
"1.0.0",
25+
"1..123",
26+
"..123",
27+
"12..",
28+
"12a.56",
29+
"1e4.6",
30+
"12#.45",
31+
"12.12@3",
32+
"12@3",
33+
"abc",
34+
"None",
35+
"",
36+
]
37+
for string in invalid_strings:
38+
with pytest.raises(ValidationError) as exception_info:
39+
Quantity(value=string, unit="mg")
40+
assert "Invalid value" in str(exception_info.value)

0 commit comments

Comments
 (0)