label-studio-sdk 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

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.

Potentially problematic release.


This version of label-studio-sdk might be problematic. Click here for more details.

Files changed (67) hide show
  1. label_studio_sdk/__init__.py +20 -1
  2. label_studio_sdk/actions/client.py +8 -8
  3. label_studio_sdk/annotations/client.py +24 -24
  4. label_studio_sdk/base_client.py +3 -0
  5. label_studio_sdk/core/client_wrapper.py +1 -1
  6. label_studio_sdk/core/http_client.py +36 -8
  7. label_studio_sdk/core/request_options.py +2 -2
  8. label_studio_sdk/export_storage/__init__.py +2 -1
  9. label_studio_sdk/export_storage/azure/client.py +28 -28
  10. label_studio_sdk/export_storage/client.py +7 -4
  11. label_studio_sdk/export_storage/gcs/client.py +28 -28
  12. label_studio_sdk/export_storage/local/client.py +28 -28
  13. label_studio_sdk/export_storage/redis/client.py +28 -28
  14. label_studio_sdk/export_storage/s3/client.py +28 -28
  15. label_studio_sdk/export_storage/s3s/__init__.py +2 -0
  16. label_studio_sdk/export_storage/s3s/client.py +836 -0
  17. label_studio_sdk/files/client.py +24 -24
  18. label_studio_sdk/import_storage/__init__.py +2 -1
  19. label_studio_sdk/import_storage/azure/client.py +28 -28
  20. label_studio_sdk/import_storage/client.py +7 -4
  21. label_studio_sdk/import_storage/gcs/client.py +28 -28
  22. label_studio_sdk/import_storage/local/client.py +28 -28
  23. label_studio_sdk/import_storage/redis/client.py +28 -28
  24. label_studio_sdk/import_storage/s3/client.py +28 -28
  25. label_studio_sdk/import_storage/s3s/__init__.py +2 -0
  26. label_studio_sdk/import_storage/s3s/client.py +1054 -0
  27. label_studio_sdk/label_interface/base.py +2 -2
  28. label_studio_sdk/label_interface/control_tags.py +32 -18
  29. label_studio_sdk/label_interface/create.py +241 -0
  30. label_studio_sdk/label_interface/interface.py +68 -0
  31. label_studio_sdk/label_interface/object_tags.py +26 -10
  32. label_studio_sdk/label_interface/objects.py +5 -5
  33. label_studio_sdk/ml/client.py +36 -36
  34. label_studio_sdk/predictions/client.py +24 -24
  35. label_studio_sdk/projects/__init__.py +8 -2
  36. label_studio_sdk/projects/client.py +232 -69
  37. label_studio_sdk/projects/client_ext.py +16 -1
  38. label_studio_sdk/projects/exports/client.py +38 -38
  39. label_studio_sdk/projects/types/__init__.py +2 -1
  40. label_studio_sdk/projects/types/projects_update_response.py +96 -0
  41. label_studio_sdk/tasks/client.py +70 -60
  42. label_studio_sdk/tasks/client_ext.py +4 -0
  43. label_studio_sdk/types/__init__.py +16 -0
  44. label_studio_sdk/types/base_task.py +4 -2
  45. label_studio_sdk/types/base_task_file_upload.py +5 -0
  46. label_studio_sdk/types/base_task_updated_by.py +5 -0
  47. label_studio_sdk/types/data_manager_task_serializer.py +3 -2
  48. label_studio_sdk/types/data_manager_task_serializer_annotators_item.py +5 -0
  49. label_studio_sdk/types/s3s_export_storage.py +80 -0
  50. label_studio_sdk/types/s3s_import_storage.py +129 -0
  51. label_studio_sdk/types/s3s_import_storage_status.py +7 -0
  52. label_studio_sdk/types/task.py +3 -2
  53. label_studio_sdk/types/task_annotators_item.py +5 -0
  54. label_studio_sdk/types/workspace.py +77 -0
  55. label_studio_sdk/users/client.py +32 -32
  56. label_studio_sdk/views/client.py +24 -24
  57. label_studio_sdk/webhooks/client.py +24 -24
  58. label_studio_sdk/workspaces/__init__.py +6 -0
  59. label_studio_sdk/workspaces/client.py +569 -0
  60. label_studio_sdk/workspaces/members/__init__.py +5 -0
  61. label_studio_sdk/workspaces/members/client.py +297 -0
  62. label_studio_sdk/workspaces/members/types/__init__.py +6 -0
  63. label_studio_sdk/workspaces/members/types/members_create_response.py +32 -0
  64. label_studio_sdk/workspaces/members/types/members_list_response_item.py +32 -0
  65. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/METADATA +11 -12
  66. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/RECORD +67 -46
  67. {label_studio_sdk-1.0.2.dist-info → label_studio_sdk-1.0.4.dist-info}/WHEEL +0 -0
