Skip to content

Commit

Permalink
Merge pull request #22 from beda-software/issue-21
Browse files Browse the repository at this point in the history
Issue 21
  • Loading branch information
ir4y authored Jul 5, 2023
2 parents 3805a4a + 528a43e commit e01496c
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 21 deletions.
33 changes: 20 additions & 13 deletions fhirpathpy/engine/evaluators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,26 +182,37 @@ def func(acc, res):
actualTypes = model["choiceTypePaths"][childPath]

toAdd = None
toAdd_ = None

if isinstance(actualTypes, list):
# Use actualTypes to find the field's value
for actualType in actualTypes:
field = key + actualType
if isinstance(res.data, (dict, list)) and field in res.data:
toAdd = res.data[field]
childPath = actualType
break
if isinstance(res.data, (dict, list)):
toAdd = res.data.get(field)
toAdd_ = res.data.get(f"_{field}")
if toAdd is not None or toAdd_ is not None:
childPath = actualType
break
else:
if isinstance(res.data, (dict, list)) and key in res.data:
toAdd = res.data[key]
if isinstance(res.data, (dict, list)):
toAdd = res.data.get(key)
toAdd_ = res.data.get(f"_{key}")
if key == 'extension':
childPath = 'Extension'

if util.is_some(toAdd):
if isinstance(toAdd, list):
mapped = [nodes.ResourceNode.create_node(x, childPath) for x in toAdd]
acc = acc + mapped
else:
acc.append(nodes.ResourceNode.create_node(toAdd, childPath))
return acc
if util.is_some(toAdd_):
if isinstance(toAdd_, list):
mapped = [nodes.ResourceNode.create_node(x, childPath) for x in toAdd_]
acc = acc + mapped
else:
acc.append(nodes.ResourceNode.create_node(toAdd_, childPath))
return acc

return func
Expand Down Expand Up @@ -265,9 +276,7 @@ def polarity_expression(ctx, parentData, node):
rtn = engine.do_eval(ctx, parentData, node["children"][0])

if len(rtn) != 1: # not yet in spec, but per Bryn Rhodes
raise Exception(
"Unary " + sign + " can only be applied to an individual number."
)
raise Exception("Unary " + sign + " can only be applied to an individual number.")

