Source code for imednet.cli

"""iMednet Command Line Interface."""

from __future__ import annotations

import argparse
import os
import sys

try:
    from dotenv import load_dotenv

    load_dotenv()
except ImportError:
    print(
        "Warning: python-dotenv not installed. Install with `pip install 'imednet[cli]'` to load .env files automatically.",
        file=sys.stderr,
    )

from ..integrations import SinkConfig
from ..integrations.export import (
    export_to_csv,
    export_to_duckdb,
    export_to_excel,
    export_to_json,
    export_to_long_sql,
    export_to_parquet,
    export_to_sql,
    export_to_sql_by_form,
)
from .decorators import with_sdk
from .utils import get_sdk, parse_filter_args

__all__ = [
    "SinkConfig",
    "app",
    "export_to_csv",
    "export_to_duckdb",
    "export_to_excel",
    "export_to_json",
    "export_to_long_sql",
    "export_to_parquet",
    "export_to_sql",
    "export_to_sql_by_form",
    "get_parser",
    "get_sdk",
    "parse_filter_args",
    "with_sdk",
]


def _setup_standalone_scripts(subparsers: argparse._SubParsersAction) -> None:  # type: ignore[type-arg]
    import glob

    script_dir = os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "..", "scripts")
    for script_path in glob.glob(os.path.join(script_dir, "*.py")):
        name = os.path.basename(script_path)[:-3]
        if name == "__init__":
            continue

        # We need a dash format for command names
        cmd_name = name.replace("_", "-")
        parser = subparsers.add_parser(cmd_name, help=f"Run {name} script.")
        parser.add_argument(
            "args", nargs=argparse.REMAINDER, help="Arguments passed to the script."
        )

        def run_script(args: argparse.Namespace, script_path: str = script_path) -> None:
            import subprocess

            res = subprocess.run([sys.executable, script_path] + args.args)  # noqa: RUF005, S603
            if res.returncode != 0:
                sys.exit(res.returncode)

        parser.set_defaults(func=run_script)


[docs]def get_parser() -> argparse.ArgumentParser: """Return the main argparse.ArgumentParser for the CLI.""" parser = argparse.ArgumentParser(description="iMednet SDK Command Line Interface") parser.add_argument( "--high-contrast", action="store_true", help="Enable high-contrast mode for accessibility." ) subparsers = parser.add_subparsers(dest="command") from .export import setup_parser as export_setup from .intervals import setup_parser as intervals_setup from .jobs import setup_parser as jobs_setup from .queries import setup_parser as queries_setup from .record_revisions import setup_parser as record_revisions_setup from .records import setup_parser as records_setup from .sites import setup_parser as sites_setup from .studies import setup_parser as studies_setup from .subjects import setup_parser as subjects_setup from .variables import setup_parser as variables_setup studies_setup(subparsers) queries_setup(subparsers) variables_setup(subparsers) record_revisions_setup(subparsers) sites_setup(subparsers) export_setup(subparsers) subjects_setup(subparsers) intervals_setup(subparsers) jobs_setup(subparsers) records_setup(subparsers) _setup_standalone_scripts(subparsers) import importlib.metadata registered_plugins = set() # Discover and register CLI plugins using entry points for entry_point in importlib.metadata.entry_points(group="imednet.cli_plugins"): try: plugin_setup = entry_point.load() plugin_setup(subparsers) registered_plugins.add(entry_point.name) except Exception as e: print(f"Warning: Failed to load CLI plugin '{entry_point.name}': {e}", file=sys.stderr) if "workflows" not in registered_plugins: workflows_parser = subparsers.add_parser( "workflows", help="Workflows plugin (not installed)" ) workflows_parser.add_argument("args", nargs=argparse.REMAINDER) def workflows_stub(args: argparse.Namespace) -> None: print("The workflows plugin is not installed.", file=sys.stderr) sys.exit(1) workflows_parser.set_defaults(func=workflows_stub) if "dashboard" not in registered_plugins: dashboard_parser = subparsers.add_parser( "dashboard", help="Dashboard plugin (not installed)" ) dashboard_parser.add_argument("args", nargs=argparse.REMAINDER) def dashboard_stub(args: argparse.Namespace) -> None: print( "The dashboard plugin is not installed. Please pip install imednet-streamlit.", file=sys.stderr, ) sys.exit(1) dashboard_parser.set_defaults(func=dashboard_stub) return parser
[docs]def app(args: list[str] | None = None) -> None: """Main entrypoint for the CLI.""" if args is None: args = sys.argv[1:] parser = get_parser() parsed = parser.parse_args(args) if parsed.high_contrast: os.environ["IMEDNET_HIGH_CONTRAST"] = "1" if hasattr(parsed, "func"): parsed.func(parsed) else: parser.print_help()
if __name__ == "__main__": app()