Skip to content
Merged
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
16 changes: 12 additions & 4 deletions .github/workflows/github-actions.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
name: github-actions
on: [push, pull_request]
on:
push:
branches:
- "*"
tags-ignore:
- "v*.*.*"
env:
BUILD_IMAGE: bedasoftware/fhirpath-extract:main
jobs:
Build:
build-and-test-fpml-server-ts:
runs-on: ubuntu-latest
steps:
- name: Copy repository
Expand All @@ -29,8 +34,8 @@ jobs:
run: yarn test:e2e
working-directory: ts/server

Publish:
needs: Build
publish-fpml-server-ts-to-dockerhub:
needs: build-and-test-fpml-server-ts
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
Expand All @@ -48,3 +53,6 @@ jobs:
docker buildx build --platform linux/arm64,linux/amd64
--push --tag ${{ env.BUILD_IMAGE }} .
working-directory: ts/server

test-fpml-py:
uses: ./.github/workflows/test-python.yml
26 changes: 26 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: publish
on:
push:
tags:
- "v*.*.*"
jobs:
test-fpml-py:
uses: ./.github/workflows/test-python.yml

publish-fpml-py-to-pypi:
needs: test-fpml-py
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
- name: Build a binary wheel and a source tarball
run: poetry --project=./python build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1
32 changes: 32 additions & 0 deletions .github/workflows/test-python.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: test-python
on: [workflow_call]
jobs:
test-fpml-py:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- python-version: 3.9
- python-version: "3.10"
- python-version: "3.11"
- python-version: "3.12"
- python-version: "3.13"
env:
PYTHON: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v3
- name: Python ${{ matrix.python-version }} sample
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install poetry
poetry --project=./python install
- name: Run lint
run: poetry --project=./python run ruff check ./python
- name: Run typecheck
run: poetry --project=./python run mypy ./python
- name: Run tests
run: poetry --project=./python run pytest --cov
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ coverage
htmlcov
.vscode
.history
.coverage
python/dist
54 changes: 49 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -547,16 +547,18 @@ that will be transformed into:

## Examples

See real-life examples of mappers for [FHIR](https://github.com/beda-software/FHIRPathMappingLanguage/blob/main/ts/server/src/utils/__data__/complex-example.fhir.yaml) and [Aidbox](https://github.com/beda-software/FHIRPathMappingLanguage/blob/main/ts/server/src/utils/__data__/complex-example.aidbox.yaml)
See real-life examples of mappers for [FHIR](https://github.com/beda-software/FHIRPathMappingLanguage/blob/main/tests/__data__/complex-example.fhir.yaml) and [Aidbox](https://github.com/beda-software/FHIRPathMappingLanguage/blob/main/tests/__data__/complex-example.aidbox.yaml)

and other usage in [unit tests](https://github.com/beda-software/FHIRPathMappingLanguage/tree/main/ts/server/src/utils).

## Reference implementation

TypeScript implementation that supports all the specification is already available [in this repository](https://github.com/beda-software/FHIRPathMappingLanguage/tree/main/server).
### TypeScript

TypeScript implementation that supports all the specification is already available [in this repository](https://github.com/beda-software/FHIRPathMappingLanguage/tree/main/ts/server).
Also, it is packed into a [docker image](https://hub.docker.com/r/bedasoftware/fhirpath-extract) to use as a microservice.

### Usage
#### Usage

POST /r4/parse-template

Expand All @@ -577,7 +579,7 @@ POST /r4/parse-template
}
```

### Strict mode
#### Strict mode

FHIRPath provides a way of accessing the `resource` variables without the percent sign. It potentially leads to the issues made by typos in the variable names.

Expand All @@ -603,4 +605,46 @@ POST /r4/parse-template?strict=true
"status": "completed"
}
}
```
```

### Python

Python implementation that supports all the specification is already available [in this repository](https://github.com/beda-software/FHIRPathMappingLanguage/tree/main/python).
Also, it's available as a PyPI package under the name `fpml` and can be installed using
```
pip install fpml
```

#### Usage

```python
from fpml import resolve_template


resource = {
"resourceType": "QuestionnaireResponse",
"status": "completed",
"item": [
{
"linkId": "name",
"answer": [
{
"valueString": "Name"
}
]
}
]
}

template = {
"resourceType": "Patient",
"name": "{{ item.where(linkId='name').answer.valueString }}"
}

context = {}

result = resolve_template(resource, template, context)

print(result)
# {'resourceType': 'Patient', 'name': 'Name'}
```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydian import DROP, Mapper, get
from pydian import get


def map(qr):
Expand Down
28 changes: 28 additions & 0 deletions examples/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[tool.black]
line-length = 100
target-version = ['py311']
exclude = '''
(
/(
| \.git
| \.pytest_cache
| htmlcov
| locales
| resources
| requirements
| embed
)/
)
'''

[tool.isort]
multi_line_output = 3
include_trailing_comma = true
use_parentheses = true
line_length = 100
default_section = "THIRDPARTY"
known_first_party = []

[tool.autohooks]
mode = "pythonpath"
pre-commit = ["autohooks.plugins.black", "autohooks.plugins.isort"]
File renamed without changes.
3 changes: 3 additions & 0 deletions python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dev.code-workspace
.mypy_cache
__pycache__
57 changes: 57 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# FHIRPathMappingLanguage - fpml python package

## Installation

```bash
pip install fpml
```

## Usage

```python
from fpml import resolve_template


resource = {
"resourceType": "QuestionnaireResponse",
"status": "completed",
"item": [
{
"linkId": "name",
"answer": [
{
"valueString": "Name"
}
]
}
]
}

template = {
"resourceType": "Patient",
"name": "{{ item.where(linkId='name').answer.valueString }}"
}

context = {}

result = resolve_template(resource, template, context)

print(result)
# {'resourceType': 'Patient', 'name': 'Name'}
```


## Development

In `./python` directory:

Run in the shell
```
autohooks activate
```

And edit `../.git/hooks/pre-commit` by replacing the first line with
```
#!/usr/bin/env -S poetry --project=./python run python
```

12 changes: 12 additions & 0 deletions python/fpml/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import importlib.metadata

from .core.exceptions import FPMLValidationError
from .core.extract import resolve_template

__title__ = "fpml"
__version__ = importlib.metadata.version("fpml")
__author__ = "beda.software"
__license__ = "MIT"
__copyright__ = "Copyright 2025 beda.software"

__all__ = ["FPMLValidationError", "resolve_template"]
Empty file added python/fpml/core/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions python/fpml/core/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# special root node key for the simplification
root_node_key = "__rootNode__"

# undefined is a special object used to remove keys from the object (similar to JS)
undefined = object()
11 changes: 11 additions & 0 deletions python/fpml/core/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .constants import root_node_key
from .types import Path


class FPMLValidationError(Exception):
def __init__(self, message: str, path: Path) -> None:
path_str = ".".join(str(x) for x in path if x != root_node_key)
super().__init__(f"{message}. Path '{path_str}'")

self.error_message = message
self.error_path = path_str
Loading