diff --git a/README.md b/README.md index 9c5c8045..c8b07e8c 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,11 @@
+ **Unofficial Python SDK for the iMednet clinical trials API.** +Full documentation: +
@@ -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() @@ -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 @@ -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. diff --git a/docs/async_quick_start.rst b/docs/async_quick_start.rst index d1599c48..5552c331 100644 --- a/docs/async_quick_start.rst +++ b/docs/async_quick_start.rst @@ -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. diff --git a/docs/cli.rst b/docs/cli.rst index 85ad8fa1..647d191d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -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:: diff --git a/docs/configuration.rst b/docs/configuration.rst index 8ad9388e..b1648970 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -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 ----------------- diff --git a/docs/index.rst b/docs/index.rst index ec06b13d..100bd2ac 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -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! ======================================= diff --git a/docs/quick_start.rst b/docs/quick_start.rst index c298fdd3..1bf0be64 100644 --- a/docs/quick_start.rst +++ b/docs/quick_start.rst @@ -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``: diff --git a/imednet/models/records.py b/imednet/models/records.py index 98f9c772..031582cb 100644 --- a/imednet/models/records.py +++ b/imednet/models/records.py @@ -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") @@ -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") @@ -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") @@ -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") diff --git a/imednet/models/sites.py b/imednet/models/sites.py index bdebb8d3..54d77f7c 100644 --- a/imednet/models/sites.py +++ b/imednet/models/sites.py @@ -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") diff --git a/imednet/models/studies.py b/imednet/models/studies.py index 5095fac9..f96599e1 100644 --- a/imednet/models/studies.py +++ b/imednet/models/studies.py @@ -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") diff --git a/imednet/models/subjects.py b/imednet/models/subjects.py index f1cd6b53..da0a89b8 100644 --- a/imednet/models/subjects.py +++ b/imednet/models/subjects.py @@ -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") @@ -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") diff --git a/scripts/quick_start_test.py b/scripts/quick_start_test.py new file mode 100644 index 00000000..2d4d6cf6 --- /dev/null +++ b/scripts/quick_start_test.py @@ -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.")