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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datalab-api
3
- Version: 0.4.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
- try:
151
- collection_immutable_id = self.get_collection(collection_id)[0]["immutable_id"]
152
- except (RuntimeError, DatalabAPIError):
153
- self.create_collection(collection_id)
154
- collection_immutable_id = self.get_collection(collection_id)[0]["immutable_id"]
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
- new_item["collections"].append({"immutable_id": collection_immutable_id})
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(self, item_id: str, file_path: Path | str) -> dict[str, Any]:
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": None},
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["User-Agent"] = f"Datalab Python API/{__version__}"
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.4.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