label-studio-sdk 1.0.5__py3-none-any.whl → 1.0.8__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.
- label_studio_sdk/__init__.py +76 -0
- label_studio_sdk/_extensions/eval/categorical.py +83 -0
- label_studio_sdk/_extensions/label_studio_tools/core/label_config.py +13 -4
- label_studio_sdk/_extensions/label_studio_tools/core/utils/io.py +35 -17
- label_studio_sdk/_extensions/label_studio_tools/core/utils/json_schema.py +86 -0
- label_studio_sdk/_legacy/schema/label_config_schema.json +42 -11
- label_studio_sdk/annotations/__init__.py +3 -0
- label_studio_sdk/annotations/client.py +109 -0
- label_studio_sdk/annotations/types/__init__.py +5 -0
- label_studio_sdk/annotations/types/annotations_create_bulk_response_item.py +29 -0
- label_studio_sdk/base_client.py +9 -0
- label_studio_sdk/comments/__init__.py +2 -0
- label_studio_sdk/comments/client.py +512 -0
- label_studio_sdk/converter/converter.py +11 -4
- label_studio_sdk/converter/imports/coco.py +14 -13
- label_studio_sdk/converter/utils.py +72 -3
- label_studio_sdk/core/client_wrapper.py +1 -1
- label_studio_sdk/files/client.py +26 -16
- label_studio_sdk/label_interface/control_tags.py +205 -10
- label_studio_sdk/label_interface/interface.py +117 -10
- label_studio_sdk/label_interface/region.py +1 -10
- label_studio_sdk/model_providers/__init__.py +2 -0
- label_studio_sdk/model_providers/client.py +708 -0
- label_studio_sdk/projects/client.py +32 -16
- label_studio_sdk/projects/exports/client.py +133 -40
- label_studio_sdk/prompts/__init__.py +21 -0
- label_studio_sdk/prompts/client.py +862 -0
- label_studio_sdk/prompts/indicators/__init__.py +2 -0
- label_studio_sdk/prompts/indicators/client.py +194 -0
- label_studio_sdk/prompts/runs/__init__.py +5 -0
- label_studio_sdk/prompts/runs/client.py +354 -0
- label_studio_sdk/prompts/runs/types/__init__.py +5 -0
- label_studio_sdk/prompts/runs/types/runs_list_request_project_subset.py +5 -0
- label_studio_sdk/prompts/types/__init__.py +15 -0
- label_studio_sdk/prompts/types/prompts_batch_failed_predictions_request_failed_predictions_item.py +42 -0
- label_studio_sdk/prompts/types/prompts_batch_failed_predictions_response.py +29 -0
- label_studio_sdk/prompts/types/prompts_batch_predictions_request_results_item.py +62 -0
- label_studio_sdk/prompts/types/prompts_batch_predictions_response.py +29 -0
- label_studio_sdk/prompts/versions/__init__.py +2 -0
- label_studio_sdk/prompts/versions/client.py +1046 -0
- label_studio_sdk/types/__init__.py +58 -0
- label_studio_sdk/types/comment.py +39 -0
- label_studio_sdk/types/comment_created_by.py +5 -0
- label_studio_sdk/types/inference_run.py +43 -0
- label_studio_sdk/types/inference_run_cost_estimate.py +57 -0
- label_studio_sdk/types/inference_run_created_by.py +5 -0
- label_studio_sdk/types/inference_run_organization.py +5 -0
- label_studio_sdk/types/inference_run_project_subset.py +5 -0
- label_studio_sdk/types/inference_run_status.py +7 -0
- label_studio_sdk/types/key_indicator_value.py +30 -0
- label_studio_sdk/types/key_indicators.py +7 -0
- label_studio_sdk/types/key_indicators_item.py +51 -0
- label_studio_sdk/types/key_indicators_item_additional_kpis_item.py +37 -0
- label_studio_sdk/types/key_indicators_item_extra_kpis_item.py +37 -0
- label_studio_sdk/types/model_provider_connection.py +71 -0
- label_studio_sdk/types/model_provider_connection_budget_reset_period.py +5 -0
- label_studio_sdk/types/model_provider_connection_created_by.py +5 -0
- label_studio_sdk/types/model_provider_connection_organization.py +5 -0
- label_studio_sdk/types/model_provider_connection_provider.py +5 -0
- label_studio_sdk/types/model_provider_connection_scope.py +5 -0
- label_studio_sdk/types/prompt.py +79 -0
- label_studio_sdk/types/prompt_created_by.py +5 -0
- label_studio_sdk/types/prompt_organization.py +5 -0
- label_studio_sdk/types/prompt_version.py +41 -0
- label_studio_sdk/types/prompt_version_created_by.py +5 -0
- label_studio_sdk/types/prompt_version_organization.py +5 -0
- label_studio_sdk/types/prompt_version_provider.py +5 -0
- label_studio_sdk/types/refined_prompt_response.py +64 -0
- label_studio_sdk/types/refined_prompt_response_refinement_status.py +7 -0
- label_studio_sdk/types/task.py +3 -2
- label_studio_sdk/types/task_comment_authors_item.py +5 -0
- label_studio_sdk/webhooks/client.py +245 -36
- label_studio_sdk/workspaces/client.py +20 -20
- label_studio_sdk-1.0.8.dist-info/LICENSE +201 -0
- {label_studio_sdk-1.0.5.dist-info → label_studio_sdk-1.0.8.dist-info}/METADATA +19 -3
- {label_studio_sdk-1.0.5.dist-info → label_studio_sdk-1.0.8.dist-info}/RECORD +77 -24
- {label_studio_sdk-1.0.5.dist-info → label_studio_sdk-1.0.8.dist-info}/WHEEL +1 -1
|
@@ -17,10 +17,11 @@ from urllib.parse import urlparse
|
|
|
17
17
|
import numpy as np
|
|
18
18
|
import requests
|
|
19
19
|
from PIL import Image
|
|
20
|
-
from label_studio_sdk._extensions.label_studio_tools.core.utils.params import get_env
|
|
21
20
|
from lxml import etree
|
|
22
21
|
from nltk.tokenize.treebank import TreebankWordTokenizer
|
|
23
22
|
|
|
23
|
+
from label_studio_sdk._extensions.label_studio_tools.core.utils.params import get_env
|
|
24
|
+
|
|
24
25
|
logger = logging.getLogger(__name__)
|
|
25
26
|
|
|
26
27
|
_LABEL_TAGS = {"Label", "Choice"}
|
|
@@ -422,7 +423,7 @@ def convert_annotation_to_yolo(label):
|
|
|
422
423
|
return x, y, w, h
|
|
423
424
|
|
|
424
425
|
|
|
425
|
-
def convert_annotation_to_yolo_obb(label):
|
|
426
|
+
def convert_annotation_to_yolo_obb(label, normalize=True):
|
|
426
427
|
"""
|
|
427
428
|
Convert LS annotation to Yolo OBB format.
|
|
428
429
|
|
|
@@ -435,6 +436,7 @@ def convert_annotation_to_yolo_obb(label):
|
|
|
435
436
|
- width (float): Width of the object in percentage of the original width.
|
|
436
437
|
- height (float): Height of the object in percentage of the original height.
|
|
437
438
|
- rotation (float, optional): Rotation angle of the object in degrees (default is 0).
|
|
439
|
+
normalize (bool, optional): Whether to normalize the coordinates to the range [0, 1] (default is True).
|
|
438
440
|
|
|
439
441
|
Returns:
|
|
440
442
|
list of tuple or None: List of tuples containing the coordinates of the object in Yolo OBB format.
|
|
@@ -470,4 +472,71 @@ def convert_annotation_to_yolo_obb(label):
|
|
|
470
472
|
]
|
|
471
473
|
|
|
472
474
|
# Normalize coordinates
|
|
473
|
-
|
|
475
|
+
if normalize:
|
|
476
|
+
return [(coord[0] / org_width, coord[1] / org_height) for coord in coords]
|
|
477
|
+
else:
|
|
478
|
+
return coords
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def convert_yolo_obb_to_annotation(xyxyxyxy, original_width, original_height):
|
|
482
|
+
"""
|
|
483
|
+
Convert YOLO Oriented Bounding Box (OBB) format to Label Studio format.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
xyxyxyxy (list): List of 8 float values representing the absolute pixel coordinates
|
|
487
|
+
of the OBB in the format [x1, y1, x2, y2, x3, y3, x4, y4].
|
|
488
|
+
original_width (int): Original width of the image.
|
|
489
|
+
original_height (int): Original height of the image.
|
|
490
|
+
|
|
491
|
+
Returns:
|
|
492
|
+
dict: Dictionary containing the converted bounding box with the following keys:
|
|
493
|
+
- x: X-coordinate of the top-left corner of the bounding box in percentage.
|
|
494
|
+
- y: Y-coordinate of the top-left corner of the bounding box in percentage.
|
|
495
|
+
- width: Width of the bounding box in percentage.
|
|
496
|
+
- height: Height of the bounding box in percentage.
|
|
497
|
+
- rotation: Rotation angle of the bounding box in degrees.
|
|
498
|
+
"""
|
|
499
|
+
# Reshape the coordinates into a 4x2 matrix
|
|
500
|
+
coords = np.array(xyxyxyxy, dtype=np.float64).reshape((4, 2))
|
|
501
|
+
|
|
502
|
+
# Calculate the center of the bounding box
|
|
503
|
+
center_x = np.mean(coords[:, 0])
|
|
504
|
+
center_y = np.mean(coords[:, 1])
|
|
505
|
+
|
|
506
|
+
# Calculate the width and height of the bounding box
|
|
507
|
+
width = np.linalg.norm(coords[0] - coords[1])
|
|
508
|
+
height = np.linalg.norm(coords[0] - coords[3])
|
|
509
|
+
|
|
510
|
+
# Calculate the rotation angle
|
|
511
|
+
dx = coords[1, 0] - coords[0, 0]
|
|
512
|
+
dy = coords[1, 1] - coords[0, 1]
|
|
513
|
+
r = np.degrees(np.arctan2(dy, dx))
|
|
514
|
+
|
|
515
|
+
# Find the top-left corner (x, y)
|
|
516
|
+
top_left_x = (
|
|
517
|
+
center_x
|
|
518
|
+
- (width / 2) * np.cos(np.radians(r))
|
|
519
|
+
+ (height / 2) * np.sin(np.radians(r))
|
|
520
|
+
)
|
|
521
|
+
top_left_y = (
|
|
522
|
+
center_y
|
|
523
|
+
- (width / 2) * np.sin(np.radians(r))
|
|
524
|
+
- (height / 2) * np.cos(np.radians(r))
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
# Normalize the values
|
|
528
|
+
x = (top_left_x / original_width) * 100
|
|
529
|
+
y = (top_left_y / original_height) * 100
|
|
530
|
+
width = (width / original_width) * 100
|
|
531
|
+
height = (height / original_height) * 100
|
|
532
|
+
|
|
533
|
+
# Create the dictionary for Label Studio
|
|
534
|
+
return {
|
|
535
|
+
"x": x,
|
|
536
|
+
"y": y,
|
|
537
|
+
"width": width,
|
|
538
|
+
"height": height,
|
|
539
|
+
"rotation": r,
|
|
540
|
+
"original_width": original_width,
|
|
541
|
+
"original_height": original_height,
|
|
542
|
+
}
|
|
@@ -17,7 +17,7 @@ class BaseClientWrapper:
|
|
|
17
17
|
headers: typing.Dict[str, str] = {
|
|
18
18
|
"X-Fern-Language": "Python",
|
|
19
19
|
"X-Fern-SDK-Name": "label-studio-sdk",
|
|
20
|
-
"X-Fern-SDK-Version": "1.0.
|
|
20
|
+
"X-Fern-SDK-Version": "1.0.8",
|
|
21
21
|
}
|
|
22
22
|
headers["Authorization"] = f"Token {self.api_key}"
|
|
23
23
|
return headers
|
label_studio_sdk/files/client.py
CHANGED
|
@@ -96,7 +96,12 @@ class FilesClient:
|
|
|
96
96
|
raise ApiError(status_code=_response.status_code, body=_response_json)
|
|
97
97
|
|
|
98
98
|
def update(
|
|
99
|
-
self,
|
|
99
|
+
self,
|
|
100
|
+
id_: int,
|
|
101
|
+
*,
|
|
102
|
+
id: typing.Optional[int] = OMIT,
|
|
103
|
+
file: typing.Optional[str] = OMIT,
|
|
104
|
+
request_options: typing.Optional[RequestOptions] = None,
|
|
100
105
|
) -> FileUpload:
|
|
101
106
|
"""
|
|
102
107
|
Update a specific uploaded file. To get the file upload ID, use [Get files list](list).
|
|
@@ -109,10 +114,12 @@ class FilesClient:
|
|
|
109
114
|
|
|
110
115
|
Parameters
|
|
111
116
|
----------
|
|
112
|
-
|
|
117
|
+
id_ : int
|
|
113
118
|
A unique integer value identifying this file upload.
|
|
114
119
|
|
|
115
|
-
|
|
120
|
+
id : typing.Optional[int]
|
|
121
|
+
|
|
122
|
+
file : typing.Optional[str]
|
|
116
123
|
|
|
117
124
|
request_options : typing.Optional[RequestOptions]
|
|
118
125
|
Request-specific configuration.
|
|
@@ -124,21 +131,19 @@ class FilesClient:
|
|
|
124
131
|
|
|
125
132
|
Examples
|
|
126
133
|
--------
|
|
127
|
-
from label_studio_sdk import FileUpload
|
|
128
134
|
from label_studio_sdk.client import LabelStudio
|
|
129
135
|
|
|
130
136
|
client = LabelStudio(
|
|
131
137
|
api_key="YOUR_API_KEY",
|
|
132
138
|
)
|
|
133
139
|
client.files.update(
|
|
134
|
-
|
|
135
|
-
request=FileUpload(),
|
|
140
|
+
id_=1,
|
|
136
141
|
)
|
|
137
142
|
"""
|
|
138
143
|
_response = self._client_wrapper.httpx_client.request(
|
|
139
|
-
f"api/import/file-upload/{jsonable_encoder(
|
|
144
|
+
f"api/import/file-upload/{jsonable_encoder(id_)}",
|
|
140
145
|
method="PATCH",
|
|
141
|
-
json=
|
|
146
|
+
json={"id": id, "file": file},
|
|
142
147
|
request_options=request_options,
|
|
143
148
|
omit=OMIT,
|
|
144
149
|
)
|
|
@@ -367,7 +372,12 @@ class AsyncFilesClient:
|
|
|
367
372
|
raise ApiError(status_code=_response.status_code, body=_response_json)
|
|
368
373
|
|
|
369
374
|
async def update(
|
|
370
|
-
self,
|
|
375
|
+
self,
|
|
376
|
+
id_: int,
|
|
377
|
+
*,
|
|
378
|
+
id: typing.Optional[int] = OMIT,
|
|
379
|
+
file: typing.Optional[str] = OMIT,
|
|
380
|
+
request_options: typing.Optional[RequestOptions] = None,
|
|
371
381
|
) -> FileUpload:
|
|
372
382
|
"""
|
|
373
383
|
Update a specific uploaded file. To get the file upload ID, use [Get files list](list).
|
|
@@ -380,10 +390,12 @@ class AsyncFilesClient:
|
|
|
380
390
|
|
|
381
391
|
Parameters
|
|
382
392
|
----------
|
|
383
|
-
|
|
393
|
+
id_ : int
|
|
384
394
|
A unique integer value identifying this file upload.
|
|
385
395
|
|
|
386
|
-
|
|
396
|
+
id : typing.Optional[int]
|
|
397
|
+
|
|
398
|
+
file : typing.Optional[str]
|
|
387
399
|
|
|
388
400
|
request_options : typing.Optional[RequestOptions]
|
|
389
401
|
Request-specific configuration.
|
|
@@ -395,21 +407,19 @@ class AsyncFilesClient:
|
|
|
395
407
|
|
|
396
408
|
Examples
|
|
397
409
|
--------
|
|
398
|
-
from label_studio_sdk import FileUpload
|
|
399
410
|
from label_studio_sdk.client import AsyncLabelStudio
|
|
400
411
|
|
|
401
412
|
client = AsyncLabelStudio(
|
|
402
413
|
api_key="YOUR_API_KEY",
|
|
403
414
|
)
|
|
404
415
|
await client.files.update(
|
|
405
|
-
|
|
406
|
-
request=FileUpload(),
|
|
416
|
+
id_=1,
|
|
407
417
|
)
|
|
408
418
|
"""
|
|
409
419
|
_response = await self._client_wrapper.httpx_client.request(
|
|
410
|
-
f"api/import/file-upload/{jsonable_encoder(
|
|
420
|
+
f"api/import/file-upload/{jsonable_encoder(id_)}",
|
|
411
421
|
method="PATCH",
|
|
412
|
-
json=
|
|
422
|
+
json={"id": id, "file": file},
|
|
413
423
|
request_options=request_options,
|
|
414
424
|
omit=OMIT,
|
|
415
425
|
)
|
|
@@ -93,6 +93,15 @@ class ControlTag(LabelStudioTag):
|
|
|
93
93
|
and tag.tag not in _NOT_CONTROL_TAGS
|
|
94
94
|
)
|
|
95
95
|
|
|
96
|
+
def to_json_schema(self):
|
|
97
|
+
"""
|
|
98
|
+
Converts the current ControlTag instance into a JSON Schema.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
dict: A dictionary representing the JSON Schema.
|
|
102
|
+
"""
|
|
103
|
+
return {"type": "string"}
|
|
104
|
+
|
|
96
105
|
@classmethod
|
|
97
106
|
def parse_node(cls, tag: xml.etree.ElementTree.Element, tags_mapping=None) -> "ControlTag":
|
|
98
107
|
"""
|
|
@@ -261,11 +270,11 @@ class ControlTag(LabelStudioTag):
|
|
|
261
270
|
|
|
262
271
|
def _validate_labels(self, labels):
|
|
263
272
|
"""Check that labels is a subset of self.labels, used for
|
|
264
|
-
example when you're validate the
|
|
273
|
+
example when you're validate the annotation or prediction to
|
|
265
274
|
make sure there no undefined labels used.
|
|
266
275
|
|
|
267
276
|
"""
|
|
268
|
-
if not self.labels:
|
|
277
|
+
if not self.labels or not labels:
|
|
269
278
|
return True
|
|
270
279
|
|
|
271
280
|
return set(labels).issubset(set(self.labels))
|
|
@@ -335,10 +344,11 @@ class ControlTag(LabelStudioTag):
|
|
|
335
344
|
|
|
336
345
|
return to_name
|
|
337
346
|
else:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
347
|
+
# TODO: "Pairwise" tag has multiple to_name
|
|
348
|
+
# if len(self.to_name) > 1:
|
|
349
|
+
# raise Exception(
|
|
350
|
+
# "Multiple to_name in control tag, specify to_name in function"
|
|
351
|
+
# )
|
|
342
352
|
|
|
343
353
|
return self.to_name[0]
|
|
344
354
|
|
|
@@ -415,7 +425,7 @@ class ControlTag(LabelStudioTag):
|
|
|
415
425
|
)
|
|
416
426
|
|
|
417
427
|
kwargs[self._label_attr_name] = label
|
|
418
|
-
|
|
428
|
+
|
|
419
429
|
return self._label_simple(to_name=to_name, **kwargs)
|
|
420
430
|
|
|
421
431
|
def label(
|
|
@@ -446,12 +456,29 @@ class ControlTag(LabelStudioTag):
|
|
|
446
456
|
Region
|
|
447
457
|
A new Region object with the specified label applied.
|
|
448
458
|
"""
|
|
449
|
-
if hasattr(self, "_label_attr_name"):
|
|
459
|
+
if hasattr(self, "_label_attr_name") and label is not None:
|
|
450
460
|
return self._label_with_labels(
|
|
451
461
|
label=label, to_name=to_name, *args, **kwargs
|
|
452
462
|
)
|
|
453
463
|
else:
|
|
454
464
|
return self._label_simple(to_name=to_name, *args, **kwargs)
|
|
465
|
+
|
|
466
|
+
def get_labels(self, regions: List[Dict]):
|
|
467
|
+
"""
|
|
468
|
+
Returns the simplified representation of the label. Sort of a reverse to label() method to retrieve an input `label` from an output regions
|
|
469
|
+
"""
|
|
470
|
+
values = [region.get('value') for region in regions if region.get('from_name') == self.name]
|
|
471
|
+
values = list(filter(lambda x: x is not None, values))
|
|
472
|
+
if not hasattr(self, "_label_attr_name"):
|
|
473
|
+
return values
|
|
474
|
+
labels = []
|
|
475
|
+
for value in values:
|
|
476
|
+
if len(value) == 1 and self._label_attr_name in value:
|
|
477
|
+
v = value[self._label_attr_name]
|
|
478
|
+
labels.append(v[0] if len(v) == 1 else v)
|
|
479
|
+
else:
|
|
480
|
+
labels.append(value)
|
|
481
|
+
return labels[0] if len(labels) == 1 else labels
|
|
455
482
|
|
|
456
483
|
def as_tuple(self):
|
|
457
484
|
""" """
|
|
@@ -485,6 +512,34 @@ class ChoicesTag(ControlTag):
|
|
|
485
512
|
_label_attr_name: str = "choices"
|
|
486
513
|
_value_class: Type[ChoicesValue] = ChoicesValue
|
|
487
514
|
|
|
515
|
+
@property
|
|
516
|
+
def is_multiple_choice(self):
|
|
517
|
+
return self.attr.get("choice") == "multiple"
|
|
518
|
+
|
|
519
|
+
def to_json_schema(self):
|
|
520
|
+
"""
|
|
521
|
+
Converts the current ChoicesTag instance into a JSON Schema.
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
525
|
+
"""
|
|
526
|
+
if self.is_multiple_choice:
|
|
527
|
+
return {
|
|
528
|
+
"type": "array",
|
|
529
|
+
"items": {
|
|
530
|
+
"type": "string",
|
|
531
|
+
"enum": self.labels,
|
|
532
|
+
},
|
|
533
|
+
"uniqueItems": True,
|
|
534
|
+
"description": f"Choices for {self.to_name[0]}",
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return {
|
|
538
|
+
"type": "string",
|
|
539
|
+
"enum": self.labels,
|
|
540
|
+
"description": f"Choices for {self.to_name[0]}"
|
|
541
|
+
}
|
|
542
|
+
|
|
488
543
|
|
|
489
544
|
class LabelsValue(SpanSelection):
|
|
490
545
|
labels: List[str]
|
|
@@ -496,6 +551,41 @@ class LabelsTag(ControlTag):
|
|
|
496
551
|
_label_attr_name: str = "labels"
|
|
497
552
|
_value_class: Type[LabelsValue] = LabelsValue
|
|
498
553
|
|
|
554
|
+
def to_json_schema(self):
|
|
555
|
+
"""
|
|
556
|
+
Converts the current LabelsTag instance into a JSON Schema.
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
560
|
+
"""
|
|
561
|
+
return {
|
|
562
|
+
"type": "array",
|
|
563
|
+
"items": {
|
|
564
|
+
"type": "object",
|
|
565
|
+
"required": ["start", "end", "labels"],
|
|
566
|
+
"properties": {
|
|
567
|
+
"start": {
|
|
568
|
+
"type": "integer",
|
|
569
|
+
"minimum": 0
|
|
570
|
+
},
|
|
571
|
+
"end": {
|
|
572
|
+
"type": "integer",
|
|
573
|
+
"minimum": 0
|
|
574
|
+
},
|
|
575
|
+
"labels": {
|
|
576
|
+
"type": "array",
|
|
577
|
+
"items": {
|
|
578
|
+
"type": "string",
|
|
579
|
+
"enum": self.labels
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
"text": {
|
|
583
|
+
"type": "string"
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"description": f"Labels and span indices for {self.to_name[0]}"
|
|
588
|
+
}
|
|
499
589
|
|
|
500
590
|
## Image tags
|
|
501
591
|
|
|
@@ -684,6 +774,26 @@ class NumberTag(ControlTag):
|
|
|
684
774
|
""" """
|
|
685
775
|
tag: str = "Number"
|
|
686
776
|
_value_class: Type[NumberValue] = NumberValue
|
|
777
|
+
_label_attr_name: str = "number"
|
|
778
|
+
|
|
779
|
+
def to_json_schema(self):
|
|
780
|
+
"""
|
|
781
|
+
Converts the current NumberTag instance into a JSON Schema.
|
|
782
|
+
|
|
783
|
+
Returns:
|
|
784
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
785
|
+
"""
|
|
786
|
+
schema = {
|
|
787
|
+
"type": "number",
|
|
788
|
+
"description": f"Number for {self.to_name[0]}"
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if 'min' in self.attr:
|
|
792
|
+
schema["minimum"] = float(self.attr['min'])
|
|
793
|
+
if 'max' in self.attr:
|
|
794
|
+
schema["maximum"] = float(self.attr['max'])
|
|
795
|
+
|
|
796
|
+
return schema
|
|
687
797
|
|
|
688
798
|
|
|
689
799
|
class DateTimeValue(BaseModel):
|
|
@@ -694,6 +804,25 @@ class DateTimeTag(ControlTag):
|
|
|
694
804
|
""" """
|
|
695
805
|
tag: str = "DateTime"
|
|
696
806
|
_value_class: Type[DateTimeValue] = DateTimeValue
|
|
807
|
+
_label_attr_name: str = "datetime"
|
|
808
|
+
|
|
809
|
+
def _label_simple(self, to_name: Optional[str] = None, *args, **kwargs) -> Region:
|
|
810
|
+
# TODO: temporary fix to force datetime to be a string
|
|
811
|
+
kwargs['datetime'] = kwargs['datetime'][0]
|
|
812
|
+
return super()._label_simple(to_name, *args, **kwargs)
|
|
813
|
+
|
|
814
|
+
def to_json_schema(self):
|
|
815
|
+
"""
|
|
816
|
+
Converts the current DateTimeTag instance into a JSON Schema.
|
|
817
|
+
|
|
818
|
+
Returns:
|
|
819
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
820
|
+
"""
|
|
821
|
+
return {
|
|
822
|
+
"type": "string",
|
|
823
|
+
"format": "date-time",
|
|
824
|
+
"description": f"Date and time for {self.to_name[0]}"
|
|
825
|
+
}
|
|
697
826
|
|
|
698
827
|
|
|
699
828
|
class HyperTextLabelsValue(SpanSelectionOffsets):
|
|
@@ -715,12 +844,27 @@ class PairwiseTag(ControlTag):
|
|
|
715
844
|
""" """
|
|
716
845
|
tag: str = "Pairwise"
|
|
717
846
|
_value_class: Type[PairwiseValue] = PairwiseValue
|
|
847
|
+
_label_attr_name: str = "selected"
|
|
718
848
|
|
|
719
|
-
def label(self,
|
|
849
|
+
def label(self, label):
|
|
720
850
|
""" """
|
|
721
|
-
value = PairwiseValue(selected=
|
|
851
|
+
value = PairwiseValue(selected=label)
|
|
852
|
+
# <Pairwise> tag has equal from_name and to_name, and string label that's not a list of strings
|
|
722
853
|
return Region(from_tag=self, to_tag=self, value=value)
|
|
723
854
|
|
|
855
|
+
def to_json_schema(self):
|
|
856
|
+
"""
|
|
857
|
+
Converts the current PairwiseTag instance into a JSON Schema.
|
|
858
|
+
|
|
859
|
+
Returns:
|
|
860
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
861
|
+
"""
|
|
862
|
+
return {
|
|
863
|
+
"type": "string",
|
|
864
|
+
"enum": ["left", "right"],
|
|
865
|
+
"description": f"Pairwise selection between {self.to_name[0]} (left) and {self.to_name[1]} (right)"
|
|
866
|
+
}
|
|
867
|
+
|
|
724
868
|
|
|
725
869
|
class ParagraphLabelsValue(SpanSelectionOffsets):
|
|
726
870
|
paragraphlabels: List[str]
|
|
@@ -759,6 +903,22 @@ class RatingTag(ControlTag):
|
|
|
759
903
|
""" """
|
|
760
904
|
tag: str = "Rating"
|
|
761
905
|
_value_class: Type[RatingValue] = RatingValue
|
|
906
|
+
_label_attr_name: str = "rating"
|
|
907
|
+
|
|
908
|
+
def to_json_schema(self):
|
|
909
|
+
"""
|
|
910
|
+
Converts the current RatingTag instance into a JSON Schema.
|
|
911
|
+
|
|
912
|
+
Returns:
|
|
913
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
914
|
+
"""
|
|
915
|
+
max_rating = int(self.attr.get('maxRating', 5)) # Default to 5 if not specified
|
|
916
|
+
return {
|
|
917
|
+
"type": "integer",
|
|
918
|
+
"minimum": 0,
|
|
919
|
+
"maximum": max_rating,
|
|
920
|
+
"description": f"Rating for {self.to_name[0]} (0 to {max_rating})"
|
|
921
|
+
}
|
|
762
922
|
|
|
763
923
|
|
|
764
924
|
class RelationsTag(ControlTag):
|
|
@@ -784,6 +944,27 @@ class TaxonomyTag(ControlTag):
|
|
|
784
944
|
""" """
|
|
785
945
|
tag: str = "Taxonomy"
|
|
786
946
|
_value_class: Type[TaxonomyValue] = TaxonomyValue
|
|
947
|
+
_label_attr_name: str = "taxonomy"
|
|
948
|
+
|
|
949
|
+
def to_json_schema(self):
|
|
950
|
+
"""
|
|
951
|
+
Converts the current TaxonomyTag instance into a JSON Schema.
|
|
952
|
+
|
|
953
|
+
Returns:
|
|
954
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
955
|
+
"""
|
|
956
|
+
return {
|
|
957
|
+
"type": "array",
|
|
958
|
+
"items": {
|
|
959
|
+
"type": "array",
|
|
960
|
+
"items": {
|
|
961
|
+
"type": "string",
|
|
962
|
+
# TODO: enforce the order of the enums according to the taxonomy tree
|
|
963
|
+
"enum": self.labels
|
|
964
|
+
}
|
|
965
|
+
},
|
|
966
|
+
"description": f"Taxonomy for {self.to_name[0]}. Each item is a path from root to selected node."
|
|
967
|
+
}
|
|
787
968
|
|
|
788
969
|
|
|
789
970
|
class TextAreaValue(BaseModel):
|
|
@@ -794,6 +975,20 @@ class TextAreaTag(ControlTag):
|
|
|
794
975
|
""" """
|
|
795
976
|
tag: str = "TextArea"
|
|
796
977
|
_value_class: Type[TextAreaValue] = TextAreaValue
|
|
978
|
+
_label_attr_name: str = "text"
|
|
979
|
+
|
|
980
|
+
def to_json_schema(self):
|
|
981
|
+
"""
|
|
982
|
+
Converts the current TextAreaTag instance into a JSON Schema.
|
|
983
|
+
|
|
984
|
+
Returns:
|
|
985
|
+
dict: A dictionary representing the JSON Schema compatible with OpenAPI 3.
|
|
986
|
+
"""
|
|
987
|
+
return {
|
|
988
|
+
"type": "string",
|
|
989
|
+
"description": f"Text for {self.to_name[0]}"
|
|
990
|
+
}
|
|
991
|
+
|
|
797
992
|
|
|
798
993
|
|
|
799
994
|
class TimeSeriesValue(SpanSelection):
|