@@ -16,6 +16,7 @@ from .exports.client import AsyncExportsClient, ExportsClient
16
16
  from .types.projects_create_response import ProjectsCreateResponse
17
17
  from .types.projects_import_tasks_response import ProjectsImportTasksResponse
18
18
  from .types.projects_list_response import ProjectsListResponse
19
+ from .types.projects_update_response import ProjectsUpdateResponse
19
20
 
20
21
  # this is used as the default value for optional parameters
21
22
  OMIT = typing.cast(typing.Any, ...)
@@ -80,7 +81,12 @@ class ProjectsClient:
80
81
  client = LabelStudio(
81
82
  api_key="YOUR_API_KEY",
82
83
  )
83
- client.projects.list()
84
+ response = client.projects.list()
85
+ for item in response:
86
+ yield item
87
+ # alternatively, you can paginate page-by-page
88
+ for page in response.iter_pages():
89
+ yield page
84
90
  """
85
91
  page = page or 1
86
92
  _response = self._client_wrapper.httpx_client.request(
@@ -89,20 +95,20 @@ class ProjectsClient:
89
95
  params={"ordering": ordering, "ids": ids, "title": title, "page": page, "page_size": page_size},
90
96
  request_options=request_options,
91
97
  )
92
- if 200 <= _response.status_code < 300:
93
- _parsed_response = pydantic_v1.parse_obj_as(ProjectsListResponse, _response.json()) # type: ignore
94
- _has_next = True
95
- _get_next = lambda: self.list(
96
- ordering=ordering,
97
- ids=ids,
98
- title=title,
99
- page=page + 1,
100
- page_size=page_size,
101
- request_options=request_options,
102
- )
103
- _items = _parsed_response.results
104
- return SyncPager(has_next=_has_next, items=_items, get_next=_get_next)
105
98
  try:
99
+ if 200 <= _response.status_code < 300:
100
+ _parsed_response = pydantic_v1.parse_obj_as(ProjectsListResponse, _response.json()) # type: ignore
101
+ _has_next = True
102
+ _get_next = lambda: self.list(
103
+ ordering=ordering,
104
+ ids=ids,
105
+ title=title,
106
+ page=page + 1,
107
+ page_size=page_size,
108
+ request_options=request_options,
109
+ )
110
+ _items = _parsed_response.results
111
+ return SyncPager(has_next=_has_next, items=_items, get_next=_get_next)
106
112
  _response_json = _response.json()
107
113
  except JSONDecodeError:
108
114
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -124,6 +130,7 @@ class ProjectsClient:
124
130
  maximum_annotations: typing.Optional[int] = OMIT,
125
131
  color: typing.Optional[str] = OMIT,
126
132
  control_weights: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
133
+ workspace: typing.Optional[int] = OMIT,
127
134
  request_options: typing.Optional[RequestOptions] = None,
128
135
  ) -> ProjectsCreateResponse:
129
136
  """
@@ -178,6 +185,9 @@ class ProjectsClient:
178
185
  control_weights : typing.Optional[typing.Dict[str, typing.Any]]
179
186
  Dict of weights for each control tag in metric calculation. Each control tag (e.g. label or choice) will have its own key in control weight dict with weight for each label and overall weight. For example, if a bounding box annotation with a control tag named my_bbox should be included with 0.33 weight in agreement calculation, and the first label Car should be twice as important as Airplane, then you need to specify: {'my_bbox': {'type': 'RectangleLabels', 'labels': {'Car': 1.0, 'Airplane': 0.5}, 'overall': 0.33}
180
187
 
188
+ workspace : typing.Optional[int]
189
+ Workspace ID
190
+
181
191
  request_options : typing.Optional[RequestOptions]
182
192
  Request-specific configuration.
183
193
 
@@ -212,13 +222,14 @@ class ProjectsClient:
212
222
  "maximum_annotations": maximum_annotations,
213
223
  "color": color,
214
224
  "control_weights": control_weights,
225
+ "workspace": workspace,
215
226
  },
