Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP DSL #23

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pytest-cov = "==3.0.0"
freezegun = "==1.2.2"
pyyaml = "==6.0.1"
pre-commit = "~=2.21.0"
black = "*"
242 changes: 151 additions & 91 deletions Pipfile.lock

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions fhirpathpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from fhirpathpy.engine import do_eval
from fhirpathpy.engine.util import arraify, get_data, set_paths
from fhirpathpy.engine.nodes import FP_Type
from fhirpathpy.dsl_impl import DSL

__title__ = "fhirpathpy"
__version__ = "0.2.2"
Expand Down Expand Up @@ -63,6 +64,8 @@ def evaluate(resource, path, context={}, model=None):
int: Description of return value

"""
if isinstance(path, DSL):
path = str(path)
node = parse(path)
return apply_parsed_path(resource, node, context, model)

Expand All @@ -82,3 +85,6 @@ def compile(path, model=None):
For example, you could pass in the result of require("fhirpath/fhir-context/r4")
"""
return set_paths(apply_parsed_path, parsedPath=parse(path), model=model)


dsl = DSL()
68 changes: 68 additions & 0 deletions fhirpathpy/dsl_impl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class DSL:
path: str

def __init__(self, prefix=None):
self.path = prefix

def __getattr__(self, attr):
if self.path is None and attr == "empty":
return DSL("{}")
if self.path is None and attr == "env":
return DSL("%")
elif self.path == "%":
return DSL(f"{self.path}{attr}")
elif self.path is not None:
if attr == "not_":
return DSL(f"{self.path}.not")
return DSL(f"{self.path}.{attr}")
else:
return DSL(attr)

def __getitem__(self, attr):
return DSL(f"{self.path}[{attr}]")

def __eq__(self, other):
return DSL(f"{self.path} = {format_value(other)}")

def __ne__(self, other):
return DSL(f"{self.path} != {format_value(other)}")

def __lshift__(self, item):
return DSL(f"{self.path} contains {format_value(item)}")

def __or__(self, item):
return DSL(f"({self.path} or {format_value(item)})")

def __not__(self):
return DSL(f"{self.path}.not()")

Check warning on line 37 in fhirpathpy/dsl_impl.py

View check run for this annotation

Codecov / codecov/patch

fhirpathpy/dsl_impl.py#L37

Added line #L37 was not covered by tests

def __call__(self, *args, **kwargs):
if len(args) == 0 and len(kwargs) == 0:
return DSL(f"{self.path}()")
elif len(args) == 0 and len(kwargs) == 1:
arg = build_args(kwargs)
return DSL(f"{self.path}({arg})")
elif len(args) == 1 and len(kwargs) == 0:
arg = format_value(args[0])
return DSL(f"{self.path}({arg})")
else:
raise Exception(f"Wrong arguements args={args} kwargs={kwargs}")

Check warning on line 49 in fhirpathpy/dsl_impl.py

View check run for this annotation

Codecov / codecov/patch

fhirpathpy/dsl_impl.py#L49

Added line #L49 was not covered by tests

def __str__(self):
return self.path

def __add__(self, item):
return DSL(f"({self.path} + {format_value(item)})")

def __radd__(self, item):
return DSL(f"({format_value(item)} + {self.path})")


def format_value(value):
if isinstance(value, str):
return f"'{value}'"
return str(value)


def build_args(args):
return ",".join(f"{k}={format_value(v)}" for k, v in args.items())
44 changes: 44 additions & 0 deletions tests/test_dsl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from fhirpathpy import dsl


def dsl_test():
assert str(dsl.Patient.id) == "Patient.id"
assert (
str(dsl.Patient.name.where(use="usual").given.first())
== "Patient.name.where(use='usual').given.first()"
)
assert (
str(
dsl.env.Source.entry[0].resource.expansion.contains.where(code=dsl.env.Coding.code)
!= dsl.empty
)
== "%Source.entry[0].resource.expansion.contains.where(code=%Coding.code) != {}"
)

assert (
str(dsl.Location.where(dsl.physicalType.coding.code << "817").name)
== "Location.where(physicalType.coding.code contains '817').name"
)
assert (
str(
(
(
dsl.env.QuestionnaireResponse.repeat(dsl.item)
.where(linkId="test-type")
.answer.children()
.Coding.code
== "trainingTest"
)
| (
dsl.env.QuestionnaireResponse.repeat(dsl.item)
.where(linkId="sample-set")
.answer.count()
== 6
)
).not_()
)
== "(%QuestionnaireResponse.repeat(item).where(linkId='test-type').answer.children().Coding.code = 'trainingTest' or %QuestionnaireResponse.repeat(item).where(linkId='sample-set').answer.count() = 6).not()"
)

assert str(dsl.Patient.id + "foo") == "(Patient.id + 'foo')"
assert str("/Patient/" + dsl.Patient.id) == "('/Patient/' + Patient.id)"
15 changes: 14 additions & 1 deletion tests/test_evaluators.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from fhirpathpy import evaluate
from fhirpathpy.engine.invocations.constants import constants

from fhirpathpy import dsl


@pytest.mark.parametrize(
("resource", "path", "expected"),
Expand Down Expand Up @@ -212,7 +214,7 @@ def now_function_test():
old_now_value = evaluate({}, 'now()')
frozen_datetime.tick(1.0)
new_now_value = evaluate({}, 'now()')

assert old_now_value != new_now_value


Expand Down Expand Up @@ -246,3 +248,14 @@ def combining_functions_test(resource, path, expected):
)
def path_functions_test(resource, path, expected):
assert evaluate(resource, path) == expected


@pytest.mark.parametrize(
("resource", "path", "expected"),
[
({"a": "lorem ipsum"}, "a.contains('sum')", [True]),
({"a": "lorem ipsum"}, dsl.a.contains('sum'), [True]),
],
)
def convert_dsl_path_test(resource, path, expected):
assert evaluate(resource, path) == expected