Skip to content

Commit 84bbd4a

Browse files
authored
Export Scale task info (#308)
* add slice and dataset export methods; helper util * semver bump and changelog
1 parent 19fc293 commit 84bbd4a

File tree

6 files changed

+107
-2
lines changed

6 files changed

+107
-2
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to the [Nucleus Python Client](https://github.com/scaleapi/n
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.12.3](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.12.3) - 2022-06-02
9+
10+
### Added
11+
12+
- New methods to export associated Scale task info at either the item or scene level.
13+
- `Dataset.export_scale_task_info`
14+
- `Slice.export_scale_task_info`
15+
16+
817
## [0.12.2](https://github.com/scaleapi/nucleus-python-client/releases/tag/v0.12.2) - 2022-06-02
918

1019
### Added

nucleus/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
AUTOTAGS_KEY = "autotags"
2828
AUTOTAG_SCORE_THRESHOLD = "score_threshold"
2929
EXPORTED_ROWS = "exportedRows"
30+
EXPORTED_SCALE_TASK_INFO_ROWS = "exportedScaleTaskInfoRows"
3031
CAMERA_MODEL_KEY = "camera_model"
3132
CAMERA_PARAMS_KEY = "camera_params"
3233
CLASS_PDF_KEY = "class_pdf"
@@ -111,6 +112,8 @@
111112
REFERENCE_ID_KEY = "reference_id"
112113
BACKEND_REFERENCE_ID_KEY = "ref_id" # TODO(355762): Our backend returns this instead of the "proper" key sometimes.
113114
REQUEST_ID_KEY = "requestId"
115+
SCALE_TASK_INFO_KEY = "scale_task_info"
116+
SCENE_KEY = "scene"
114117
SCENES_KEY = "scenes"
115118
SERIALIZED_REQUEST_KEY = "serialized_request"
116119
SEGMENTATIONS_KEY = "segmentations"

nucleus/dataset.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
convert_export_payload,
1919
format_dataset_item_response,
2020
format_prediction_response,
21+
format_scale_task_info_response,
2122
paginate_generator,
2223
serialize_and_write_to_presigned_url,
2324
)
@@ -1278,6 +1279,36 @@ def export_predictions(self, model):
12781279
)
12791280
return format_prediction_response({ANNOTATIONS_KEY: json_response})
12801281

1282+
def export_scale_task_info(self):
1283+
"""Fetches info for all linked Scale tasks of items/scenes in the dataset.
1284+
1285+
Returns:
1286+
A list of dicts, each with two keys, respectively mapping to items/scenes
1287+
and info on their corresponding Scale tasks within the dataset::
1288+
1289+
List[{
1290+
"item" | "scene": Union[:class:`DatasetItem`, :class:`Scene`],
1291+
"scale_task_info": {
1292+
"task_id": str,
1293+
"subtask_id": str,
1294+
"task_status": str,
1295+
"task_audit_status": str,
1296+
"task_audit_review_comment": Optional[str],
1297+
"project_name": str,
1298+
"batch": str,
1299+
"created_at": str,
1300+
"completed_at": Optional[str]
1301+
}[]
1302+
}]
1303+
1304+
"""
1305+
response = self._client.make_request(
1306+
payload=None,
1307+
route=f"dataset/{self.id}/exportScaleTaskInfo",
1308+
requests_command=requests.get,
1309+
)
1310+
return format_scale_task_info_response(response)
1311+
12811312
def calculate_evaluation_metrics(self, model, options: dict = None):
12821313
"""Starts computation of evaluation metrics for a model on the dataset.
12831314

nucleus/slice.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
KeyErrorDict,
1313
convert_export_payload,
1414
format_dataset_item_response,
15+
format_scale_task_info_response,
1516
paginate_generator,
1617
)
1718

@@ -265,6 +266,37 @@ def export_predictions(
265266
)
266267
return convert_export_payload(api_payload[EXPORTED_ROWS], True)
267268

269+
def export_scale_task_info(self):
270+
"""Fetches info for all linked Scale tasks of items/scenes in the slice.
271+
272+
Returns:
273+
A list of dicts, each with two keys, respectively mapping to items/scenes
274+
and info on their corresponding Scale tasks within the dataset::
275+
276+
List[{
277+
"item" | "scene": Union[:class:`DatasetItem`, :class:`Scene`],
278+
"scale_task_info": {
279+
"task_id": str,
280+
"subtask_id": str,
281+
"task_status": str,
282+
"task_audit_status": str,
283+
"task_audit_review_comment": Optional[str],
284+
"project_name": str,
285+
"batch": str,
286+
"created_at": str,
287+
"completed_at": Optional[str]
288+
}[]
289+
}]
290+
291+
"""
292+
response = self._client.make_request(
293+
payload=None,
294+
route=f"slice/{self.id}/exportScaleTaskInfo",
295+
requests_command=requests.get,
296+
)
297+
# TODO: implement format function with nice keying
298+
return format_scale_task_info_response(response)
299+
268300
def send_to_labeling(self, project_id: str):
269301
"""Send items in the Slice as tasks to a Scale labeling project.
270302

