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
4 changes: 1 addition & 3 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,4 @@ jobs:
id: release
uses: googleapis/release-please-action@v4
with:
release-type: python
package-name: imednet
version-file: packages/core/pyproject.toml
command: manifest
5 changes: 5 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"packages/core": "0.6.0",
"packages/providers-airflow": "0.5.1",
"packages/plugins-workflows": "0.5.1"
}
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ Releases are fully automated and driven by merged PR titles:
poetry run pytest -q
```
3. Merge to `main` using **Squash and merge** so the PR title becomes the merged commit message.
4. The `Automated Release` workflow runs `release-please` on `main` pushes and opens/updates a
Release PR with calculated semantic version, changelog updates, and `packages/core/pyproject.toml`
version updates.
4. The `Automated Release` workflow runs `release-please` in manifest mode on `main` pushes and
opens/updates a Release PR with calculated semantic version and changelog updates for the package
manifests under `packages/`.
5. Maintainers trigger publication by approving and merging the bot-created Release PR.

Configuration requirements:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pip install "imednet[excel]" # For Excel export
pip install imednet-workflows

# Airflow provider package
pip install apache-airflow-providers-imednet apache-airflow-providers-amazon
pip install "apache-airflow>=3.2.0" apache-airflow-providers-imednet apache-airflow-providers-amazon
```

### Development Version
Expand Down
2 changes: 1 addition & 1 deletion docs/airflow.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Install the provider package:

.. code-block:: bash

pip install apache-airflow-providers-imednet
pip install "apache-airflow>=3.2.0" apache-airflow-providers-imednet

Example DAG
-----------
Expand Down
6 changes: 3 additions & 3 deletions docs/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ Releases are fully automated and driven by merged PR titles:
poetry run pytest -q

3. Merge to ``main`` with **Squash and merge** so the PR title becomes the merged commit message.
4. The ``Automated Release`` workflow runs ``release-please`` on ``main`` pushes and opens/updates
a Release PR with semantic version, changelog updates, and ``packages/core/pyproject.toml``
version updates.
4. The ``Automated Release`` workflow runs ``release-please`` in manifest mode on ``main`` pushes
and opens/updates a Release PR with semantic version and changelog updates for the package
manifests under ``packages/``.
5. Maintainers trigger publication by approving and merging the bot-created Release PR.

Configuration requirements:
Expand Down
2 changes: 1 addition & 1 deletion docs/quick_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Optional plugin packages:
.. code-block:: console

pip install imednet-workflows
pip install apache-airflow-providers-imednet
pip install "apache-airflow>=3.2.0" apache-airflow-providers-imednet

Set your credentials by copying the environment template or exporting them directly (see :doc:`configuration` for details):

Expand Down
19 changes: 13 additions & 6 deletions packages/core/src/imednet/core/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
from __future__ import annotations

import logging
from typing import Any, Optional, Union
from typing import TYPE_CHECKING, Any, Optional, Union

try:
from opentelemetry import trace
if TYPE_CHECKING:
from opentelemetry.trace import Tracer
except Exception: # pragma: no cover - optional dependency
trace = None
Tracer = None
else: # pragma: no cover - typing fallback for optional dependency
Tracer = Any

import httpx

from imednet.auth.api_key import ApiKeyAuth
Expand All @@ -24,6 +23,13 @@
)
from imednet.utils import sanitize_base_url

trace: Any = None
try:
from opentelemetry import trace as _trace

