Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
97078e0
Add global_key to data_row
Aug 9, 2022
44b892d
Update CHANGELOG.md
msokoloff1 Aug 15, 2022
d44e091
Merge pull request #672 from Labelbox/msokoloff1-patch-1
msokoloff1 Aug 15, 2022
d35b867
Update config [AL-3061]
mmarinwilcke Aug 10, 2022
6ce0366
Changelog update
mmarinwilcke Aug 10, 2022
9b1a5f0
formatting
mmarinwilcke Aug 10, 2022
87103df
more formatting
mmarinwilcke Aug 10, 2022
cd49e9b
uses is None
mmarinwilcke Aug 15, 2022
fd82d25
Addresses review comments
mmarinwilcke Aug 17, 2022
6682046
linting
mmarinwilcke Aug 17, 2022
539c300
changelog section + capitalizes model run in comments
mmarinwilcke Aug 17, 2022
ef5c450
initial pass at creation of conversion from export to a labelbox format
jtsodapop Aug 17, 2022
dc39c79
template date
mmarinwilcke Aug 18, 2022
59b05c2
no more camelcase in config
mmarinwilcke Aug 18, 2022
cc2462c
test fix
mmarinwilcke Aug 18, 2022
f95a13a
Merge pull request #669 from Labelbox/mmw/AL-3061
mmarinwilcke Aug 18, 2022
392806c
refactor to move two new fields into LBV1ObjectBase
jtsodapop Aug 18, 2022
294f14d
addition of new test case for pdf documents
jtsodapop Aug 18, 2022
5588dbb
Delete test.py
jtsodapop Aug 18, 2022
64a3bc9
remove unnecessary comments
jtsodapop Aug 18, 2022
1dfd302
Merge pull request #666 from Labelbox/kkim/AL-3099
kkim-labelbox Aug 18, 2022
b11bc13
update to test image to account for new extra fields
jtsodapop Aug 18, 2022
896f96b
fix to test cases
jtsodapop Aug 18, 2022
f11ac19
Merge pull request #673 from Labelbox/jtso/al-3329
jtsodapop Aug 18, 2022
90716f4
[QQC-362] Document new project update restrictions regarding quality …
Aug 22, 2022
2d1065f
Modify return get_config return value [AL-3352]
mmarinwilcke Aug 23, 2022
667e400
changelog
mmarinwilcke Aug 23, 2022
24352e4
empty commit
mmarinwilcke Aug 23, 2022
d62f3b2
[QQC-362] Review feedback
Aug 23, 2022
2aa0972
Merge pull request #676 from Labelbox/rsun/QQC-362
richardsun0713 Aug 23, 2022
0b98bfc
Merge pull request #677 from Labelbox/mmw/AL-3352
kkim-labelbox Aug 23, 2022
e83f63e
Prep 3.26.1
Aug 23, 2022
8afcfb9
Merge pull request #679 from Labelbox/kkim/prep_3.26.1
kkim-labelbox Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Changelog

# Version 3.26.0 (2022-08-12)
# Version 3.26.1 (2022-08-23)
### Changed
* `ModelRun.get_config()`
* Modifies get_config to return un-nested Model Run config
### Added
* `ModelRun.update_config()`
* Updates model run training metadata
* `ModelRun.reset_config()`
* Resets model run training metadata
* `ModelRun.get_config()`
* Fetches model run training metadata

### Changed
* `Model.create_model_run()`
* Add training metadata config as a model run creation param

# Version 3.26.0 (2022-08-15)
## Added
* `Batch.delete()` which will delete an existing `Batch`
* `Batch.delete_labels()` which will delete all `Label`’s created after a `Project`’s mode has been set to batch.
Expand Down Expand Up @@ -663,7 +679,3 @@ a `Label`. Default value is 0.0.

## Version 2.2 (2019-10-18)
Changelog not maintained before version 2.2.

### Changed
* `Model.create_model_run()`
* Add training metadata config as a model run creation param
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
copyright = '2021, Labelbox'
author = 'Labelbox'