216
227
  request_options=request_options,
217
228
  omit=OMIT,
218
229
  )
219
- if 200 <= _response.status_code < 300:
220
- return pydantic_v1.parse_obj_as(ProjectsCreateResponse, _response.json()) # type: ignore
221
230
  try:
231
+ if 200 <= _response.status_code < 300:
232
+ return pydantic_v1.parse_obj_as(ProjectsCreateResponse, _response.json()) # type: ignore
222
233
  _response_json = _response.json()
223
234
  except JSONDecodeError:
224
235
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -255,9 +266,9 @@ class ProjectsClient:
255
266
  _response = self._client_wrapper.httpx_client.request(
256
267
  f"api/projects/{jsonable_encoder(id)}/", method="GET", request_options=request_options
257
268
  )
258
- if 200 <= _response.status_code < 300:
259
- return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
260
269
  try:
270
+ if 200 <= _response.status_code < 300:
271
+ return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
261
272
  _response_json = _response.json()
262
273
  except JSONDecodeError:
263
274
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -295,15 +306,34 @@ class ProjectsClient:
295
306
  _response = self._client_wrapper.httpx_client.request(
296
307
  f"api/projects/{jsonable_encoder(id)}/", method="DELETE", request_options=request_options
297
308
  )
298
- if 200 <= _response.status_code < 300:
299
- return
300
309
  try:
310
+ if 200 <= _response.status_code < 300:
311
+ return
301
312
  _response_json = _response.json()
302
313
  except JSONDecodeError:
303
314
  raise ApiError(status_code=_response.status_code, body=_response.text)
304
315
  raise ApiError(status_code=_response.status_code, body=_response_json)
305
316
 
306
- def update(self, id: int, *, request: Project, request_options: typing.Optional[RequestOptions] = None) -> Project:
317
+ def update(
318
+ self,
319
+ id: int,
320
+ *,
321
+ title: typing.Optional[str] = OMIT,
322
+ description: typing.Optional[str] = OMIT,
323
+ label_config: typing.Optional[str] = OMIT,
324
+ expert_instruction: typing.Optional[str] = OMIT,
325
+ show_instruction: typing.Optional[bool] = OMIT,
326
+ show_skip_button: typing.Optional[bool] = OMIT,
327
+ enable_empty_annotation: typing.Optional[bool] = OMIT,
328
+ show_annotation_history: typing.Optional[bool] = OMIT,
329
+ reveal_preannotations_interactively: typing.Optional[bool] = OMIT,
330
+ show_collab_predictions: typing.Optional[bool] = OMIT,
331
+ maximum_annotations: typing.Optional[int] = OMIT,
332
+ color: typing.Optional[str] = OMIT,
333
+ control_weights: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
334
+ workspace: typing.Optional[int] = OMIT,
335
+ request_options: typing.Optional[RequestOptions] = None,
336
+ ) -> ProjectsUpdateResponse:
307
337
  """
308
338
  Update the project settings for a specific project. For more information, see the following:
309
339
 
@@ -324,19 +354,58 @@ class ProjectsClient:
324
354
  id : int
325
355
  A unique integer value identifying this project.
326
356
 
327
- request : Project
357
+ title : typing.Optional[str]
358
+ Project title
359
+
360
+ description : typing.Optional[str]
361
+ Project description
362
+
363
+ label_config : typing.Optional[str]
364
+ Label config in XML format
365
+
366
+ expert_instruction : typing.Optional[str]
367
+ Labeling instructions to show to the user
368
+
369
+ show_instruction : typing.Optional[bool]
370
+ Show labeling instructions
371
+
372
+ show_skip_button : typing.Optional[bool]
373
+ Show skip button
374
+
375
+ enable_empty_annotation : typing.Optional[bool]
376
+ Allow empty annotations
377
+
378
+ show_annotation_history : typing.Optional[bool]
379
+ Show annotation history
380
+
381
+ reveal_preannotations_interactively : typing.Optional[bool]
382
+ Reveal preannotations interactively. If set to True, predictions will be shown to the user only after selecting the area of interest
383
+
384
+ show_collab_predictions : typing.Optional[bool]
385
+ Show predictions to annotators
386
+
387
+ maximum_annotations : typing.Optional[int]
388
+ Maximum annotations per task
389
+
390
+ color : typing.Optional[str]
391
+ Project color in HEX format
392
+
393
+ control_weights : typing.Optional[typing.Dict[str, typing.Any]]
394
+ Dict of weights for each control tag in metric calculation. Each control tag (e.g. label or choice) will have its own key in control weight dict with weight for each label and overall weight. For example, if a bounding box annotation with a control tag named my_bbox should be included with 0.33 weight in agreement calculation, and the first label Car should be twice as important as Airplane, then you need to specify: {'my_bbox': {'type': 'RectangleLabels', 'labels': {'Car': 1.0, 'Airplane': 0.5}, 'overall': 0.33}
395
+
396
+ workspace : typing.Optional[int]
397
+ Workspace ID
328
398
 
329
399
  request_options : typing.Optional[RequestOptions]
330
400
  Request-specific configuration.
331
401
 
332
402
  Returns
333
403
  -------
334
- Project
404
+ ProjectsUpdateResponse
335
405
 
336
406
 
337
407
  Examples
338
408
  --------
339
- from label_studio_sdk import Project
340
409
  from label_studio_sdk.client import LabelStudio
341
410
 
342
411
  client = LabelStudio(
@@ -344,19 +413,33 @@ class ProjectsClient:
344
413
  )
345
414
  client.projects.update(
346
415
  id=1,
347
- request=Project(),
348
416
  )
349
417
  """
