Skip to content

Commit 3728631

Browse files
committed
Merge remote-tracking branch 'origin/develop' into mno/AL-2849
2 parents c69da34 + d44e091 commit 3728631

File tree

18 files changed

+587
-188
lines changed

18 files changed

+587
-188
lines changed

CHANGELOG.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
# Changelog
22

3+
# Version 3.26.0 (2022-08-15)
4+
## Added
5+
* `Batch.delete()` which will delete an existing `Batch`
6+
* `Batch.delete_labels()` which will delete all `Label`’s created after a `Project`’s mode has been set to batch.
7+
* Note: Does not include labels that were imported via model-assisted labeling or label imports
8+
* Support for creating model config when creating a model run
9+
* `RAW_TEXT` and `TEXT_FILE` attachment types to replace the `TEXT` type.
10+
11+
# Version 3.25.3 (2022-08-10)
12+
## Fixed
13+
* Label export will continue polling if the downloadUrl is None
14+
15+
# Version 3.25.2 (2022-07-26)
16+
## Updated
17+
* Mask downloads now have retries
18+
* Failed `upload_data` now shows more details in the error message
19+
20+
## Fixed
21+
* Fixed Metadata not importing with DataRows when bulk importing local files.
22+
* Fixed COCOConverter failing for empty annotations
23+
24+
## Documentation
25+
* Notebooks are up-to-date with examples of importing annotations without `schema_id`
26+
27+
# Version 3.25.1 (2022-07-20)
28+
## Fixed
29+
* Removed extra dependency causing import errors.
30+
331
# Version 3.25.0 (2022-07-20)
432

533
## Added
@@ -10,7 +38,7 @@
1038
* `Batch.export_data_rows(include_metadata=True)`
1139
* `Dataset.export_data_rows(include_metadata=True)`
1240
* `Project.export_queued_data_rows(include_metadata=True)`
13-
* `VideoObjectAnnotation` has `segment_index` to group video annotations into video segments
41+
* `VideoObjectAnnotation` has `segment_index` to group video annotations into video segments
1442

1543
## Removed
1644
* `Project.video_label_generator`. Use `Project.label_generator` instead.
@@ -21,7 +49,7 @@
2149
* 150,000 rows per upload without metadata
2250
* 30,000 rows per upload with metadata
2351

24-
52+
2553
# Version 3.24.1 (2022-07-07)
2654
## Updated
2755
* Added `refresh_ontology()` as part of create/update/delete metadata schema functions
@@ -635,3 +663,7 @@ a `Label`. Default value is 0.0.
635663

636664
## Version 2.2 (2019-10-18)
637665
Changelog not maintained before version 2.2.
666+
667+
### Changed
668+
* `Model.create_model_run()`
669+
* Add training metadata config as a model run creation param

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ The Labelbox Python API offers a simple, user-friendly way to interact with the
2020