release = '3.25.1'
release = '3.26.1'

# -- General configuration ---------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion labelbox/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = "labelbox"
__version__ = "3.26.0"
__version__ = "3.26.1"

from labelbox.client import Client
from labelbox.schema.project import Project
Expand Down
1 change: 1 addition & 0 deletions labelbox/data/serialization/labelbox_v1/label.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class LBV1Label(BaseModel):
skipped: Optional[bool] = Extra('Skipped')
media_type: Optional[str] = Extra('media_type')
data_split: Optional[str] = Extra('Data Split')
global_key: Optional[str] = Extra('Global Key')

def to_common(self) -> Label:
if isinstance(self.label, list):
Expand Down
6 changes: 5 additions & 1 deletion labelbox/data/serialization/labelbox_v1/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class LBV1ObjectBase(LBV1Feature):
instanceURI: Optional[str] = None
classifications: List[Union[LBV1Text, LBV1Radio, LBV1Dropdown,
LBV1Checklist]] = []
page: Optional[int] = None
unit: Optional[str] = None

def dict(self, *args, **kwargs) -> Dict[str, Any]:
res = super().dict(*args, **kwargs)
Expand Down Expand Up @@ -262,7 +264,7 @@ def from_common(cls, text_entity: TextEntity,
class LBV1Objects(BaseModel):
objects: List[Union[LBV1Line, LBV1Point, LBV1Polygon, LBV1Rectangle,
LBV1TextEntity, LBV1Mask, LBV1TIPoint, LBV1TILine,
LBV1TIPolygon, LBV1TIRectangle]]
LBV1TIPolygon, LBV1TIRectangle,]]

def to_common(self) -> List[ObjectAnnotation]:
objects = [
Expand All @@ -285,6 +287,8 @@ def to_common(self) -> List[ObjectAnnotation]:
'color': obj.color,
'feature_id': obj.feature_id,
'value': obj.value,
'page': obj.page,
'unit': obj.unit,
}) for obj in self.objects
]
return objects
Expand Down
2 changes: 2 additions & 0 deletions labelbox/schema/data_row.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class DataRow(DbObject, Updateable, BulkDeletable):

Attributes:
external_id (str): User-generated file name or identifier
global_key (str): User-generated globally unique identifier
row_data (str): Paths to local files are uploaded to Labelbox's server.
Otherwise, it's treated as an external URL.
updated_at (datetime)
Expand All @@ -33,6 +34,7 @@ class DataRow(DbObject, Updateable, BulkDeletable):
attachments (Relationship) `ToMany` relationship with AssetAttachment
"""
external_id = Field.String("external_id")
global_key = Field.String("global_key")
row_data = Field.String("row_data")
updated_at = Field.DateTime("updated_at")
created_at = Field.DateTime("created_at")
Expand Down
60 changes: 55 additions & 5 deletions labelbox/schema/model_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Status(Enum):
FAILED = "FAILED"

def upsert_labels(self, label_ids, timeout_seconds=60):
""" Adds data rows and labels to a model run
""" Adds data rows and labels to a Model Run
Args:
label_ids (list): label ids to insert
timeout_seconds (float): Max waiting time, in seconds.
Expand Down Expand Up @@ -75,7 +75,7 @@ def upsert_labels(self, label_ids, timeout_seconds=60):
timeout_seconds=timeout_seconds)

def upsert_data_rows(self, data_row_ids, timeout_seconds=60):
""" Adds data rows to a model run without any associated labels
""" Adds data rows to a Model Run without any associated labels
Args:
data_row_ids (list): data row ids to add to mea
timeout_seconds (float): Max waiting time, in seconds.
Expand Down Expand Up @@ -167,7 +167,7 @@ def model_run_data_rows(self):
['annotationGroups', 'pageInfo', 'endCursor'])

def delete(self):
""" Deletes specified model run.
""" Deletes specified Model Run.

Returns:
Query execution success.
Expand All @@ -178,10 +178,10 @@ def delete(self):
self.client.execute(query_str, {ids_param: str(self.uid)})

def delete_model_run_data_rows(self, data_row_ids: List[str]):
""" Deletes data rows from model runs.
""" Deletes data rows from Model Runs.

Args:
data_row_ids (list): List of data row ids to delete from the model run.
data_row_ids (list): List of data row ids to delete from the Model Run.
Returns:
Query execution success.
"""
Expand Down Expand Up @@ -262,6 +262,56 @@ def update_status(self,
},
experimental=True)