350
418
  _response = self._client_wrapper.httpx_client.request(
351
419
  f"api/projects/{jsonable_encoder(id)}/",
352
420
  method="PATCH",
353
- json=request,
421
+ json={
422
+ "title": title,
423
+ "description": description,
424
+ "label_config": label_config,
425
+ "expert_instruction": expert_instruction,
426
+ "show_instruction": show_instruction,
427
+ "show_skip_button": show_skip_button,
428
+ "enable_empty_annotation": enable_empty_annotation,
429
+ "show_annotation_history": show_annotation_history,
430
+ "reveal_preannotations_interactively": reveal_preannotations_interactively,
431
+ "show_collab_predictions": show_collab_predictions,
432
+ "maximum_annotations": maximum_annotations,
433
+ "color": color,
434
+ "control_weights": control_weights,
435
+ "workspace": workspace,
436
+ },
354
437
  request_options=request_options,
355
438
  omit=OMIT,
356
439
  )
357
- if 200 <= _response.status_code < 300:
358
- return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
359
440
  try:
441
+ if 200 <= _response.status_code < 300:
442
+ return pydantic_v1.parse_obj_as(ProjectsUpdateResponse, _response.json()) # type: ignore
360
443
  _response_json = _response.json()
361
444
  except JSONDecodeError:
362
445
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -473,11 +556,11 @@ class ProjectsClient:
473
556
  request_options=request_options,
474
557
  omit=OMIT,
475
558
  )