if not util.is_number(rtn[0]):
raise Exception("Unary " + sign + " can only be applied to a number.")
Expand Down Expand Up @@ -301,9 +310,7 @@ def polarity_expression(ctx, parentData, node):
# expressions
"PolarityExpression": polarity_expression,
"IndexerExpression": indexer_expression,
"MembershipExpression": alias_op_expression(
{"contains": "containsOp", "in": "inOp"}
),
"MembershipExpression": alias_op_expression({"contains": "containsOp", "in": "inOp"}),
"TermExpression": term_expression,
"UnionExpression": union_expression,
"InvocationExpression": invocation_expression,
Expand Down
5 changes: 3 additions & 2 deletions fhirpathpy/engine/invocations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"count": {"fn": existence.count_fn},
"repeat": {"fn": filtering.repeat_macro, "arity": {1: ["Expr"]}},
"where": {"fn": filtering.where_macro, "arity": {1: ["Expr"]}},
"extension": {"fn": filtering.extension, "arity": {1: ["String"]}},
"select": {"fn": filtering.select_macro, "arity": {1: ["Expr"]}},
"single": {"fn": filtering.single_fn},
"first": {"fn": filtering.first_fn},
Expand All @@ -42,8 +43,8 @@
"toInteger": {"fn": misc.to_integer},
"toDecimal": {"fn": misc.to_decimal},
"toString": {"fn": misc.to_string},
# toDateTime: {fn: misc.toDateTime},
# toTime: {fn: misc.toTime},
"toDateTime": {"fn": misc.to_date_time},
"toTime": {"fn": misc.to_time},
"indexOf": {"fn": strings.index_of, "arity": {1: ["String"]}, "nullable_input": True},
"substring": {
"fn": strings.substring,
Expand Down
6 changes: 4 additions & 2 deletions fhirpathpy/engine/invocations/equality.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ def datetime_equality(ctx, x, y):
datetime_x = x[0]
datetime_y = y[0]
if type(datetime_x) not in DATETIME_NODES_LIST:
datetime_x = nodes.FP_DateTime(datetime_x) or nodes.FP_Time(datetime_x)
v_x = util.get_data(datetime_x)
datetime_x = nodes.FP_DateTime(v_x) or nodes.FP_Time(v_x)
if type(datetime_y) not in DATETIME_NODES_LIST:
datetime_y = nodes.FP_DateTime(datetime_y) or nodes.FP_Time(datetime_y)
v_y = util.get_data(datetime_y)
datetime_y = nodes.FP_DateTime(v_y) or nodes.FP_Time(v_y)
return datetime_x.equals(datetime_y)


Expand Down
12 changes: 12 additions & 0 deletions fhirpathpy/engine/invocations/filtering.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import numbers
import fhirpathpy.engine.util as util
import fhirpathpy.engine.nodes as nodes

# Contains the FHIRPath Filtering and Projection functions.
# (Section 5.2 of the FHIRPath 1.0.0 specification).
Expand Down Expand Up @@ -115,3 +116,14 @@ def check_fhir_type(ctx, x, tp):

def of_type_fn(ctx, coll, tp):
return list(filter(lambda x: check_fhir_type(ctx, util.get_data(x), tp), coll))


def extension(ctx, data, url):
res = []
for d in data:
element = util.get_data(d)
if isinstance(element, dict):
exts = [e for e in element.get("extension", []) if e["url"] == url]
if len(exts) > 0:
res.append(nodes.ResourceNode.create_node(exts[0], "Extension"))
return res
4 changes: 2 additions & 2 deletions fhirpathpy/engine/invocations/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def to_date_time(ctx, coll):
dateTimeObject = nodes.FP_DateTime(value)

if dateTimeObject:
rtn[0] = dateTimeObject
rtn.append(dateTimeObject)

return rtn

Expand All @@ -113,6 +113,6 @@ def to_time(ctx, coll):
timeObject = nodes.FP_Time(value)

if timeObject:
rtn[0] = timeObject
rtn.append(timeObject)

return rtn
108 changes: 108 additions & 0 deletions tests/cases/extensions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
tests:
# https://www.hl7.org/fhir/fhirpath.html#types
- 'group: Extension and id for primitive types':

- desc: '** id for primitive type'
expression: Functions.attrtrue.id = 'someid'
result:
- true

- desc: '** expression with extension for primitive type 1'
inputfile: patient-example.json
expression: Patient.birthDate.extension.where(url = '').empty()
result:
- true

- desc: '** expression with extension for primitive type 2'
inputfile: patient-example.json
expression: >-
Patient.birthDate.extension
.where(url = 'http://hl7.org/fhir/StructureDefinition/patient-birthTime')
.valueDateTime.toDateTime() = @1974-12-25T14:35:45-05:00
result:
- true

- desc: '** expression with extension for primitive type 3'
inputfile: patient-example.json
model: r4
expression: >-
Patient.birthDate.extension
.where(url = 'http://hl7.org/fhir/StructureDefinition/patient-birthTime')
.value = @1974-12-25T14:35:45-05:00
result:
- true

# https://www.hl7.org/fhir/fhirpath.html#functions
- 'group: Additional functions':
- desc: 'extension(url : string) : collection'

# If the url is empty ({ }), the result is empty.
- desc: '** empty url'
inputfile: patient-example.json
expression: Patient.birthDate.extension('').empty()
result:
- true

# If the input collection is empty ({ }), the result is empty.
- desc: '** empty input collection'
inputfile: patient-example.json
expression: >-
Patient.birthDate1
.extension('http://hl7.org/fhir/StructureDefinition/patient-birthTime').empty()
result:
- true

- desc: '** expression with extension() for primitive type (without using FHIR model data)'
inputfile: patient-example.json
expression: >-
Patient.birthDate.extension('http://hl7.org/fhir/StructureDefinition/patient-birthTime')
.valueDateTime.toDateTime() = @1974-12-25T14:35:45-05:00
result:
- true

- desc: '** expression with extension() for primitive type (without using FHIR model data) when only extension is present'
inputfile: patient-example-2.json
expression: >-
Patient.communication.preferred.extension('test').exists()
result:
- true

- desc: '** expression with extension() for primitive type (using FHIR model data) when only extension is present'
inputfile: patient-example-2.json
model: r4
expression: >-
Patient.communication.preferred.extension('test').value.id
result:
- testing

- desc: '** expression with extension() for primitive type (using FHIR model data)'
inputfile: patient-example.json
model: r4
expression: >-
Patient.birthDate.extension('http://hl7.org/fhir/StructureDefinition/patient-birthTime')
.value = @1974-12-25T14:35:45-05:00
result:
- true

- desc: '** value of extension of extension (using FHIR model data)'
model: r4
expression: Functions.attrtrue.extension('url1').extension('url2').value = 'someuri'
result:
- true

- desc: '** id of extension of extension'
expression: Functions.attrtrue.extension('url1').extension('url2').id = 'someid2'
result:
- true

subject:
resourceType: Functions
attrtrue: true
_attrtrue:
id: someid
extension:
- url: url1
extension:
- url: url2
id: someid2
valueUri: someuri
1 change: 1 addition & 0 deletions tests/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def save_to_resources(resources, resource_filename):

save_to_resources(resources, "observation-example.json")
save_to_resources(resources, "patient-example.json")
save_to_resources(resources, "patient-example-2.json")
save_to_resources(resources, "quantity-example.json")
save_to_resources(resources, "questionnaire-example.json")
save_to_resources(resources, "valueset-example-expansion.json")
26 changes: 26 additions & 0 deletions tests/resources/patient-example-2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"resourceType": "Patient",
"communication": [
{
"language": {
"coding": [
{
"system": "urn:ietf:bcp:47",
"code": "nl",
"display": "Dutch"
}
]
},
"_preferred": {
"extension": [
{
"url": "test",
"_valueString": {
"id": "testing"
}
}
]
}
}
]
}
36 changes: 34 additions & 2 deletions tests/test_real.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,37 @@ def pickle_test():
assert reload(resource) == ["Patient/cdf"]




def extension_test():
patient = {
"identifier": [
{
"period": {
"start": "2020-01-01"
},
"system": "http://hl7.org/fhir/sid/us-mbi",
"type": {
"coding": [
{
"code": "MC",
"display": "Patient's Medicare number",
"extension": [
{
"url": "https://bluebutton.cms.gov/resources/codesystem/identifier-currency",
"valueCoding": {
"code": "current",
"display": "Current",
"system": "https://bluebutton.cms.gov/resources/codesystem/identifier-currency"
}
}
],
"system": "http://terminology.hl7.org/CodeSystem/v2-0203"
}
]
},
"value": "7SM0A00AA00"
}
],
"resourceType": "Patient"
}
result = evaluate(patient, "Patient.identifier.where(type.coding.extension('https://bluebutton.cms.gov/resources/codesystem/identifier-currency').valueCoding.code = 'current').where(system = 'http://hl7.org/fhir/sid/us-mbi').value")
assert result == ["7SM0A00AA00"]

0 comments on commit e01496c

Please sign in to comment.