Skip to content

Commit 311bdd1

Browse files
authored
Release 3.59.0 (#1353)
2 parents eebd88e + cf2f0af commit 311bdd1

33 files changed

+850
-253
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
# Changelog
2+
# Version 3.59.0 (2024-01-05)
3+
## Added
4+
* Support set_labeling_parameter_overrides for global keys
5+
* Support bulk_delete of data row metadata for global keys
6+
* Support bulk_export of data row metadata for global keys
7+
## Fixed
8+
* Stop overwriting class annotations on prediction upload
9+
* Prevent users from uploading video annotations over the API limit (5000)
10+
* Make description optional for foundry app
11+
## Notebooks
12+
* Update notebooks for Project set_labeling_parameter_overrides add support for global keys
13+
214
# Version 3.58.1 (2023-12-15)
315
## Added
416
* Support to export all projects and all model runs to `export_v2` for a `dataset` and a `slice`

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
copyright = '2021, Labelbox'
2222
author = 'Labelbox'
2323

24-
release = '3.58.1'
24+
release = '3.59.0'
2525

2626
# -- General configuration ---------------------------------------------------
2727

docs/source/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ DataRowMetadata
179179

180180
.. automodule:: labelbox.schema.data_row_metadata
181181
:members:
182+
:exclude-members: _DeleteBatchDataRowMetadata
182183
:show-inheritance:
183184

184185
AnnotationImport
@@ -236,6 +237,12 @@ Identifiables
236237
:members:
237238
:show-inheritance:
238239

240+
Identifiable
241+
---------------------------
242+
.. automodule:: labelbox.schema.identifiable
243+
:members:
244+
:show-inheritance:
245+
239246
ConflictResolutionStrategy
240247
---------------------------
241248
.. automodule:: labelbox.schema.conflict_resolution_strategy

examples/project_configuration/queue_management.ipynb

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@
7373
"metadata": {},
7474
"source": [
7575
"# API Key and Client\n",
76-
"Provide a valid api key below in order to properly connect to the Labelbox Client."
76+
"See the developer guide for [creating an API key](https://docs.labelbox.com/reference/create-api-key)."
7777
],
7878
"cell_type": "markdown"
7979
},
8080
{
8181
"metadata": {},
8282
"source": [
83-
"# Add your api key\n",
83+
"# Add your API key\n",
8484
"API_KEY = \"\"\n",
8585
"client = lb.Client(api_key=API_KEY)"
8686
],
@@ -98,9 +98,7 @@
9898
{
9999
"metadata": {},
100100
"source": [
101-
"## Create two Labelbox projects\n",
102-
"# Project defaults to batch mode with benchmark quality settings if queue mode argument is not provided\n",
103-
"# Note that queue mode will be deprecated once dataset mode is deprecated \n",
101+
"# Create Labelbox project\n",
104102
"\n",
105103
"project = client.create_project(name=\"batch-test-project\",\n",
106104
" description=\"a description\",\n",
@@ -139,7 +137,7 @@
139137
"data_rows = dataset.create_data_rows(uploads)\n",
140138
"data_rows.wait_till_done()\n",
141139
"print(\"Errors\" , data_rows.errors)\n",
142-
"print(\"Dataset status: \", data_rows.status)"
140+
"print(\"Dataset status: \", data_rows.status)\n"
143141
],
144142
"cell_type": "code",
145143
"outputs": [],
@@ -196,33 +194,48 @@
196194
"source": [
197195
"## Queue Order\n",
198196
"- Add priority for each data row\n",
199-
"- Remove all the batch priority in your project"
197+
"- Update priority for each data row"
200198
],
201199
"cell_type": "markdown"
202200
},
203201
{
204202
"metadata": {},
205203
"source": [
206-
"## See current LPOs\n",
207-
"print(project.labeling_parameter_overrides().get_many(4))\n",
204+
"# Select data rows from batches \n",
205+
"data_rows = []\n",
206+
"for batch in list(project.batches()):\n",
207+
" for data_row in batch.export_data_rows(): \n",
208+
" data_rows.append(data_row)\n",
208209
"\n",
210+
"# Get label parameter overrides (LPOs)\n",
211+
"project_lpos = list(project.labeling_parameter_overrides())\n",
209212
"\n",
210-
"## Select data rows from batches \n",
211-
"data_rows = []\n",
212-
"for b in list(project.batches()):\n",
213-
" for dr in b.export_data_rows(): \n",
214-
" data_rows.append(dr)\n",
215-
" \n",
216-
"## Add LPOs\n",
217-
"lpos1 = []\n",
218-
"p=1;\n",
219-
"for dr in data_rows: \n",
220-
" lpos1.append((dr, p, 1))\n",
221-
" p+=1\n",
213+
"for lpo in project_lpos:\n",
214+
" print(lpo)\n",
215+
" print(\"Data row:\", lpo.data_row().uid)"
216+
],
217+
"cell_type": "code",
218+
"outputs": [],
219+
"execution_count": null
220+
},
221+
{
222+
"metadata": {},
223+
"source": [
224+
"# Add LPOs\n",
225+
"lpos = []\n",
226+
"priority=1\n",
227+
"for data_row in data_rows: \n",
228+
" lpos.append((data_row, priority, 1))\n",
229+
" priority+=1\n",
222230
"\n",
223231
"\n",
224-
"project.set_labeling_parameter_overrides(lpos1)\n",
225-
"# Get the project's LPOs"
232+
"project.set_labeling_parameter_overrides(lpos)\n",
233+
"\n",
234+
"# Check results\n",
235+
"project_lpos = list(project.labeling_parameter_overrides())\n",
236+
"\n",
237+
"for lpo in project_lpos:\n",
238+
" print(lpo)"
226239
],
227240
"cell_type": "code",
228241
"outputs": [],
@@ -231,14 +244,18 @@
231244
{
232245
"metadata": {},
233246
"source": [
234-
"## Verify LPOs\n",
247+
"# Update LPOs\n",
248+
"global_keys = []\n",
249+
"for data_row in data_rows:\n",
250+
" global_keys.append(data_row.global_key)\n",
251+
"\n",
252+
"project.update_data_row_labeling_priority(data_rows=lb.GlobalKeys(global_keys), priority=1)\n",
253+
"\n",
254+
"# Check results\n",
235255
"project_lpos = list(project.labeling_parameter_overrides())\n",
236-
"## Remove LPOs\n",
237-
"# project.unset_labeling_parameter_overrides(dataset.export_data_rows())\n",
238256
"\n",
239257
"for lpo in project_lpos:\n",
240-
" print(lpo)\n",
241-
" print(\"Data row:\", lpo.data_row().uid)"
258+
" print(lpo)"
242259
],
243260
"cell_type": "code",
244261
"outputs": [],

labelbox/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "labelbox"
22

3-
__version__ = "3.58.1"
3+
__version__ = "3.59.0"
44

55
from labelbox.client import Client
66
from labelbox.schema.project import Project
@@ -34,3 +34,4 @@
3434
from labelbox.schema.queue_mode import QueueMode
3535
from labelbox.schema.task_queue import TaskQueue
3636
from labelbox.schema.identifiables import UniqueIds, GlobalKeys, DataRowIds
37+
from labelbox.schema.identifiable import UniqueId, GlobalKey

labelbox/client.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def convert_value(value):
183183

184184
endpoint = self.endpoint if not experimental else self.endpoint.replace(
185185
"/graphql", "/_gql")
186+
186187
try:
187188
request = {
188189
'url': endpoint,
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import abc
2-
from uuid import UUID, uuid4
3-
from typing import Any, Dict
2+
from uuid import UUID
3+
from typing import Any, Dict, Optional
44
from pydantic import PrivateAttr
55

66
from .feature import FeatureSchema
@@ -9,10 +9,10 @@
99
class BaseAnnotation(FeatureSchema, abc.ABC):
1010
""" Base annotation class. Shouldn't be directly instantiated
1111
"""
12-
_uuid: UUID = PrivateAttr()
12+
_uuid: Optional[UUID] = PrivateAttr()
1313
extra: Dict[str, Any] = {}
1414

1515
def __init__(self, **data):
1616
super().__init__(**data)
1717
extra_uuid = data.get("extra", {}).get("uuid")
18-
self._uuid = data.get("_uuid") or extra_uuid or uuid4()
18+
self._uuid = data.get("_uuid") or extra_uuid or None

labelbox/data/serialization/ndjson/converter.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import logging
2+
import uuid
23
from typing import Any, Dict, Generator, Iterable
34

45
from ...annotation_types.collection import LabelCollection, LabelGenerator
6+
from ...annotation_types.relationship import RelationshipAnnotation
57
from .label import NDLabel
68

79
logger = logging.getLogger(__name__)
@@ -21,7 +23,7 @@ def deserialize(json_data: Iterable[Dict[str, Any]]) -> LabelGenerator:
2123
Returns:
2224
LabelGenerator containing the ndjson data.
2325
"""
24-
data = NDLabel(**{'annotations': json_data})
26+
data = NDLabel(**{"annotations": json_data})
2527
res = data.to_common()
2628
return res
2729

@@ -40,8 +42,50 @@ def serialize(
4042
Returns:
4143
A generator for accessing the ndjson representation of the data
4244
"""
45+
used_annotation_uuids = set()
46+
for label in labels:
47+
annotation_uuid_to_generated_uuid_lookup = {}
48+
# UUIDs are private properties used to enhance UX when defining relationships.
49+
# They are created for all annotations, but only utilized for relationships.
50+
# To avoid overwriting, UUIDs must be unique across labels.
51+
# Non-relationship annotation UUIDs are dropped (server-side generation will occur).
52+
# For relationship annotations, new UUIDs are generated and stored in a lookup table.
53+
for annotation in label.annotations:
54+
if isinstance(annotation, RelationshipAnnotation):
55+
source_uuid = annotation.value.source._uuid
56+
target_uuid = annotation.value.target._uuid
57+
58+
if (len(
59+
used_annotation_uuids.intersection(
60+
{source_uuid, target_uuid})) > 0):
61+
new_source_uuid = uuid.uuid4()
62+
new_target_uuid = uuid.uuid4()
63+
64+
annotation_uuid_to_generated_uuid_lookup[
65+
source_uuid] = new_source_uuid
66+
annotation_uuid_to_generated_uuid_lookup[
67+
target_uuid] = new_target_uuid
68+
annotation.value.source._uuid = new_source_uuid
69+
annotation.value.target._uuid = new_target_uuid
70+
else:
71+
annotation_uuid_to_generated_uuid_lookup[
72+
source_uuid] = source_uuid
73+
annotation_uuid_to_generated_uuid_lookup[
74+
target_uuid] = target_uuid
75+
used_annotation_uuids.add(annotation._uuid)
76+
77+
for annotation in label.annotations:
78+
if (not isinstance(annotation, RelationshipAnnotation) and
79+
hasattr(annotation, "_uuid")):
80+
annotation._uuid = annotation_uuid_to_generated_uuid_lookup.get(
81+
annotation._uuid, annotation._uuid)
82+
4383
for example in NDLabel.from_common(labels):
44-
res = example.dict(by_alias=True)
84+
annotation_uuid = getattr(example, "uuid", None)
85+
86+
res = example.dict(
87+
by_alias=True,
88+
exclude={"uuid"} if annotation_uuid == "None" else None)
4589
for k, v in list(res.items()):
4690
if k in IGNORE_IF_NONE and v is None:
4791
del res[k]

labelbox/schema/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@
2222
import labelbox.schema.iam_integration
2323
import labelbox.schema.media_type
2424
import labelbox.schema.identifiables
25+
import labelbox.schema.identifiable

labelbox/schema/annotation_import.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from labelbox.types import Label
2424

2525
NDJSON_MIME_TYPE = "application/x-ndjson"
26+
LABEL_LIMIT = 5000
2627
logger = logging.getLogger(__name__)
2728

2829

@@ -153,6 +154,9 @@ def _get_ndjson_from_objects(cls, objects: Union[List[Dict[str, Any]],
153154
)
154155

155156
objects = serialize_labels(objects)
157+
if len(objects) > LABEL_LIMIT:
158+
raise ValueError(
159+
f"Label count {len(objects)} exceeds limit of {(LABEL_LIMIT)}")
156160
cls._validate_data_rows(objects)
157161

158162
data_str = parser.dumps(objects)
@@ -502,8 +506,8 @@ def create_from_objects(
502506
Returns:
503507
MALPredictionImport
504508
"""
505-
data = cls._get_ndjson_from_objects(predictions, 'annotations')
506509

510+
data = cls._get_ndjson_from_objects(predictions, 'annotations')
507511
if len(predictions) > 0 and isinstance(predictions[0], Dict):
508512
predictions_dicts = cast(List[Dict[str, Any]], predictions)
509513
has_confidence = LabelsConfidencePresenceChecker.check(

0 commit comments

Comments
 (0)