476
- if 200 <= _response.status_code < 300:
477
- return pydantic_v1.parse_obj_as(ProjectsImportTasksResponse, _response.json()) # type: ignore
478
- if _response.status_code == 400:
479
- raise BadRequestError(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
480
559
  try:
560
+ if 200 <= _response.status_code < 300:
561
+ return pydantic_v1.parse_obj_as(ProjectsImportTasksResponse, _response.json()) # type: ignore
562
+ if _response.status_code == 400:
563
+ raise BadRequestError(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
481
564
  _response_json = _response.json()
482
565
  except JSONDecodeError:
483
566
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -528,9 +611,9 @@ class ProjectsClient:
528
611
  request_options=request_options,
529
612
  omit=OMIT,
530
613
  )
531
- if 200 <= _response.status_code < 300:
532
- return pydantic_v1.parse_obj_as(ProjectLabelConfig, _response.json()) # type: ignore
533
614
  try:
615
+ if 200 <= _response.status_code < 300:
616
+ return pydantic_v1.parse_obj_as(ProjectLabelConfig, _response.json()) # type: ignore
534
617
  _response_json = _response.json()
535
618
  except JSONDecodeError:
536
619
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -596,7 +679,12 @@ class AsyncProjectsClient:
596
679
  client = AsyncLabelStudio(
597
680
  api_key="YOUR_API_KEY",
598
681
  )
599
- await client.projects.list()
682
+ response = await client.projects.list()
683
+ async for item in response:
684
+ yield item
685
+ # alternatively, you can paginate page-by-page
686
+ async for page in response.iter_pages():
687
+ yield page
600
688
  """
601
689
  page = page or 1
602
690
  _response = await self._client_wrapper.httpx_client.request(
@@ -605,20 +693,20 @@ class AsyncProjectsClient:
605
693
  params={"ordering": ordering, "ids": ids, "title": title, "page": page, "page_size": page_size},
606
694
  request_options=request_options,
607
695
  )
608
- if 200 <= _response.status_code < 300:
609
- _parsed_response = pydantic_v1.parse_obj_as(ProjectsListResponse, _response.json()) # type: ignore
610
- _has_next = True
611
- _get_next = lambda: self.list(
612
- ordering=ordering,
613
- ids=ids,
614
- title=title,
615
- page=page + 1,
616
- page_size=page_size,
617
- request_options=request_options,
618
- )
619
- _items = _parsed_response.results
620
- return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next)
621
696
  try:
697
+ if 200 <= _response.status_code < 300:
698
+ _parsed_response = pydantic_v1.parse_obj_as(ProjectsListResponse, _response.json()) # type: ignore
699
+ _has_next = True
700
+ _get_next = lambda: self.list(
701
+ ordering=ordering,
702
+ ids=ids,
703
+ title=title,
704
+ page=page + 1,
705
+ page_size=page_size,
706
+ request_options=request_options,
707
+ )
708
+ _items = _parsed_response.results
709
+ return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next)
622
710
  _response_json = _response.json()
623
711
  except JSONDecodeError:
624
712
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -640,6 +728,7 @@ class AsyncProjectsClient:
640
728
  maximum_annotations: typing.Optional[int] = OMIT,
641
729
  color: typing.Optional[str] = OMIT,
642
730
  control_weights: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
731
+ workspace: typing.Optional[int] = OMIT,
643
732
  request_options: typing.Optional[RequestOptions] = None,
644
733
  ) -> ProjectsCreateResponse:
645
734
  """
@@ -694,6 +783,9 @@ class AsyncProjectsClient:
694
783
  control_weights : typing.Optional[typing.Dict[str, typing.Any]]
695
784
  Dict of weights for each control tag in metric calculation. Each control tag (e.g. label or choice) will have its own key in control weight dict with weight for each label and overall weight. For example, if a bounding box annotation with a control tag named my_bbox should be included with 0.33 weight in agreement calculation, and the first label Car should be twice as important as Airplane, then you need to specify: {'my_bbox': {'type': 'RectangleLabels', 'labels': {'Car': 1.0, 'Airplane': 0.5}, 'overall': 0.33}
696
785
 
786
+ workspace : typing.Optional[int]
787
+ Workspace ID
788
+
697
789
  request_options : typing.Optional[RequestOptions]
698
790
  Request-specific configuration.
699
791
 
@@ -728,13 +820,14 @@ class AsyncProjectsClient:
728
820
  "maximum_annotations": maximum_annotations,
729
821
  "color": color,
730
822
  "control_weights": control_weights,
823
+ "workspace": workspace,
731
824
  },
732
825
  request_options=request_options,
733
826
  omit=OMIT,
734
827
  )
735
- if 200 <= _response.status_code < 300:
736
- return pydantic_v1.parse_obj_as(ProjectsCreateResponse, _response.json()) # type: ignore
737
828
  try:
829
+ if 200 <= _response.status_code < 300:
830
+ return pydantic_v1.parse_obj_as(ProjectsCreateResponse, _response.json()) # type: ignore
738
831
  _response_json = _response.json()
739
832
  except JSONDecodeError:
740
833
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -771,9 +864,9 @@ class AsyncProjectsClient:
771
864
  _response = await self._client_wrapper.httpx_client.request(
772
865
  f"api/projects/{jsonable_encoder(id)}/", method="GET", request_options=request_options
773
866
  )
774
- if 200 <= _response.status_code < 300:
775
- return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
776
867
  try:
868
+ if 200 <= _response.status_code < 300:
869
+ return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
777
870
  _response_json = _response.json()
778
871
  except JSONDecodeError:
779
872
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -811,17 +904,34 @@ class AsyncProjectsClient:
811
904
  _response = await self._client_wrapper.httpx_client.request(
812
905
  f"api/projects/{jsonable_encoder(id)}/", method="DELETE", request_options=request_options
813
906
  )
814
- if 200 <= _response.status_code < 300:
815
- return
816
907
  try:
