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
18 changes: 14 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@

<div align="center">


**Unofficial Python SDK for the iMednet clinical trials API.**

Full documentation: <https://fderuiter.github.io/imednet-python-sdk/>

</div>

<div align="center">
Expand Down Expand Up @@ -72,8 +75,7 @@ pip install git+https://github.com/fderuiter/imednet-python-sdk.git@main
### Synchronous Example

```python
from imednet import ImednetSDK
from imednet.config import load_config
from imednet import ImednetSDK, load_config
from imednet.utils import configure_json_logging

configure_json_logging()
Expand All @@ -90,8 +92,7 @@ print(sdk.studies.list())

```python
import asyncio
from imednet import AsyncImednetSDK
from imednet.config import load_config
from imednet import AsyncImednetSDK, load_config
from imednet.utils import configure_json_logging


Expand Down Expand Up @@ -176,10 +177,19 @@ disable this behaviour. See ``docs/cli.rst`` for full examples.
./scripts/setup.sh # once
poetry run ruff check --fix .
poetry run black --check .
poetry run isort --check --profile black .
poetry run mypy imednet
poetry run pytest -q
```

After running tests, validate documentation builds cleanly (no warnings):

```bash
make docs
```

See [docs/AGENTS.md](docs/AGENTS.md) for full documentation guidelines.

### Smoke-test workflow

The optional [smoke.yml](.github/workflows/smoke.yml) action runs the `tests/live` suite.
Expand Down
3 changes: 3 additions & 0 deletions docs/async_quick_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ List studies asynchronously and poll a job:
status = await sdk.async_poll_job("STUDY", "BATCH", interval=2, timeout=60)
print(status)


asyncio.run(main())

For synchronous usage, see :doc:`quick_start`.

The example script :doc:`examples/async_quick_start` provides a runnable version that
validates required environment variables and optionally polls a job when
``IMEDNET_JOB_STUDY_KEY`` and ``IMEDNET_BATCH_ID`` are set.
16 changes: 16 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ for the full list and details on using an ``.env`` file. The CLI calls
:func:`imednet.config.load_config` under the hood to read these values.

Command Hierarchy

Global Options
--------------

The CLI supports several global flags and behaviors:

- ``--help``: Show help for any command or subcommand.
- ``--version``: Print the CLI version and exit.
- ``.env`` loading: If a ``.env`` file is present in the working directory, environment variables are loaded automatically.

Example:

.. code-block:: console

$ imednet --version
imednet, version X.Y.Z
-----------------

.. mermaid::
Expand Down
54 changes: 31 additions & 23 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,37 @@ shell or stored in a ``.env`` file that the CLI loads automatically via
Environment Variables
---------------------

``IMEDNET_API_KEY``
API key used for authentication.

``IMEDNET_SECURITY_KEY``
Security key used for authentication.

``IMEDNET_BASE_URL``
Optional base URL for private deployments.

``IMEDNET_STUDY_KEY``
Study identifier used by examples and some tests.

``IMEDNET_RUN_E2E``
Set to ``1`` to enable end-to-end tests that hit a live environment.

``IMEDNET_BATCH_ID``
Batch identifier used by job polling tests. Created automatically if unset.

``IMEDNET_FORM_KEY``
Form key for record-creation tests. If unset, the first form is used.

``IMEDNET_ALLOW_MUTATION``
Set to ``1`` to allow workflow tests that submit data.
.. list-table::
:header-rows: 1
:widths: 25 50 15

* - Variable
- Description
- Default
* - IMEDNET_API_KEY
- API key used for authentication.
- None
* - IMEDNET_SECURITY_KEY
- Security key used for authentication.
- None
* - IMEDNET_BASE_URL
- Optional base URL for private deployments.
- None
* - IMEDNET_STUDY_KEY
- Study identifier used by examples and some tests.
- None
* - IMEDNET_RUN_E2E
- Set to ``1`` to enable end-to-end tests that hit a live environment.
- None
* - IMEDNET_BATCH_ID
- Batch identifier used by job polling tests. Created automatically if unset.
- None
* - IMEDNET_FORM_KEY
- Form key for record-creation tests. If unset, the first form is used.
- None
* - IMEDNET_ALLOW_MUTATION
- Set to ``1`` to allow workflow tests that submit data.
- None

Using a .env File
-----------------
Expand Down
4 changes: 4 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@

.. note::
This SDK is unofficial and not affiliated with the vendor. Use at your own risk.

Welcome to imednet's documentation!
=======================================

Expand Down
3 changes: 3 additions & 0 deletions docs/quick_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ Enable structured logging and list studies:
The example script :doc:`examples/quick_start` provides a runnable version that
validates required environment variables.


For asynchronous usage, see :doc:`async_quick_start`.

Cached endpoints can be refreshed with ``refresh=True``. The caches are not thread safe so long running applications should recreate the SDK when needed.

Custom retry logic can be provided via a ``RetryPolicy``:
Expand Down
10 changes: 10 additions & 0 deletions imednet/models/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@


class Keyword(JsonModel):
"""A keyword or tag associated with a record."""

keyword_name: str = Field("", alias="keywordName")
keyword_key: str = Field("", alias="keywordKey")
keyword_id: int = Field(0, alias="keywordId")
Expand All @@ -18,6 +20,8 @@ class Keyword(JsonModel):


class Record(JsonModel):
"""A data record for a subject, form, and visit."""

study_key: str = Field("", alias="studyKey")
interval_id: int = Field(0, alias="intervalId")
form_id: int = Field(0, alias="formId")
Expand All @@ -42,6 +46,8 @@ class Record(JsonModel):


class RecordJobResponse(JsonModel):
"""Response for a record-related job (batch operations, etc)."""

job_id: str = Field("", alias="jobId")
batch_id: str = Field("", alias="batchId")
state: str = Field("", alias="state")
Expand All @@ -50,10 +56,14 @@ class RecordJobResponse(JsonModel):


class RecordData(RootModel[Dict[str, Any]]):
"""Arbitrary record data as a dictionary."""

pass


class BaseRecordRequest(JsonModel):
"""Base class for record creation/update requests."""

form_key: str = Field("", alias="formKey")
data: RecordData = Field(default_factory=lambda: RecordData({}), alias="data")

Expand Down
2 changes: 2 additions & 0 deletions imednet/models/sites.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@


class Site(JsonModel):
"""A site participating in a study."""

study_key: str = Field("", alias="studyKey")
site_id: int = Field(0, alias="siteId")
site_name: str = Field("", alias="siteName")
Expand Down
2 changes: 2 additions & 0 deletions imednet/models/studies.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@


class Study(JsonModel):
"""Represents a clinical study and its metadata."""

sponsor_key: str = Field("", alias="sponsorKey")
study_key: str = Field("", alias="studyKey")
study_id: int = Field(0, alias="studyId")
Expand Down
4 changes: 4 additions & 0 deletions imednet/models/subjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@


class SubjectKeyword(JsonModel):
"""A keyword or tag associated with a subject."""

keyword_name: str = Field("", alias="keywordName")
keyword_key: str = Field("", alias="keywordKey")
keyword_id: int = Field(0, alias="keywordId")
Expand All @@ -18,6 +20,8 @@ class SubjectKeyword(JsonModel):


class Subject(JsonModel):
"""A subject (participant) in a study, with status and site info."""

study_key: str = Field("", alias="studyKey")
subject_id: int = Field(0, alias="subjectId")
subject_oid: str = Field("", alias="subjectOid")
Expand Down
11 changes: 11 additions & 0 deletions scripts/quick_start_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from imednet import ImednetSDK, load_config
from imednet.utils import configure_json_logging

configure_json_logging()
cfg = load_config()
sdk = ImednetSDK(
api_key=cfg.api_key,
security_key=cfg.security_key,
base_url=cfg.base_url,
)
print("SDK initialized successfully.")
Loading