unitlab 2.4.2__tar.gz → 2.4.4__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.
@@ -41,7 +41,9 @@
41
41
  "Bash(ps:*)",
42
42
  "WebFetch(domain:127.0.0.1)",
43
43
  "Bash(DATASET_ID=\"b727a6fd-7786-4d40-b75b-56e7f52f378b\")",
44
- "Bash(pipenv run:*)"
44
+ "Bash(pipenv run:*)",
45
+ "Bash(DS=\"b727a6fd-7786-4d40-b75b-56e7f52f378b\")",
46
+ "Bash(python3 -c \"import sys,json; d=json.load\\(sys.stdin\\); print\\(''''OK'''' if ''''file'''' in d else d\\)\")"
45
47
  ]
46
48
  }
47
49
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitlab
3
- Version: 2.4.2
3
+ Version: 2.4.4
4
4
  Summary: Python SDK for the Unitlab.ai data annotation platform
5
5
  Project-URL: Homepage, https://unitlab.ai
6
6
  Project-URL: Documentation, https://docs.unitlab.ai
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "unitlab"
7
- version = "2.4.2"
7
+ version = "2.4.4"
8
8
  description = "Python SDK for the Unitlab.ai data annotation platform"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -37,12 +37,6 @@ class DownloadType(str, Enum):
37
37
  files = "files"
38
38
 
39
39
 
40
- class DatasetSplit(str, Enum):
41
- train = "train"
42
- validation = "validation"
43
- test = "test"
44
-
45
-
46
40
  @app.command(help="Configure the credentials")
47
41
  def configure(
48
42
  api_key: Annotated[
@@ -124,11 +118,14 @@ def dataset_download(
124
118
  typer.Option(help="Export type (e.g. COCO, YOLOv8, YOLOv5, UUEF)"),
125
119
  ] = "COCO",
126
120
  split_type: Annotated[
127
- DatasetSplit, typer.Option(help="Dataset split (train, validation, test)")
128
- ] = DatasetSplit.train,
121
+ str | None,
122
+ typer.Option(
123
+ help="Dataset split (e.g. train, validation). Auto-detected if omitted."
124
+ ),
125
+ ] = None,
129
126
  ):
130
127
  if download_type == DownloadType.annotation:
131
- get_client(api_key).dataset_download(str(pk), export_type, split_type.value)
128
+ get_client(api_key).dataset_download(str(pk), export_type, split_type)
132
129
  else:
133
130
  typer.echo(get_client(api_key).dataset_download_files(str(pk)))
134
131
 
@@ -4,6 +4,7 @@ import asyncio
4
4
  import functools
5
5
  import logging
6
6
  import os
7
+ import uuid
7
8
  from pathlib import Path
8
9
  from typing import Any
9
10
  from urllib.parse import urlparse
@@ -247,6 +248,9 @@ class UnitlabClient:
247
248
  num_batches = (num_files + batch_size - 1) // batch_size
248
249
  semaphore = asyncio.Semaphore(_UPLOAD_CONCURRENCY)
249
250
 
251
+ # Medical projects use session-based upload with finalize
252
+ session_id = str(uuid.uuid4()) if generic_type == "medical" else None
253
+
250
254
  async def post_file(
251
255
  client: httpx.AsyncClient, file: str, project_id: str
252
256
  ) -> int:
@@ -256,6 +260,8 @@ class UnitlabClient:
256
260
  extra_data["sentences_per_chunk"] = str(sentences_per_chunk)
257
261
  elif generic_type == "video":
258
262
  extra_data["fps"] = str(fps)
263
+ if session_id:
264
+ extra_data["session_id"] = session_id
259
265
 
260
266
  try:
261
267
  with open(file, "rb") as f:
@@ -288,14 +294,55 @@ class UnitlabClient:
288
294
  for f in asyncio.as_completed(tasks):
289
295
  pbar.update(await f)
290
296
 
297
+ # Finalize medical session after all uploads
298
+ if session_id:
299
+ response = await client.post(
300
+ f"/api/sdk/projects/{project_id}/"
301
+ f"medical-upload-sessions/{session_id}/finalize/",
302
+ )
303
+ response.raise_for_status()
304
+ logger.info("Medical session finalized")
305
+
291
306
  asyncio.run(main())
292
307
 
293
308
  def datasets(self, pretty: int = 0) -> Any:
294
309
  return self._get(f"/api/sdk/datasets/?pretty={pretty}")
295
310
 
311
+ def dataset_info(self, dataset_id: str) -> Any:
312
+ """Return metadata for a dataset.
313
+
314
+ Includes name, version, annotation_type, number_of_data,
315
+ download_formats and the list of available ``splits``.
316
+ """
317
+ return self._get(f"/api/sdk/datasets/{dataset_id}/")
318
+
296
319
  def dataset_download(
297
- self, dataset_id: str, export_type: str, split_type: str
320
+ self,
321
+ dataset_id: str,
322
+ export_type: str,
323
+ split_type: str | None = None,
298
324
  ) -> str:
325
+ if split_type is None:
326
+ info = self.dataset_info(dataset_id)
327
+ splits = info.get("splits") or []
328
+ if not splits:
329
+ raise NetworkError(
330
+ message=(
331
+ "No splits available for this dataset. Nothing to download."
332
+ ),
333
+ detail=None,
334
+ )
335
+ if len(splits) > 1:
336
+ raise NetworkError(
337
+ message=(
338
+ f"Multiple splits available: {', '.join(splits)}. "
339
+ f"Specify one with split_type=..."
340
+ ),
341
+ detail=None,
342
+ )
343
+ split_type = splits[0]
344
+ logger.info(f"Auto-selected split: {split_type}")
345
+
299
346
  response = self._post(
300
347
  f"/api/sdk/datasets/{dataset_id}/",
301
348
  data={
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes