Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fdc70f1
Added cli/fhir_transform.py and transform_utils.py
dsafarian Aug 14, 2026
134df13
Add fhir_transform cli, utils, and tests
dsafarian Aug 14, 2026
92d603e
Remove unnecessary imports
dsafarian Aug 14, 2026
44940a9
Add documentation, format tests, integrate into gen3 cli, add test_da…
dsafarian Aug 18, 2026
aab4d54
Fix and add tests, create folder even if output_file path doesn't exist
dsafarian Aug 18, 2026
c66e855
Pass all tests, modify multiprocessing to not cause issues with gen3,…
dsafarian Aug 19, 2026
404be39
test md
dsafarian Aug 19, 2026
8899bef
Add fhir.md
dsafarian Aug 19, 2026
2d2e6fe
Merge branch 'uc-cdis:master' into fhir_transform
dsafarian Aug 19, 2026
3f861f6
Black formatting
dsafarian Aug 19, 2026
80f1315
black formatting
dsafarian Aug 19, 2026
d8ae042
Fix pull request comments
dsafarian Aug 22, 2026
76c6797
Delete test outputs
dsafarian Aug 22, 2026
a34ed6c
Remove setuptools dependency from pyproject.toml
dsafarian Aug 22, 2026
d929fd2
Fix pyproject.toml dependencies and poetry.lock
dsafarian Aug 22, 2026
8e0174c
Remove FIXME
dsafarian Aug 22, 2026
6192216
Fix default working directory and directory creation, imports, rename…
dsafarian Aug 24, 2026
6b5f319
Remove test outputs, fix typos, add assertions, change to SHA-256 has…
dsafarian Aug 27, 2026
d00ecf5
Fix hash bug
dsafarian Aug 27, 2026
8339418
Add unit test for global authz and fix tmp_path
dsafarian Aug 27, 2026
9cbf8ba
Comment out all parrallelization code
dsafarian Aug 27, 2026
8f5eee5
Remove _resume_run from all code
dsafarian Aug 27, 2026
9cdf886
Fix exception handling
dsafarian Aug 27, 2026
818c537
Fix resource_type check
dsafarian Aug 27, 2026
dd2abb2
Use fhirpathpy.compile instead of fhirpathpy.evaluate for speed up.
dsafarian Aug 27, 2026
157eb94
Add check for if output file exists and is not empty for _is_new.
dsafarian Aug 27, 2026
d7773f6
Fix poetry dependencies
dsafarian Aug 28, 2026
82e8710
Remove all parallelization code and make fhir import optional in cli
dsafarian Aug 29, 2026
9c42dd7
Add helper function for tag_fhir_resources_with_authz and fix tmp_roo…
dsafarian Aug 29, 2026
a9a0418
Black formatting
dsafarian Aug 31, 2026
7b942b0
fix(drsclient): remove dep due to out of date sub-deps, implement DRS…
Avantol13 Aug 31, 2026
e7c6dac
Merge branch 'master' into copy-of-external-pr-313
Avantol13 Sep 1, 2026
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
19 changes: 2 additions & 17 deletions docs/howto/devTest.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,11 @@
## Dev-Test

### Set up Python Virtual Environment

You can set up a Python development environment with a virtual environment:

```bash
python3 -m venv py3
```

Make sure that you have the virtual environment activated:

```bash
. py3/bin/activate
```

### Install poetry

To use the latest code in this repo (or to develop new features) you can clone this repo, install `poetry`:

```
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
curl -sSL https://install.python-poetry.org | python3 -
```

and then use `poetry` to install this package:
Expand All @@ -34,8 +20,7 @@ poetry install -vv
Local development like this:

```
poetry shell
poetry install -vv
eval $(poetry env activate)
python3 -m pytest
```

Expand Down
32 changes: 32 additions & 0 deletions docs/howto/fhir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## FHIR

This integration aims to enhance the data ingestion capabilities of Gen3 by integrating a Fast Healthcare Interoperability Resources (FHIR) data ingestion pipeline & tools. FHIR is an important standard for working with Electronic Health Records (EHR) and we have started development of a Gen3 FHIR Proxy service. The overall goal is to allow users to seamlessly ingest data into an existing FHIR server. Gen3 is working on adding support for FHIR and this tool will help with data preparation and interaction in the future.


The fhir commands can be invoked as follows

`gen3 fhir COMMAND [ARGS] [OPTIONS]`

For a list of commands and options run

`gen3 fhir --help`

For example, the following tags the 'Patient.ndjson' file with Gen3 authorization and outputs 'gen3_Patient.ndjson' using the authorization rules from 'config.yaml'

`gen3 fhir transform Patient.ndjson gen3_Patient.ndjson config.yaml --batch_size 10000 --workers 8 `