2121
- Use Python 3.7, 3.8 or 3.9
2222
- [Create an account](http://app.labelbox.com/)
23-
- [Generate an API key](https://labelbox.com/docs/api/getting-started#create_api_key)
23+
- [Generate an API key](https://docs.labelbox.com/docs/create-an-api-key)
2424

2525
## Installation
2626

@@ -59,9 +59,9 @@ pip install labelbox[data]
5959

6060
## Documentation
6161

62-
- [Visit our docs](https://labelbox.com/docs/python-api) to learn how the SDK works
62+
- [Visit our docs](https://docs.labelbox.com/reference) to learn how the SDK works
6363
- Checkout our [notebook examples](examples/) to follow along with interactive tutorials
64-
- view our [API reference](https://labelbox.com/docs/python-api/api-reference).
64+
- view our [API reference](https://labelbox-python.readthedocs.io/en/latest/index.html).
6565

6666
## Authentication
6767

examples/annotation_import/image.ipynb

Lines changed: 409 additions & 155 deletions
Large diffs are not rendered by default.

examples/model_diagnostics/model_diagnostics_guide.ipynb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,8 @@
350350
"source": [
351351
"lb_model = client.create_model(name=f\"{project.name}-model\",\n",
352352
" ontology_id=project.ontology().uid)\n",
353-
"lb_model_run = lb_model.create_model_run(\"0.0.0\")\n",
353+
"lb_model_run_hyperparameters = {\"batch_size\": 1000}\n",
354+
"lb_model_run = lb_model.create_model_run(\"0.0.0\", lb_model_run_hyperparameters)\n",
354355
"lb_model_run.upsert_labels([label.uid for label in labels])"
355356
]
356357
},

labelbox/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name = "labelbox"
2-
__version__ = "3.25.1"
2+
__version__ = "3.26.0"
33

44
from labelbox.client import Client
55
from labelbox.schema.project import Project

labelbox/client.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -381,9 +381,14 @@ def upload_data(self,
381381
"Failed to upload, unknown cause", e)
382382

383383
if not file_data or not file_data.get("uploadFile", None):
384+
try:
385+
errors = response.json().get("errors", [])
386+
error_msg = next(iter(errors), {}).get("message",
387+
"Unknown error")
388+
except Exception as e:
389+
error_msg = "Unknown error"
384390
raise labelbox.exceptions.LabelboxError(
385-
"Failed to upload, message: %s" % file_data or
386-
file_data.get("error"))
391+
"Failed to upload, message: %s" % error_msg)
387392

388393
return file_data["uploadFile"]["url"]
389394

labelbox/data/serialization/coco/instance_dataset.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@ def mask_to_coco_object_annotation(
2828
xmin, ymin, xmax, ymax = shapely.bounds
2929
# Iterate over polygon once or multiple polygon for each item
3030
area = shapely.area
31-
if shapely.type == 'Polygon':
32-
shapely = [shapely]
3331

3432
return COCOObjectAnnotation(
3533
id=annot_idx,
3634
image_id=image_id,
3735
category_id=category_id,
3836
segmentation=[
39-
np.array(s.exterior.coords).ravel().tolist() for s in shapely
37+
np.array(s.exterior.coords).ravel().tolist()
38+
for s in ([shapely] if shapely.type == "Polygon" else shapely.geoms)
4039
],
4140
area=area,
4241
bbox=[xmin, ymin, xmax - xmin, ymax - ymin],

labelbox/schema/asset_attachment.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class AssetAttachment(DbObject):
99
""" Asset attachment provides extra context about an asset while labeling.
1010
1111
Attributes:
12-
attachment_type (str): IMAGE, VIDEO, TEXT, IMAGE_OVERLAY, or HTML
12+
attachment_type (str): IMAGE, VIDEO, IMAGE_OVERLAY, HTML, RAW_TEXT, or TEXT_URL. TEXT attachment type is deprecated.
1313
attachment_value (str): URL to an external file or a string of text
1414
"""
1515

@@ -19,6 +19,8 @@ class AttachmentType(Enum):
1919
TEXT = "TEXT"
2020
IMAGE_OVERLAY = "IMAGE_OVERLAY"
2121
HTML = "HTML"
22+
RAW_TEXT = "RAW_TEXT"
23+
TEXT_URL = "TEXT_URL"
2224

2325
for topic in AttachmentType:
2426
vars()[topic.name] = topic.value

labelbox/schema/batch.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,15 @@ def remove_queued_data_rows(self) -> None:
6868

6969
project_id_param = "projectId"
7070
batch_id_param = "batchId"
71-
self.client.execute("""mutation ArchiveBatchPyApi($%s: ID!, $%s: ID!) {
72-
project(where: {id: $%s}) { archiveBatch(batchId: $%s) { id archivedAt } }
71+
self.client.execute(
72+
"""mutation RemoveQueuedDataRowsFromBatchPyApi($%s: ID!, $%s: ID!) {
73+
project(where: {id: $%s}) { removeQueuedDataRowsFromBatch(batchId: $%s) { id } }
7374
}""" % (project_id_param, batch_id_param, project_id_param,
7475
batch_id_param), {
7576
project_id_param: self.project_id,
7677
batch_id_param: self.uid
7778
},
78-
experimental=True)
79+
experimental=True)
7980

8081
def export_data_rows(self,
8182
timeout_seconds=120,
@@ -125,3 +126,50 @@ def export_data_rows(self,
125126
logger.debug("Batch '%s' data row export, waiting for server...",
126127
self.uid)
127128
time.sleep(sleep_time)
129+
130+
def delete(self) -> None:
131+
""" Deletes the given batch.
132+
133+
Note: Batch deletion for batches that has labels is forbidden.
134+
135+
Args:
136+
batch (Batch): Batch to remove queued data rows from
137+
"""
138+
139+
project_id_param = "projectId"
140+
batch_id_param = "batchId"
141+
self.client.execute("""mutation DeleteBatchPyApi($%s: ID!, $%s: ID!) {
142+
project(where: {id: $%s}) { deleteBatch(batchId: $%s) { deletedBatchId } }
143+
}""" % (project_id_param, batch_id_param, project_id_param,
144+
batch_id_param), {
145+
project_id_param: self.project_id,
146+
batch_id_param: self.uid
147+
},
148+
experimental=True)
149+
150+
def delete_labels(self, set_labels_as_template=False) -> None:
151+
""" Deletes labels that were created for data rows in the batch.
152+
153+
Args:
154+
batch (Batch): Batch to remove queued data rows from
155+
set_labels_as_template (bool): When set to true, the deleted labels will be kept as templates.
156+
"""
157+
158+
project_id_param = "projectId"
159+
batch_id_param = "batchId"
160+
type_param = "type"
161+
res = self.client.execute(
162+
"""mutation DeleteBatchLabelsPyApi($%s: ID!, $%s: ID!, $%s: DeleteBatchLabelsType!) {
163+
project(where: {id: $%s}) { deleteBatchLabels(batchId: $%s, data:{ type: $%s }) { deletedLabelIds } }
164+
}""" % (project_id_param, batch_id_param, type_param, project_id_param,
165+
batch_id_param, type_param), {
166+
project_id_param:
167+
self.project_id,
168+
batch_id_param:
169+
self.uid,
170+
type_param:
171+
"RequeueDataWithLabelAsTemplate"
172+
if set_labels_as_template else "RequeueData"
173+
},
174+
experimental=True)
175+
return res

labelbox/schema/model.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,26 @@ class Model(DbObject):
1818
name = Field.String("name")
1919
model_runs = Relationship.ToMany("ModelRun", False)
2020

21-
def create_model_run(self, name) -> "ModelRun":
21+
def create_model_run(self, name, config=None) -> "ModelRun":
2222
""" Creates a model run belonging to this model.
2323
2424
Args:
2525
name (string): The name for the model run.
26+
config (json): Model run's training metadata config
2627
Returns:
2728
ModelRun, the created model run.
2829
"""
2930
name_param = "name"
31+
config_param = "config"
3032
model_id_param = "modelId"
3133
ModelRun = Entity.ModelRun
32-
query_str = """mutation CreateModelRunPyApi($%s: String!, $%s: ID!) {
33-
createModelRun(data: {name: $%s, modelId: $%s}) {%s}}""" % (
34-
name_param, model_id_param, name_param, model_id_param,
35-
query.results_query_part(ModelRun))
34+
query_str = """mutation CreateModelRunPyApi($%s: String!, $%s: Json, $%s: ID!) {
35+
createModelRun(data: {name: $%s, trainingMetadata: $%s, modelId: $%s}) {%s}}""" % (
36+
name_param, config_param, model_id_param, name_param, config_param,
37+
model_id_param, query.results_query_part(ModelRun))
3638
res = self.client.execute(query_str, {
3739
name_param: name,
40+
config_param: config,
3841
model_id_param: self.uid
3942
})
4043
return ModelRun(self.client, res["createModelRun"])

0 commit comments

Comments
 (0)