datalab-api 0.4.0__tar.gz → 0.5.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.
- {datalab_api-0.4.0/src/datalab_api.egg-info → datalab_api-0.5.0}/PKG-INFO +1 -1
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api/__init__.py +64 -9
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api/_base.py +2 -2
- {datalab_api-0.4.0 → datalab_api-0.5.0/src/datalab_api.egg-info}/PKG-INFO +1 -1
- {datalab_api-0.4.0 → datalab_api-0.5.0}/tests/test_client.py +13 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/LICENSE +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/README.md +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/pyproject.toml +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/setup.cfg +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api/cli.py +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api/utils.py +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api.egg-info/SOURCES.txt +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api.egg-info/dependency_links.txt +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api.egg-info/entry_points.txt +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api.egg-info/requires.txt +0 -0
- {datalab_api-0.4.0 → datalab_api-0.5.0}/src/datalab_api.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datalab-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
|
|
5
5
|
Author-email: Matthew Evans <matthew@ml-evs.science>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -127,6 +127,8 @@ class DatalabClient(BaseDatalabClient):
|
|
|
127
127
|
item_type: str = "samples",
|
|
128
128
|
item_data: dict[str, Any] | None = None,
|
|
129
129
|
collection_id: str | None = None,
|
|
130
|
+
collection_ids: str | Iterable[str] | None = None, # alias for collection_id
|
|
131
|
+
group_ids: str | Iterable[str] | None = None,
|
|
130
132
|
) -> dict[str, Any]:
|
|
131
133
|
"""Create an item with a given ID and item data.
|
|
132
134
|
|
|
@@ -135,7 +137,9 @@ class DatalabClient(BaseDatalabClient):
|
|
|
135
137
|
item_type: The type of item to create, e.g., 'samples', 'cells'.
|
|
136
138
|
item_data: The data for the item.
|
|
137
139
|
collection_id: The ID of the collection to add the item to (optional).
|
|
138
|
-
If such a collection does not exist, one will be made.
|
|
140
|
+
If such a collection does not exist, one will be made. Deprecated in favor of `collection_ids`.
|
|
141
|
+
collection_ids: The ID or IDs of the collection(s) to add the item to (optional).
|
|
142
|
+
group_ids: The ID or IDs of the group(s) to share the item with (optional).
|
|
139
143
|
|
|
140
144
|
"""
|
|
141
145
|
new_item = {}
|
|
@@ -147,13 +151,34 @@ class DatalabClient(BaseDatalabClient):
|
|
|
147
151
|
new_item.pop("item_id")
|
|
148
152
|
|
|
149
153
|
if collection_id is not None:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
warnings.warn(
|
|
155
|
+
DeprecationWarning(
|
|
156
|
+
"`collection_id` is deprecated, please use `collection_ids` instead."
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
if collection_ids is None:
|
|
160
|
+
collection_ids = [collection_id]
|
|
161
|
+
|
|
162
|
+
if collection_ids:
|
|
155
163
|
new_item["collections"] = new_item.get("collections", [])
|
|
156
|
-
|
|
164
|
+
for collection_id in collection_ids:
|
|
165
|
+
try:
|
|
166
|
+
collection_immutable_id = self.get_collection(collection_id)[0]["immutable_id"]
|
|
167
|
+
except (RuntimeError, DatalabAPIError):
|
|
168
|
+
self.create_collection(collection_id)
|
|
169
|
+
collection_immutable_id = self.get_collection(collection_id)[0]["immutable_id"]
|
|
170
|
+
new_item["collections"].append({"immutable_id": collection_immutable_id})
|
|
171
|
+
|
|
172
|
+
if group_ids:
|
|
173
|
+
# For now, we have to use the search groups endpoint
|
|
174
|
+
# until we add a get group by id endpoint that is locked down per user
|
|
175
|
+
new_item["groups"] = new_item.get("groups", [])
|
|
176
|
+
for group_id in group_ids:
|
|
177
|
+
try:
|
|
178
|
+
group_immutable_id = self.get_group(group_id)["immutable_id"]
|
|
179
|
+
new_item["groups"].append({"immutable_id": group_immutable_id})
|
|
180
|
+
except (RuntimeError, DatalabAPIError):
|
|
181
|
+
pass
|
|
157
182
|
|
|
158
183
|
create_item_url = f"{self.datalab_api_url}/new-sample/"
|
|
159
184
|
created_item = self._post(
|
|
@@ -271,12 +296,16 @@ class DatalabClient(BaseDatalabClient):
|
|
|
271
296
|
block = self._post(block_url, json=block_request)
|
|
272
297
|
return block["new_block_data"]
|
|
273
298
|
|
|
274
|
-
def upload_file(
|
|
299
|
+
def upload_file(
|
|
300
|
+
self, item_id: str, file_path: Path | str, replace_file_id: str | None = None
|
|
301
|
+
) -> dict[str, Any]:
|
|
275
302
|
"""Upload a file to an item with a given ID.
|
|
276
303
|
|
|
277
304
|
Parameters:
|
|
278
305
|
item_id: The ID of the item to upload the file to.
|
|
279
306
|
file_path: The path to the file to upload.
|
|
307
|
+
replace_file_id: The ID of an existing file to replace with the uploaded file, if any.
|
|
308
|
+
If provided, the new file will take the place of the old file in any blocks it was attached to.
|
|
280
309
|
|
|
281
310
|
Returns:
|
|
282
311
|
A dictionary of the uploaded file data.
|
|
@@ -297,7 +326,7 @@ class DatalabClient(BaseDatalabClient):
|
|
|
297
326
|
upload = self._post(
|
|
298
327
|
upload_url,
|
|
299
328
|
files=files,
|
|
300
|
-
data={"item_id": item_id, "replace_file":
|
|
329
|
+
data={"item_id": item_id, "replace_file": replace_file_id},
|
|
301
330
|
expected_status=201,
|
|
302
331
|
timeout=upload_timeout,
|
|
303
332
|
)
|
|
@@ -435,6 +464,32 @@ class DatalabClient(BaseDatalabClient):
|
|
|
435
464
|
collection = self._get(collection_url)
|
|
436
465
|
return collection["data"], collection["child_items"]
|
|
437
466
|
|
|
467
|
+
def get_group(self, group_id: str) -> dict[str, Any]:
|
|
468
|
+
"""Get a group with a given ID.
|
|
469
|
+
|
|
470
|
+
Uses the search groups endpoint under the hood,
|
|
471
|
+
so will only find groups that the user has access
|
|
472
|
+
to and that match the query.
|
|
473
|
+
|
|
474
|
+
Parameters:
|
|
475
|
+
group_id: The human-readable ID of the group to search for.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
A dictionary of group data for the group with the given ID.
|
|
479
|
+
|
|
480
|
+
Raises:
|
|
481
|
+
DatalabAPIError: If no group with the given ID is found.
|
|
482
|
+
|
|
483
|
+
"""
|
|
484
|
+
group_url = f"{self.datalab_api_url}/search/groups?query={group_id}"
|
|
485
|
+
group_resp = self._get(group_url)
|
|
486
|
+
matching_groups = group_resp["data"]
|
|
487
|
+
if matching_groups:
|
|
488
|
+
matching_groups = [g for g in matching_groups if g.get("group_id") == group_id]
|
|
489
|
+
return matching_groups[0]
|
|
490
|
+
|
|
491
|
+
raise DatalabAPIError(f"No group found with ID {group_id!r}")
|
|
492
|
+
|
|
438
493
|
def create_collection(
|
|
439
494
|
self, collection_id: str, collection_data: dict | None = None
|
|
440
495
|
) -> dict[str, Any]:
|
|
@@ -35,7 +35,7 @@ class BaseDatalabClient(metaclass=AutoPrettyPrint):
|
|
|
35
35
|
|
|
36
36
|
_api_key: Optional[str] = None
|
|
37
37
|
_session: Optional[httpx.Client] = None
|
|
38
|
-
_headers: dict[str, str]
|
|
38
|
+
_headers: dict[str, str]
|
|
39
39
|
_timeout: httpx.Timeout = httpx.Timeout(10.0, read=60.0)
|
|
40
40
|
|
|
41
41
|
interactive: bool = True
|
|
@@ -85,7 +85,7 @@ class BaseDatalabClient(metaclass=AutoPrettyPrint):
|
|
|
85
85
|
self.log = logging.getLogger(__name__)
|
|
86
86
|
|
|
87
87
|
self._http_client = httpx.Client
|
|
88
|
-
self._headers
|
|
88
|
+
self._headers = {"User-Agent": f"Datalab Python API/{__version__}"}
|
|
89
89
|
|
|
90
90
|
self._detect_api_url()
|
|
91
91
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datalab-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
|
|
5
5
|
Author-email: Matthew Evans <matthew@ml-evs.science>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -58,3 +58,16 @@ def test_collection_get(fake_api_url, mocked_api):
|
|
|
58
58
|
|
|
59
59
|
collection, children = client.get_collection("test_collection", display=True)
|
|
60
60
|
assert mocked_api["collection-test_collection"].called
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@respx.mock
|
|
64
|
+
def test_group_get(fake_api_url, mocked_api):
|
|
65
|
+
with DatalabClient("https://api.datalab.industries") as client:
|
|
66
|
+
assert mocked_api["api"].called
|
|
67
|
+
assert mocked_api["info"].called
|
|
68
|
+
assert mocked_api["info-blocks"].called
|
|
69
|
+
|
|
70
|
+
group = client.get_group("test_group", display=True)
|
|
71
|
+
assert mocked_api["search-groups"].called
|
|
72
|
+
assert group["immutable_id"] == "67d0b01e03000cdc75134dbd"
|
|
73
|
+
assert group["group_id"] == "test_group"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|