@experimental
def update_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Updates the Model Run's training metadata config
Args:
config (dict): A dictionary of keys and values
Returns:
Model Run id and updated training metadata
"""
data: Dict[str, Any] = {'config': config}
res = self.client.execute(
"""mutation updateModelRunConfigPyApi($modelRunId: ID!, $data: UpdateModelRunConfigInput!){
updateModelRunConfig(modelRun: {id : $modelRunId}, data: $data){trainingMetadata}
}
""", {
'modelRunId': self.uid,
'data': data
},
experimental=True)
return res["updateModelRunConfig"]

@experimental
def reset_config(self) -> Dict[str, Any]:
"""
Resets Model Run's training metadata config
Returns:
Model Run id and reset training metadata
"""
res = self.client.execute(
"""mutation resetModelRunConfigPyApi($modelRunId: ID!){
resetModelRunConfig(modelRun: {id : $modelRunId}){trainingMetadata}
}
""", {'modelRunId': self.uid},
experimental=True)
return res["resetModelRunConfig"]

@experimental
def get_config(self) -> Dict[str, Any]:
"""
Gets Model Run's training metadata
Returns:
training metadata as a dictionary
"""
res = self.client.execute("""query ModelRunPyApi($modelRunId: ID!){
modelRun(where: {id : $modelRunId}){trainingMetadata}
}
""", {'modelRunId': self.uid},
experimental=True)
return res["modelRun"]["trainingMetadata"]

@experimental
def export_labels(
self,
Expand Down
16 changes: 16 additions & 0 deletions labelbox/schema/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ class QueueMode(Enum):
Dataset = "Dataset"

def update(self, **kwargs):
""" Updates this project with the specified attributes

Args:
kwargs: a dictionary containing attributes to be upserted