908
+ if 200 <= _response.status_code < 300:
909
+ return
817
910
  _response_json = _response.json()
818
911
  except JSONDecodeError:
819
912
  raise ApiError(status_code=_response.status_code, body=_response.text)
820
913
  raise ApiError(status_code=_response.status_code, body=_response_json)
821
914
 
822
915
  async def update(
823
- self, id: int, *, request: Project, request_options: typing.Optional[RequestOptions] = None
824
- ) -> Project:
916
+ self,
917
+ id: int,
918
+ *,
919
+ title: typing.Optional[str] = OMIT,
920
+ description: typing.Optional[str] = OMIT,
921
+ label_config: typing.Optional[str] = OMIT,
922
+ expert_instruction: typing.Optional[str] = OMIT,
923
+ show_instruction: typing.Optional[bool] = OMIT,
924
+ show_skip_button: typing.Optional[bool] = OMIT,
925
+ enable_empty_annotation: typing.Optional[bool] = OMIT,
926
+ show_annotation_history: typing.Optional[bool] = OMIT,
927
+ reveal_preannotations_interactively: typing.Optional[bool] = OMIT,
928
+ show_collab_predictions: typing.Optional[bool] = OMIT,
929
+ maximum_annotations: typing.Optional[int] = OMIT,
930
+ color: typing.Optional[str] = OMIT,
931
+ control_weights: typing.Optional[typing.Dict[str, typing.Any]] = OMIT,
932
+ workspace: typing.Optional[int] = OMIT,
933
+ request_options: typing.Optional[RequestOptions] = None,
934
+ ) -> ProjectsUpdateResponse:
825
935
  """
826
936
  Update the project settings for a specific project. For more information, see the following:
827
937
 
@@ -842,19 +952,58 @@ class AsyncProjectsClient:
842
952
  id : int
843
953
  A unique integer value identifying this project.
844
954
 
845
- request : Project
955
+ title : typing.Optional[str]
956
+ Project title
957
+
958
+ description : typing.Optional[str]
959
+ Project description
960
+
961
+ label_config : typing.Optional[str]
962
+ Label config in XML format
963
+
964
+ expert_instruction : typing.Optional[str]
965
+ Labeling instructions to show to the user
966
+
967
+ show_instruction : typing.Optional[bool]
968
+ Show labeling instructions
969
+
970
+ show_skip_button : typing.Optional[bool]
971
+ Show skip button
972
+
973
+ enable_empty_annotation : typing.Optional[bool]
974
+ Allow empty annotations
975
+
976
+ show_annotation_history : typing.Optional[bool]
977
+ Show annotation history
978
+
979
+ reveal_preannotations_interactively : typing.Optional[bool]
980
+ Reveal preannotations interactively. If set to True, predictions will be shown to the user only after selecting the area of interest
981
+
982
+ show_collab_predictions : typing.Optional[bool]
983
+ Show predictions to annotators
984
+
985
+ maximum_annotations : typing.Optional[int]
986
+ Maximum annotations per task
987
+
988
+ color : typing.Optional[str]
989
+ Project color in HEX format
990
+
991
+ control_weights : typing.Optional[typing.Dict[str, typing.Any]]
992
+ Dict of weights for each control tag in metric calculation. Each control tag (e.g. label or choice) will have its own key in control weight dict with weight for each label and overall weight. For example, if a bounding box annotation with a control tag named my_bbox should be included with 0.33 weight in agreement calculation, and the first label Car should be twice as important as Airplane, then you need to specify: {'my_bbox': {'type': 'RectangleLabels', 'labels': {'Car': 1.0, 'Airplane': 0.5}, 'overall': 0.33}
993
+
994
+ workspace : typing.Optional[int]
995
+ Workspace ID
846
996
 
847
997
  request_options : typing.Optional[RequestOptions]
848
998
  Request-specific configuration.
849
999
 
850
1000
  Returns
851
1001
  -------
852
- Project
1002
+ ProjectsUpdateResponse
853
1003
 
854
1004
 
855
1005
  Examples
856
1006
  --------
857
- from label_studio_sdk import Project
858
1007
  from label_studio_sdk.client import AsyncLabelStudio
859
1008
 
860
1009
  client = AsyncLabelStudio(
@@ -862,19 +1011,33 @@ class AsyncProjectsClient:
862
1011
  )
863
1012
  await client.projects.update(
864
1013
  id=1,
865
- request=Project(),
866
1014
  )
867
1015
  """
