nimbusimage 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. nimbusimage-0.1.0/PKG-INFO +207 -0
  2. nimbusimage-0.1.0/README.md +177 -0
  3. nimbusimage-0.1.0/nimbusimage/__init__.py +107 -0
  4. nimbusimage-0.1.0/nimbusimage/_access.py +20 -0
  5. nimbusimage-0.1.0/nimbusimage/_girder.py +77 -0
  6. nimbusimage-0.1.0/nimbusimage/annotations.py +247 -0
  7. nimbusimage-0.1.0/nimbusimage/client.py +263 -0
  8. nimbusimage-0.1.0/nimbusimage/collections.py +195 -0
  9. nimbusimage-0.1.0/nimbusimage/connections.py +111 -0
  10. nimbusimage-0.1.0/nimbusimage/coordinates.py +221 -0
  11. nimbusimage-0.1.0/nimbusimage/dataset.py +264 -0
  12. nimbusimage-0.1.0/nimbusimage/export.py +76 -0
  13. nimbusimage-0.1.0/nimbusimage/filters.py +80 -0
  14. nimbusimage-0.1.0/nimbusimage/history.py +31 -0
  15. nimbusimage-0.1.0/nimbusimage/images.py +338 -0
  16. nimbusimage-0.1.0/nimbusimage/jobs.py +140 -0
  17. nimbusimage-0.1.0/nimbusimage/models.py +201 -0
  18. nimbusimage-0.1.0/nimbusimage/projects.py +142 -0
  19. nimbusimage-0.1.0/nimbusimage/properties.py +215 -0
  20. nimbusimage-0.1.0/nimbusimage/sharing.py +47 -0
  21. nimbusimage-0.1.0/nimbusimage/urls.py +94 -0
  22. nimbusimage-0.1.0/nimbusimage/worker.py +256 -0
  23. nimbusimage-0.1.0/nimbusimage.egg-info/PKG-INFO +207 -0
  24. nimbusimage-0.1.0/nimbusimage.egg-info/SOURCES.txt +44 -0
  25. nimbusimage-0.1.0/nimbusimage.egg-info/dependency_links.txt +1 -0
  26. nimbusimage-0.1.0/nimbusimage.egg-info/requires.txt +17 -0
  27. nimbusimage-0.1.0/nimbusimage.egg-info/top_level.txt +1 -0
  28. nimbusimage-0.1.0/pyproject.toml +46 -0
  29. nimbusimage-0.1.0/setup.cfg +4 -0
  30. nimbusimage-0.1.0/tests/test_annotations.py +135 -0
  31. nimbusimage-0.1.0/tests/test_client.py +135 -0
  32. nimbusimage-0.1.0/tests/test_collections.py +209 -0
  33. nimbusimage-0.1.0/tests/test_connections.py +94 -0
  34. nimbusimage-0.1.0/tests/test_coordinates.py +266 -0
  35. nimbusimage-0.1.0/tests/test_dataset.py +89 -0
  36. nimbusimage-0.1.0/tests/test_export.py +35 -0
  37. nimbusimage-0.1.0/tests/test_filters.py +87 -0
  38. nimbusimage-0.1.0/tests/test_history.py +26 -0
  39. nimbusimage-0.1.0/tests/test_images.py +288 -0
  40. nimbusimage-0.1.0/tests/test_jobs.py +324 -0
  41. nimbusimage-0.1.0/tests/test_models.py +203 -0
  42. nimbusimage-0.1.0/tests/test_projects.py +88 -0
  43. nimbusimage-0.1.0/tests/test_properties.py +117 -0
  44. nimbusimage-0.1.0/tests/test_sharing.py +36 -0
  45. nimbusimage-0.1.0/tests/test_urls.py +176 -0
  46. nimbusimage-0.1.0/tests/test_worker.py +187 -0
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: nimbusimage
3
+ Version: 0.1.0
4
+ Summary: Python API for NimbusImage
5
+ Author-email: Arjun Raj <arjunrajlab@gmail.com>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://nimbusimage.com
8
+ Project-URL: Repository, https://github.com/arjunrajlaboratory/NimbusImage
9
+ Project-URL: Documentation, https://docs.nimbusimage.com
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: girder-client
17
+ Requires-Dist: numpy
18
+ Requires-Dist: pydantic>=2.0
19
+ Requires-Dist: shapely
20
+ Requires-Dist: scikit-image
21
+ Provides-Extra: worker
22
+ Requires-Dist: large-image; extra == "worker"
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest; extra == "dev"
25
+ Requires-Dist: pytest-mock; extra == "dev"
26
+ Provides-Extra: docs
27
+ Requires-Dist: mkdocs; extra == "docs"
28
+ Requires-Dist: mkdocs-material; extra == "docs"
29
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
30
+
31
+ # nimbusimage
32
+
33
+ Python API for [NimbusImage](https://nimbusimage.com) — programmatic access to scientific imaging datasets, annotations, workers, and analysis.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install nimbusimage
39
+ ```
40
+
41
+ For Docker worker development (includes `large_image` for writing TIFF files):
42
+
43
+ ```bash
44
+ pip install nimbusimage[worker]
45
+ ```
46
+
47
+ ## Authentication
48
+
49
+ The recommended setup uses a **Girder API key**, which is persistent and doesn't expire.
50
+
51
+ **How to get an API key:**
52
+
53
+ - **nimbusimage.com (hosted):** Email **[support@cytopixel.com](mailto:support@cytopixel.com)** with your account email address to request an API key.
54
+ - **Local/self-hosted server:** In the Girder admin UI, go to **Users** > select the user > **Edit User** > **API Keys** > create a new key and copy the key string.
55
+
56
+ Set environment variables for persistent access:
57
+
58
+ ```bash
59
+ # Add to ~/.zshrc or ~/.bashrc
60
+
61
+ # For nimbusimage.com:
62
+ export NI_API_URL="https://nimbusimage.com/girder"
63
+ export NI_API_KEY="your-api-key-here"
64
+
65
+ # For a local server:
66
+ export NI_API_URL="http://localhost:8080/api/v1"
67
+ export NI_API_KEY="your-api-key-here"
68
+ ```
69
+
70
+ Then connect with no arguments:
71
+
72
+ ```python
73
+ import nimbusimage as ni
74
+
75
+ client = ni.connect()
76
+ ```
77
+
78
+ Or pass credentials explicitly:
79
+
80
+ ```python
81
+ client = ni.connect("http://localhost:8080/api/v1", api_key="your-api-key")
82
+ ```
83
+
84
+ ## Quick start
85
+
86
+ ```python
87
+ import nimbusimage as ni
88
+
89
+ client = ni.connect()
90
+
91
+ # List datasets
92
+ for d in client.list_datasets():
93
+ print(f"{d['name']} (ID: {d['_id']})")
94
+
95
+ # Open a dataset
96
+ ds = client.dataset(name="My Experiment")
97
+ print(f"{ds.name}: {ds.channels}, {ds.num_z} z-slices, {ds.shape}")
98
+
99
+ # Fetch an image
100
+ img = ds.images.get(channel=0, z=0) # numpy array
101
+
102
+ # Get a composite RGB image
103
+ rgb = ds.images.get_composite(dtype="uint8")
104
+
105
+ # List annotations
106
+ polygons = ds.annotations.list(shape="polygon")
107
+
108
+ # Run a worker
109
+ job = ds.annotations.compute(
110
+ image="annotations/random_squares:latest",
111
+ channel=0, tags=["detected"],
112
+ worker_interface={"Number of squares": 10, "Square size": 15},
113
+ )
114
+ job.wait()
115
+
116
+ # Export data
117
+ ds.export.to_csv(property_paths=[["prop_id", "Area"]], path="results.csv")
118
+
119
+ # Open in browser
120
+ ds.open(z=3)
121
+ ```
122
+
123
+ ## API overview
124
+
125
+ The package follows an accessor pattern:
126
+
127
+ ```
128
+ ni.connect() -> NimbusClient
129
+ client.dataset(id) -> Dataset
130
+ ds.images # fetch frames, composites, z-stacks
131
+ ds.annotations # create, list, filter, delete annotations
132
+ ds.connections # parent-child annotation links
133
+ ds.properties # computed measurements
134
+ ds.collections # display configuration (layers, tools)
135
+ ds.export # JSON and CSV export
136
+ ds.history # undo/redo
137
+ ds.sharing # access control
138
+ client.list_datasets()
139
+ client.list_workers()
140
+ client.list_projects()
141
+ ```
142
+
143
+ See the [full documentation](docs/) for detailed API reference and examples.
144
+
145
+ ## Claude Code integration
146
+
147
+ NimbusImage includes skills for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) that teach Claude how to use this API. After installing, you can ask Claude things like "connect to my NimbusImage server and list datasets" and it will write correct code.
148
+
149
+ ### Install the skills
150
+
151
+ **Option A — Marketplace install (recommended):**
152
+
153
+ ```bash
154
+ # Add the NimbusImage marketplace (one-time)
155
+ claude plugin marketplace add arjunrajlaboratory/NimbusImage
156
+
157
+ # Install the nimbusimage plugin
158
+ claude plugin install nimbusimage@arjunrajlaboratory/NimbusImage
159
+ ```
160
+
161
+ **Option B — For this session only:**
162
+
163
+ ```bash
164
+ claude --plugin-dir /path/to/NimbusImage/plugins/nimbusimage
165
+ ```
166
+
167
+ **Option C — Permanent (project-scoped):**
168
+
169
+ Add to your project's `.claude/settings.local.json`:
170
+
171
+ ```json
172
+ {
173
+ "plugins": ["/path/to/NimbusImage/plugins/nimbusimage"]
174
+ }
175
+ ```
176
+
177
+ ### Available skills
178
+
179
+
180
+ | Command | What it covers |
181
+ | -------------------------- | -------------------------------------------------- |
182
+ | `/nimbusimage` | Connection, dataset discovery, metadata, projects |
183
+ | `/nimbusimage:annotations` | Annotation CRUD, geometry helpers, bulk operations |
184
+ | `/nimbusimage:images` | Frame retrieval, composites, z-stacks, crops |
185
+ | `/nimbusimage:workers` | Docker worker discovery, execution, job tracking |
186
+ | `/nimbusimage:analyze` | Properties, export, connections, sharing |
187
+
188
+
189
+ The skills use progressive disclosure — Claude loads only what it needs for the current task.
190
+
191
+ ## Development
192
+
193
+ ```bash
194
+ cd nimbusimage
195
+ python3 -m venv .venv && source .venv/bin/activate
196
+ pip install -e ".[dev]"
197
+
198
+ # Run unit tests (no backend required)
199
+ pytest tests/ --ignore=tests/integration -v
200
+
201
+ # Run integration tests (requires docker compose up)
202
+ pytest tests/integration/ -v -m integration
203
+ ```
204
+
205
+ ## License
206
+
207
+ See the project root for license information.
@@ -0,0 +1,177 @@
1
+ # nimbusimage
2
+
3
+ Python API for [NimbusImage](https://nimbusimage.com) — programmatic access to scientific imaging datasets, annotations, workers, and analysis.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install nimbusimage
9
+ ```
10
+
11
+ For Docker worker development (includes `large_image` for writing TIFF files):
12
+
13
+ ```bash
14
+ pip install nimbusimage[worker]
15
+ ```
16
+
17
+ ## Authentication
18
+
19
+ The recommended setup uses a **Girder API key**, which is persistent and doesn't expire.
20
+
21
+ **How to get an API key:**
22
+
23
+ - **nimbusimage.com (hosted):** Email **[support@cytopixel.com](mailto:support@cytopixel.com)** with your account email address to request an API key.
24
+ - **Local/self-hosted server:** In the Girder admin UI, go to **Users** > select the user > **Edit User** > **API Keys** > create a new key and copy the key string.
25
+
26
+ Set environment variables for persistent access:
27
+
28
+ ```bash
29
+ # Add to ~/.zshrc or ~/.bashrc
30
+
31
+ # For nimbusimage.com:
32
+ export NI_API_URL="https://nimbusimage.com/girder"
33
+ export NI_API_KEY="your-api-key-here"
34
+
35
+ # For a local server:
36
+ export NI_API_URL="http://localhost:8080/api/v1"
37
+ export NI_API_KEY="your-api-key-here"
38
+ ```
39
+
40
+ Then connect with no arguments:
41
+
42
+ ```python
43
+ import nimbusimage as ni
44
+
45
+ client = ni.connect()
46
+ ```
47
+
48
+ Or pass credentials explicitly:
49
+
50
+ ```python
51
+ client = ni.connect("http://localhost:8080/api/v1", api_key="your-api-key")
52
+ ```
53
+
54
+ ## Quick start
55
+
56
+ ```python
57
+ import nimbusimage as ni
58
+
59
+ client = ni.connect()
60
+
61
+ # List datasets
62
+ for d in client.list_datasets():
63
+ print(f"{d['name']} (ID: {d['_id']})")
64
+
65
+ # Open a dataset
66
+ ds = client.dataset(name="My Experiment")
67
+ print(f"{ds.name}: {ds.channels}, {ds.num_z} z-slices, {ds.shape}")
68
+
69
+ # Fetch an image
70
+ img = ds.images.get(channel=0, z=0) # numpy array
71
+
72
+ # Get a composite RGB image
73
+ rgb = ds.images.get_composite(dtype="uint8")
74
+
75
+ # List annotations
76
+ polygons = ds.annotations.list(shape="polygon")
77
+
78
+ # Run a worker
79
+ job = ds.annotations.compute(
80
+ image="annotations/random_squares:latest",
81
+ channel=0, tags=["detected"],
82
+ worker_interface={"Number of squares": 10, "Square size": 15},
83
+ )
84
+ job.wait()
85
+
86
+ # Export data
87
+ ds.export.to_csv(property_paths=[["prop_id", "Area"]], path="results.csv")
88
+
89
+ # Open in browser
90
+ ds.open(z=3)
91
+ ```
92
+
93
+ ## API overview
94
+
95
+ The package follows an accessor pattern:
96
+
97
+ ```
98
+ ni.connect() -> NimbusClient
99
+ client.dataset(id) -> Dataset
100
+ ds.images # fetch frames, composites, z-stacks
101
+ ds.annotations # create, list, filter, delete annotations
102
+ ds.connections # parent-child annotation links
103
+ ds.properties # computed measurements
104
+ ds.collections # display configuration (layers, tools)
105
+ ds.export # JSON and CSV export
106
+ ds.history # undo/redo
107
+ ds.sharing # access control
108
+ client.list_datasets()
109
+ client.list_workers()
110
+ client.list_projects()
111
+ ```
112
+
113
+ See the [full documentation](docs/) for detailed API reference and examples.
114
+
115
+ ## Claude Code integration
116
+
117
+ NimbusImage includes skills for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) that teach Claude how to use this API. After installing, you can ask Claude things like "connect to my NimbusImage server and list datasets" and it will write correct code.
118
+
119
+ ### Install the skills
120
+
121
+ **Option A — Marketplace install (recommended):**
122
+
123
+ ```bash
124
+ # Add the NimbusImage marketplace (one-time)
125
+ claude plugin marketplace add arjunrajlaboratory/NimbusImage
126
+
127
+ # Install the nimbusimage plugin
128
+ claude plugin install nimbusimage@arjunrajlaboratory/NimbusImage
129
+ ```
130
+
131
+ **Option B — For this session only:**
132
+
133
+ ```bash
134
+ claude --plugin-dir /path/to/NimbusImage/plugins/nimbusimage
135
+ ```
136
+
137
+ **Option C — Permanent (project-scoped):**
138
+
139
+ Add to your project's `.claude/settings.local.json`:
140
+
141
+ ```json
142
+ {
143
+ "plugins": ["/path/to/NimbusImage/plugins/nimbusimage"]
144
+ }
145
+ ```
146
+
147
+ ### Available skills
148
+
149
+
150
+ | Command | What it covers |
151
+ | -------------------------- | -------------------------------------------------- |
152
+ | `/nimbusimage` | Connection, dataset discovery, metadata, projects |
153
+ | `/nimbusimage:annotations` | Annotation CRUD, geometry helpers, bulk operations |
154
+ | `/nimbusimage:images` | Frame retrieval, composites, z-stacks, crops |
155
+ | `/nimbusimage:workers` | Docker worker discovery, execution, job tracking |
156
+ | `/nimbusimage:analyze` | Properties, export, connections, sharing |
157
+
158
+
159
+ The skills use progressive disclosure — Claude loads only what it needs for the current task.
160
+
161
+ ## Development
162
+
163
+ ```bash
164
+ cd nimbusimage
165
+ python3 -m venv .venv && source .venv/bin/activate
166
+ pip install -e ".[dev]"
167
+
168
+ # Run unit tests (no backend required)
169
+ pytest tests/ --ignore=tests/integration -v
170
+
171
+ # Run integration tests (requires docker compose up)
172
+ pytest tests/integration/ -v -m integration
173
+ ```
174
+
175
+ ## License
176
+
177
+ See the project root for license information.
@@ -0,0 +1,107 @@
1
+ """NimbusImage Python API.
2
+
3
+ Usage:
4
+ import nimbusimage as ni
5
+
6
+ client = ni.connect(api_url, token=...)
7
+ ds = client.dataset(dataset_id)
8
+ img = ds.images.get(channel=0)
9
+ anns = ds.annotations.list(shape='polygon')
10
+ """
11
+
12
+ from nimbusimage.client import NimbusClient
13
+ from nimbusimage.collections import Collection
14
+ from nimbusimage.coordinates import attach_geometry_methods
15
+ from nimbusimage.dataset import Dataset
16
+ from nimbusimage.jobs import Job
17
+ from nimbusimage.filters import (
18
+ filter_by_tags,
19
+ filter_by_location,
20
+ group_by_location,
21
+ )
22
+ from nimbusimage.models import (
23
+ Annotation,
24
+ Connection,
25
+ FrameInfo,
26
+ Location,
27
+ PixelSize,
28
+ Property,
29
+ )
30
+ from nimbusimage.worker import WorkerContext
31
+
32
+ # Attach geometry methods (polygon, point, get_mask, etc.) to Annotation
33
+ attach_geometry_methods()
34
+
35
+
36
+ def connect(
37
+ api_url: str | None = None,
38
+ token: str | None = None,
39
+ api_key: str | None = None,
40
+ username: str | None = None,
41
+ password: str | None = None,
42
+ ) -> NimbusClient:
43
+ """Connect to a NimbusImage server.
44
+
45
+ Args:
46
+ api_url: Girder API URL. Or set NI_API_URL env var.
47
+ token: Auth token. Or set NI_TOKEN env var.
48
+ api_key: Girder API key. Or set NI_API_KEY env var.
49
+ API keys are persistent and don't expire (unlike
50
+ session tokens). Recommended for automation.
51
+ username: Username for interactive auth.
52
+ password: Password for interactive auth.
53
+
54
+ Returns:
55
+ Authenticated NimbusClient.
56
+ """
57
+ return NimbusClient(
58
+ api_url=api_url, token=token, api_key=api_key,
59
+ username=username, password=password,
60
+ )
61
+
62
+
63
+ def worker_context(
64
+ dataset_id: str | None = None,
65
+ api_url: str | None = None,
66
+ token: str | None = None,
67
+ params: dict | None = None,
68
+ ) -> WorkerContext:
69
+ """Create a worker context for Docker worker scripts.
70
+
71
+ Args:
72
+ dataset_id: The dataset folder ID.
73
+ api_url: Girder API URL.
74
+ token: Auth token.
75
+ params: Worker parameters dict (from the job).
76
+
77
+ Returns:
78
+ WorkerContext with parsed parameters and dataset access.
79
+ """
80
+ return WorkerContext(
81
+ dataset_id=dataset_id, api_url=api_url,
82
+ token=token, params=params,
83
+ )
84
+
85
+
86
+ __all__ = [
87
+ # Connection
88
+ "connect",
89
+ "worker_context",
90
+ # Classes
91
+ "NimbusClient",
92
+ "Collection",
93
+ "Dataset",
94
+ "Job",
95
+ "WorkerContext",
96
+ # Data models
97
+ "Annotation",
98
+ "Connection",
99
+ "Property",
100
+ "Location",
101
+ "PixelSize",
102
+ "FrameInfo",
103
+ # Filters
104
+ "filter_by_tags",
105
+ "filter_by_location",
106
+ "group_by_location",
107
+ ]
@@ -0,0 +1,20 @@
1
+ """Shared access level constants and helpers."""
2
+
3
+ ACCESS_MAP = {"read": 0, "write": 1, "admin": 2, "remove": -1}
4
+
5
+
6
+ def resolve_access(access: str) -> int:
7
+ """Convert a human-readable access level to its integer code.
8
+
9
+ Args:
10
+ access: One of 'read', 'write', 'admin', 'remove'.
11
+
12
+ Raises:
13
+ ValueError: If the access level is not recognized.
14
+ """
15
+ if access not in ACCESS_MAP:
16
+ raise ValueError(
17
+ f"Invalid access level '{access}'. "
18
+ f"Must be one of: {', '.join(ACCESS_MAP.keys())}"
19
+ )
20
+ return ACCESS_MAP[access]
@@ -0,0 +1,77 @@
1
+ """Internal wrapper around girder_client.GirderClient.
2
+
3
+ This module is an implementation detail. Users should never import from it.
4
+ All HTTP communication goes through this wrapper so that endpoint paths
5
+ and error handling are centralized.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+
12
+ import girder_client
13
+
14
+
15
+ def create_client(
16
+ api_url: str | None = None,
17
+ token: str | None = None,
18
+ api_key: str | None = None,
19
+ username: str | None = None,
20
+ password: str | None = None,
21
+ ) -> girder_client.GirderClient:
22
+ """Create and authenticate a GirderClient.
23
+
24
+ Connection modes (tried in order):
25
+ 1. Explicit token
26
+ 2. Explicit API key
27
+ 3. Username + password
28
+ 4. NI_API_KEY environment variable
29
+ 5. NI_TOKEN environment variable
30
+
31
+ Args:
32
+ api_url: Girder API URL (e.g., 'http://localhost:8080/api/v1').
33
+ token: Pre-existing authentication token.
34
+ api_key: Girder API key (persistent, doesn't expire).
35
+ username: Username for interactive auth.
36
+ password: Password for interactive auth.
37
+
38
+ Returns:
39
+ Authenticated GirderClient instance.
40
+
41
+ Raises:
42
+ ValueError: If no valid authentication method is provided.
43
+ """
44
+ if api_url is None:
45
+ api_url = os.environ.get("NI_API_URL")
46
+ if api_url is None:
47
+ raise ValueError(
48
+ "api_url must be provided or set NI_API_URL "
49
+ "environment variable"
50
+ )
51
+
52
+ gc = girder_client.GirderClient(apiUrl=api_url)
53
+
54
+ if token is not None:
55
+ gc.setToken(token)
56
+ elif api_key is not None:
57
+ gc.authenticate(apiKey=api_key)
58
+ elif username is not None or password is not None:
59
+ if username is None or password is None:
60
+ raise ValueError(
61
+ "Both username and password must be provided"
62
+ )
63
+ gc.authenticate(username=username, password=password)
64
+ else:
65
+ env_api_key = os.environ.get("NI_API_KEY")
66
+ env_token = os.environ.get("NI_TOKEN")
67
+ if env_api_key is not None:
68
+ gc.authenticate(apiKey=env_api_key)
69
+ elif env_token is not None:
70
+ gc.setToken(env_token)
71
+ else:
72
+ raise ValueError(
73
+ "Provide token=, api_key=, username=/password=, "
74
+ "or set NI_API_KEY/NI_TOKEN environment variable"
75
+ )
76
+
77
+ return gc