nucleus/utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
BOX_TYPE,
2929
CATEGORY_TYPE,
3030
CUBOID_TYPE,
31+
EXPORTED_SCALE_TASK_INFO_ROWS,
3132
ITEM_KEY,
3233
KEYPOINTS_TYPE,
3334
LAST_PAGE,
@@ -39,6 +40,8 @@
3940
POLYGON_TYPE,
4041
PREDICTIONS_KEY,
4142
REFERENCE_ID_KEY,
43+
SCALE_TASK_INFO_KEY,
44+
SCENE_KEY,
4245
SEGMENTATION_TYPE,
4346
)
4447
from .dataset_item import DatasetItem
@@ -161,7 +164,7 @@ def format_dataset_item_response(response: dict) -> dict:
161164
Args:
162165
response: JSON dictionary response from REST endpoint
163166
Returns:
164-
item_dict: A dictionary with two entries, one for the dataset item, and annother
167+
item_dict: A dictionary with two entries, one for the dataset item, and another
165168
for all of the associated annotations.
166169
"""
167170
if ANNOTATIONS_KEY not in response:
@@ -188,6 +191,33 @@ def format_dataset_item_response(response: dict) -> dict:
188191
}
189192

190193

194+
def format_scale_task_info_response(response: dict) -> Union[Dict, List[Dict]]:
195+
"""Format the raw client response into api objects.
196+
197+
Args:
198+
response: JSON dictionary response from REST endpoint
199+
Returns:
200+
A dictionary with two entries, one for the dataset item, and another
201+
for all of the associated Scale tasks.
202+
"""
203+
if EXPORTED_SCALE_TASK_INFO_ROWS not in response:
204+
# Payload is empty so an error occurred
205+
return response
206+
207+
ret = []
208+
for row in response[EXPORTED_SCALE_TASK_INFO_ROWS]:
209+
if ITEM_KEY in row:
210+
ret.append(
211+
{
212+
ITEM_KEY: DatasetItem.from_json(row[ITEM_KEY]),
213+
SCALE_TASK_INFO_KEY: row[SCALE_TASK_INFO_KEY],
214+
}
215+
)
216+
elif SCENE_KEY in row:
217+
ret.append(row)
218+
return ret
219+
220+
191221
def convert_export_payload(api_payload, has_predictions: bool = False):
192222
"""Helper function to convert raw JSON to API objects
193223

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ exclude = '''
2121

2222
[tool.poetry]
2323
name = "scale-nucleus"
24-
version = "0.12.2"
24+
version = "0.12.3"
2525
description = "The official Python client library for Nucleus, the Data Platform for AI"
2626
license = "MIT"
2727
authors = ["Scale AI Nucleus Team <nucleusapi@scaleapi.com>"]

0 commit comments

Comments
 (0)