868
1016
  _response = await self._client_wrapper.httpx_client.request(
869
1017
  f"api/projects/{jsonable_encoder(id)}/",
870
1018
  method="PATCH",
871
- json=request,
1019
+ json={
1020
+ "title": title,
1021
+ "description": description,
1022
+ "label_config": label_config,
1023
+ "expert_instruction": expert_instruction,
1024
+ "show_instruction": show_instruction,
1025
+ "show_skip_button": show_skip_button,
1026
+ "enable_empty_annotation": enable_empty_annotation,
1027
+ "show_annotation_history": show_annotation_history,
1028
+ "reveal_preannotations_interactively": reveal_preannotations_interactively,
1029
+ "show_collab_predictions": show_collab_predictions,
1030
+ "maximum_annotations": maximum_annotations,
1031
+ "color": color,
1032
+ "control_weights": control_weights,
1033
+ "workspace": workspace,
1034
+ },
872
1035
  request_options=request_options,
873
1036
  omit=OMIT,
874
1037
  )
875
- if 200 <= _response.status_code < 300:
876
- return pydantic_v1.parse_obj_as(Project, _response.json()) # type: ignore
877
1038
  try:
1039
+ if 200 <= _response.status_code < 300:
1040
+ return pydantic_v1.parse_obj_as(ProjectsUpdateResponse, _response.json()) # type: ignore
878
1041
  _response_json = _response.json()
879
1042
  except JSONDecodeError:
880
1043
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -991,11 +1154,11 @@ class AsyncProjectsClient:
991
1154
  request_options=request_options,
992
1155
  omit=OMIT,
993
1156
  )
994
- if 200 <= _response.status_code < 300:
995
- return pydantic_v1.parse_obj_as(ProjectsImportTasksResponse, _response.json()) # type: ignore
996
- if _response.status_code == 400:
997
- raise BadRequestError(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
998
1157
  try:
1158
+ if 200 <= _response.status_code < 300:
1159
+ return pydantic_v1.parse_obj_as(ProjectsImportTasksResponse, _response.json()) # type: ignore
1160
+ if _response.status_code == 400:
1161
+ raise BadRequestError(pydantic_v1.parse_obj_as(typing.Any, _response.json())) # type: ignore
999
1162
  _response_json = _response.json()
1000
1163
  except JSONDecodeError:
1001
1164
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -1046,9 +1209,9 @@ class AsyncProjectsClient:
1046
1209
  request_options=request_options,
1047
1210
  omit=OMIT,
1048
1211
  )
1049
- if 200 <= _response.status_code < 300:
1050
- return pydantic_v1.parse_obj_as(ProjectLabelConfig, _response.json()) # type: ignore
1051
1212
  try:
1213
+ if 200 <= _response.status_code < 300:
1214
+ return pydantic_v1.parse_obj_as(ProjectLabelConfig, _response.json()) # type: ignore
1052
1215
  _response_json = _response.json()
1053
1216
  except JSONDecodeError:
1054
1217
  raise ApiError(status_code=_response.status_code, body=_response.text)
@@ -1,6 +1,18 @@
1
+ import typing
2
+ from typing_extensions import Annotated
1
3
  from .client import ProjectsClient, AsyncProjectsClient
2
-
4
+ from pydantic import model_validator, validator, Field, ConfigDict
3
5
  from label_studio_sdk._extensions.pager_ext import SyncPagerExt, AsyncPagerExt, T
6
+ from label_studio_sdk.types.project import Project
7
+ from label_studio_sdk.label_interface import LabelInterface
8
+
9
+ from ..core import RequestOptions
10
+
11
+
12
+ class ProjectExt(Project):
13
+
14
+ def get_label_interface(self):
15
+ return LabelInterface(self.label_config)
4
16
 
5
17
 
6
18
  class ProjectsClientExt(ProjectsClient):
@@ -10,6 +22,9 @@ class ProjectsClientExt(ProjectsClient):
10
22
 
11
23
  list.__doc__ = ProjectsClient.list.__doc__
12
24
 
25
+ def get(self, id: int, *, request_options: typing.Optional[RequestOptions] = None) -> ProjectExt:
26
+ return ProjectExt(**dict(super().get(id, request_options=request_options)))
27
+
13
28
 
14
29
  class AsyncProjectsClientExt(AsyncProjectsClient):
15
30