trace = _trace
except Exception: # pragma: no cover - optional dependency
pass
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -55,6 +61,7 @@ def __init__(
self.auth = ApiKeyAuth(config.api_key or "", config.security_key or "")

self._client = self._create_client(self.auth)
self._tracer: Optional[Tracer] = None

if tracer is not None:
self._tracer = tracer
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins-workflows/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ packages = [{ include = "imednet_workflows", from = "src" }]

[tool.poetry.dependencies]
python = "^3.10"
imednet = ">=0.6.0,<1.0.0"
imednet = { path = "../core", develop = true }
pandas = ">=2.2.3,<3.0.0"
typer = { extras = ["all"], version = "^0.15.2" }

Expand Down
4 changes: 2 additions & 2 deletions packages/providers-airflow/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ packages = [{ include = "apache_airflow_providers_imednet", from = "src" }]

[tool.poetry.dependencies]
python = "^3.10"
imednet = ">=0.6.0,<1.0.0"
apache-airflow = ">=2.8.0"
imednet = { path = "../core", develop = true }
apache-airflow = ">=3.2.0"
apache-airflow-providers-amazon = ">=8.0.0"

[build-system]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Dict

if TYPE_CHECKING:
from airflow.exceptions import AirflowException
from airflow.utils.context import Context
else: # pragma: no cover - typing fallback for optional Airflow dependency
Context = Dict[str, Any]
try:
from airflow.exceptions import AirflowException
except (ImportError, ModuleNotFoundError):

class _FallbackAirflowError(Exception):
pass

AirflowException = _FallbackAirflowError
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

from __future__ import annotations

from typing import Any, Dict, Iterable, Optional
from typing import TYPE_CHECKING, Any, Dict, Optional, Sequence

if TYPE_CHECKING:
from airflow.utils.context import Context
else: # pragma: no cover - typing fallback for optional Airflow dependency
Context = Dict[str, Any]

from airflow.models import BaseOperator

Expand All @@ -13,7 +18,7 @@
class ImednetExportOperator(BaseOperator):
"""Export study records using helpers from :mod:`imednet.integrations.export`."""

template_fields: Iterable[str] = ("study_key", "output_path")
template_fields: Sequence[str] = ("study_key", "output_path")

def __init__(
self,
Expand All @@ -32,7 +37,7 @@ def __init__(
self.export_kwargs = export_kwargs or {}
self.imednet_conn_id = imednet_conn_id

def execute(self, context: Dict[str, Any]) -> str:
def execute(self, context: Context) -> str:
hook = ImednetHook(self.imednet_conn_id)
sdk = hook.get_conn()
export_callable = getattr(export, self.export_func)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from __future__ import annotations

import json
from typing import Any, Dict, Iterable, Optional
from typing import Any, Dict, Optional, Sequence

from .._airflow_compat import AirflowException, Context

_MISSING_AMAZON_PROVIDER_MESSAGE = (
"apache-airflow-providers-amazon package is required for "
Expand All @@ -12,14 +14,12 @@
)

try: # pragma: no cover - optional Airflow dependency
from airflow.exceptions import AirflowException # type: ignore
from airflow.models import BaseOperator # type: ignore
from airflow.providers.amazon.aws.hooks.s3 import S3Hook # type: ignore
except (ImportError, ModuleNotFoundError): # pragma: no cover - placeholder fallback
AirflowException = Exception

class BaseOperator: # type: ignore
template_fields: Iterable[str] = ()
template_fields: Sequence[str] = ()

def __init__(self, *args: Any, **kwargs: Any) -> None: # pragma: no cover
pass
Expand All @@ -40,7 +40,7 @@ def load_string(self, *args: Any, **kwargs: Any) -> None: # pragma: no cover
class ImednetToS3Operator(BaseOperator):
"""Fetch data from iMednet and store it in S3 as JSON."""

template_fields: Iterable[str] = ("study_key", "s3_key")
template_fields: Sequence[str] = ("study_key", "s3_key")

def __init__(
self,
Expand All @@ -66,7 +66,7 @@ def __init__(
def _get_sdk(self) -> ImednetSDK:
return ImednetHook(self.imednet_conn_id).get_conn()

def execute(self, context: Dict[str, Any]) -> str:
def execute(self, context: Context) -> str:
sdk = self._get_sdk()
endpoint_obj = getattr(sdk, self.endpoint)
if hasattr(endpoint_obj, "list"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,22 @@

from __future__ import annotations

from typing import Any, Dict, Iterable
from typing import Any, Sequence

try: # pragma: no cover - optional Airflow dependency
from airflow.exceptions import AirflowException
from airflow.sensors.base import BaseSensorOperator
except (ImportError, ModuleNotFoundError): # pragma: no cover - placeholder fallback
AirflowException = Exception

class BaseSensorOperator: # type: ignore
template_fields: Iterable[str] = ()
template_fields: Sequence[str] = ()

def __init__(self, *args: Any, **kwargs: Any) -> None: # pragma: no cover
pass


from imednet.sdk import ImednetSDK

from ._airflow_compat import AirflowException, Context
from .hooks import ImednetHook

TERMINAL_STATES = {"COMPLETED", "FAILED", "CANCELLED"}
Expand All @@ -27,7 +26,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: # pragma: no cover
class ImednetJobSensor(BaseSensorOperator):
"""Poll iMednet for job completion."""

template_fields: Iterable[str] = ("study_key", "batch_id")
template_fields: Sequence[str] = ("study_key", "batch_id")

def __init__(
self,
Expand All @@ -46,7 +45,7 @@ def __init__(
def _get_sdk(self) -> ImednetSDK:
return ImednetHook(self.imednet_conn_id).get_conn()

def poke(self, context: Dict[str, Any]) -> bool:
def poke(self, context: Context) -> bool:
sdk = self._get_sdk()
job = sdk.jobs.get(self.study_key, self.batch_id)
state = job.state.upper()
Expand Down
Loading
Loading