Note that the quality setting cannot be changed after a project has been created. The quality mode
for a project is inferred through the following attributes:
Benchmark:
auto_audit_number_of_labels = 1
auto_audit_percentage = 1.0
Consensus:
auto_audit_number_of_labels > 1
auto_audit_percentage <= 1.0
Attempting to switch between benchmark and consensus modes is an invalid operation and will result
in an error.
"""
mode: Optional[Project.QueueMode] = kwargs.pop("queue_mode", None)
if mode:
self._update_queue_mode(mode)
Expand Down
130 changes: 130 additions & 0 deletions tests/data/assets/labelbox_v1/pdf_export.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
[{
"ID": "cl6xnzi4a7ldn0729381g7104",
"DataRow ID": "cl6xnv9h61fv0085yhtoq06ht",
"Labeled Data": "https://storage.labelbox.com/ckcz6bubudyfi0855o1dt1g9s%2F4cef4e08-e13d-8a5e-fbbf-c7624babb490-Airbnb_%20Labelbox%20-%20Focus%20on%20Workforce%20-%20Labelbox%20Labeling%20Operations%20(1).pdf?Expires=1661971050348&KeyName=labelbox-assets-key-3&Signature=JK6ral5CXF7T9Q5LaQqKvJy5A2A",
"Label": {
"objects": [{
"featureId": "cl6xnzjpq0dmr07yocs2vfot8",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 144.68,
"left": 107.84,
"height": 441.6,
"width": 9.48
},
"page": 0,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmr07yocs2vfot8?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dms07yobwv68gxf",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 162.73,
"left": 32.45,
"height": 388.17,
"width": 101.66
},
"page": 4,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dms07yobwv68gxf?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dmt07yo8pp45gru",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 223.26,
"left": 251.42,
"height": 457.04,
"width": 186.78
},
"page": 7,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmt07yo8pp45gru?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dmu07yo2qik0en4",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 32.52,
"left": 218.17,
"height": 231.73,
"width": 110.56
},
"page": 6,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmu07yo2qik0en4?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dmv07yo7phz7ofz",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 117.39,
"left": 4.25,
"height": 456.92,
"width": 164.83
},
"page": 7,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmv07yo7phz7ofz?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dmw07yofocp6uf6",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 82.13,
"left": 217.28,
"height": 279.76,
"width": 82.43
},
"page": 8,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmw07yofocp6uf6?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}, {
"featureId": "cl6xnzjpq0dmx07yo0qh40z0n",
"schemaId": "cl6xnuwt95lqq07330tbb3mfd",
"color": "#1CE6FF",
"title": "boxy",
"value": "boxy",
"bbox": {
"top": 298.12,
"left": 83.34,
"height": 203.83,
"width": 0.38
},
"page": 3,
"unit": "POINTS",
"instanceURI": "https://api.labelbox.com/masks/feature/cl6xnzjpq0dmx07yo0qh40z0n?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJja2NjOWZtbXc0aGNkMDczOHFpeWM2YW54Iiwib3JnYW5pemF0aW9uSWQiOiJja2N6NmJ1YnVkeWZpMDg1NW8xZHQxZzlzIiwiaWF0IjoxNjYwNzYxNDUwLCJleHAiOjE2NjMzNTM0NTB9.X4-j6zee8o685PUrL9C6oC2m6TayKuJQHhN8iLgG8kI"
}],
"classifications": [],
"relationships": []
},
"Created By": "jtso@labelbox.com",
"Project Name": "PDF MAL Test",
"Created At": "2022-08-17T18:37:18.000Z",
"Updated At": "2022-08-17T18:37:20.073Z",
"Seconds to Label": 15.003,
"External ID": "Airbnb_ Labelbox - Focus on Workforce - Labelbox Labeling Operations (1).pdf",
"Global Key": null,
"Agreement": -1,
"Benchmark Agreement": -1,
"Benchmark ID": null,
"Dataset Name": "PDF ",
"Reviews": [],
"View Label": "https://editor.labelbox.com?project=cl6xntneb7t28072bggdydv7a&label=cl6xnzi4a7ldn0729381g7104",
"Has Open Issues": 0,
"Skipped": false
}]
45 changes: 45 additions & 0 deletions tests/data/serialization/labelbox_v1/test_document.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import json
from typing import Dict, Any

from labelbox.data.serialization.labelbox_v1.converter import LBV1Converter

IGNORE_KEYS = [
"Data Split", "media_type", "DataRow Metadata", "Media Attributes"
]


def round_dict(data: Dict[str, Any]) -> Dict[str, Any]:
for key in data:
if isinstance(data[key], float):
data[key] = int(data[key])
elif isinstance(data[key], dict):
data[key] = round_dict(data[key])
return data


def test_pdf():
"""
Tests an export from a pdf document with only bounding boxes
"""
payload = json.load(
open('tests/data/assets/labelbox_v1/pdf_export.json', 'r'))
collection = LBV1Converter.deserialize(payload)
serialized = next(LBV1Converter.serialize(collection))

payload = payload[0] # only one document in the export

serialized = {k: v for k, v in serialized.items() if k not in IGNORE_KEYS}

assert serialized.keys() == payload.keys()
for key in payload.keys():
if key == 'Label':
serialized_no_classes = [{
k: v for k, v in dic.items() if k != 'classifications'
} for dic in serialized[key]['objects']]
serialized_round = [
round_dict(dic) for dic in serialized_no_classes
]
payload_round = [round_dict(dic) for dic in payload[key]['objects']]
assert payload_round == serialized_round
else:
assert serialized[key] == payload[key]
Loading