The authorization configuration file has to be in yaml format and can have multiple conditions, e.g:

```yaml
rules:
- resource_type: "Patient"
condition: "Patient.managingOrganization.reference = 'Organization/site-alpha'"
authz: "/programs/Alpha/projects/Main"

- resource_type: "Specimen"
condition: ""Specimen.status = 'available' and Specimen.Type = 'Blood specimen (specimen)'""
authz: "/programs/Alpha/projects/Biobank"
```

And example config.yaml file can be found in [Config reference](tests/test_data/fhir_config.yaml)
2 changes: 2 additions & 0 deletions gen3/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import gen3.cli.drs_pull as drs_pull
import gen3.cli.users as users
import gen3.cli.wrap as wrap
import gen3.cli.fhir as fhir
import gen3
from gen3 import logging as sdklogging
from gen3.cli import nih
Expand Down Expand Up @@ -145,4 +146,5 @@ def main(
main.add_command(nih.nih)
main.add_command(users.users)
main.add_command(wrap.run)
main.add_command(fhir.fhir)
main()
127 changes: 127 additions & 0 deletions gen3/cli/fhir.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import click
import os
import pathlib
from cdislogging import get_logger
from gen3.fhir import *

logging = get_logger(__name__)

@click.group()
def fhir():
"""Commands for FHIR data processing"""
pass


@click.command(
context_settings={"help_option_names": ["-h", "--help"]},
help="""Tag Bulk FHIR data with Gen3 compatible authorization tags.

\b
input_file (str): Input .ndjson file with Bulk FHIR data, one resource type per file
output_file (str): Output file name, also an .ndjson file
config (str): .yaml file with authorization rules, see docs/howto/fhir.md for more details on formatting
""",
)
@click.argument(
"input_file",
type=click.Path(exists=True, dir_okay=False, readable=True),
metavar="input_file",
)
@click.argument(
"output_file", type=click.Path(dir_okay=False, writable=True), metavar="output_file"
)
@click.argument(
"config",
type=click.Path(exists=True, dir_okay=False, readable=True),
metavar="config",
)

@click.option(
"--work_dir",
type=click.Path(),
metavar="work_dir",
help=f"Specify which working directory to clean, if not specified the default ({DEFAULT_WORK_DIR}) will be cleaned. Can also be set as an environment variable: GEN3_FHIR_WORK_DIR",
)

@click.option(
"-b",
"--batch_size",
type=click.IntRange(min=1),
default=10000,
show_default=True,
metavar="batch_size",
help="batch size for chunking",
)
@click.option(
"-w",
"--workers",
type=click.IntRange(min=1),
default=8,
show_default=True,
metavar="workers",
help="number of parallel processes",
)
@click.option(
"--force",
is_flag=True,
help="Remove all intermediate files for this run before exiting even if run crashes",
)
def cli(
input_file: str | os.PathLike[str],
output_file: str | os.PathLike[str],
config: str | os.PathLike[str],
workers: int,
work_dir: str | os.PathLike[str],
batch_size: int,
force: bool,
):
"""
CLI implementation of tag_fhir_resources_with_authz.

Args:
input_file (str): Input .ndjson file
output_file (str): Output file name
config (str): .yaml file with authorization rules
work_dir (str): Working directory to save intermediate files for each run
batch_size (int): number of lines per chunk
workers (int): number of parallel processes
force (bool): remove all intermediate files for this run before exiting even if it crashes
"""
tag_fhir_resources_with_authz(input_file, output_file, config, batch_size, work_dir, workers, force)


@click.command(
context_settings={"help_option_names": ["-h", "--help"]},
help="Remove all intermediate files in the tmp folder from previous runs",
)
@click.option(
"--work_dir",
type=click.Path(),
metavar="work_dir",
help=f"Specify which working directory to clean, if not specified the default ({DEFAULT_WORK_DIR}) will be cleaned. Can also be set as an environment variable: GEN3_FHIR_WORK_DIR",
)
@click.option(
"--dry-run",
is_flag=True,
help="Used with --cleanup, report what would be deleted with --cleanup without deleting the files",
)
@click.option(
"--force",
is_flag=True,
help="Remove temporary directory ignoring status of each directory",
)
def cleanup(work_dir, dry_run: bool, force: bool):
"""
Remove all intermediate files in the tmp folder from previous runs

Args:
work_dir (str): Working directory to save intermediate files for each run
dry_run (bool): If True, list the files that would be removed, but not actually remove them
force (bool): Delete all intermediate directories disregarding the status
"""

cleanup_fhir_transform_artifacts(work_dir,dry_run=dry_run, force=force)


fhir.add_command(cli, name="transform")
fhir.add_command(cleanup, name="cleanup")
Loading
Loading