From c5bc124041d8f6b91bacf624b2b163194a74f5bb Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Mon, 6 Feb 2023 13:11:11 +0100 Subject: [PATCH 1/6] Handle default values properly --- labelbox/schema/model_run.py | 17 +++++++++------ labelbox/schema/project.py | 42 ++++++++++++++++++++---------------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index 701fd38b7..ac4b19c17 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -456,13 +456,16 @@ def export_labels( """ - def export_v2(self, task_name: str, - params: Optional[ModelRunExportParams]) -> Task: - _params = params or {} + def export_v2(self, + task_name: Optional[str] = None, + params: Optional[ModelRunExportParams] = {}) -> Task: mutation_name = "exportDataRowsInModelRun" create_task_query_str = """mutation exportDataRowsInModelRunPyApi($input: ExportDataRowsInModelRunInput!){ %s(input: $input) {taskId} } """ % (mutation_name) + if (task_name is None): + task_name = f'Export Data Rows in Model Run - {self.name}' + params = { "input": { "taskName": task_name, @@ -471,13 +474,13 @@ def export_v2(self, task_name: str, }, "params": { "includeAttachments": - _params.get('attachments', False), + params.get('attachments', False), "includeMediaAttributes": - _params.get('media_attributes', False), + params.get('media_attributes', False), "includeMetadata": - _params.get('metadata_fields', False), + params.get('metadata_fields', False), "includeDataRowDetails": - _params.get('data_row_details', False), + params.get('data_row_details', False), # Arguments locked based on exectuion context "includeProjectDetails": False, diff --git a/labelbox/schema/project.py b/labelbox/schema/project.py index 277bf6622..e601b4dda 100644 --- a/labelbox/schema/project.py +++ b/labelbox/schema/project.py @@ -375,6 +375,15 @@ def _validate_datetime(string_date: str) -> bool: self.uid) time.sleep(sleep_time) + defaultExportParams: ProjectExportParams = { + "attachments": False, + "media_attributes": False, + "metadata_fields": False, + "data_row_details": False, + "project_details": False, + "labels": False, + "performance_details": False + } """ Creates a project run export task with the given params and returns the task. @@ -382,18 +391,13 @@ def _validate_datetime(string_date: str) -> bool: """ - def export_v2(self, task_name: str, - params: Optional[ProjectExportParams]) -> Task: - defaultParams: ProjectExportParams = { - "attachments": False, - "media_attributes": False, - "metadata_fields": False, - "data_row_details": False, - "project_details": False, - "labels": False, - "performance_details": False - } - _params: ProjectExportParams = params if params is not None else defaultParams + def export_v2(self, + task_name: Optional[str] = None, + params: ProjectExportParams = defaultExportParams) -> Task: + + if (task_name is None): + task_name = f'Export Data Rows in Project - {self.name}' + mutation_name = "exportDataRowsInProject" create_task_query_str = """mutation exportDataRowsInProjectPyApi($input: ExportDataRowsInProjectInput!){ %s(input: $input) {taskId} } @@ -406,19 +410,19 @@ def export_v2(self, task_name: str, }, "params": { "includeAttachments": - _params.get('attachments', False), + params.get('attachments', False), "includeMediaAttributes": - _params.get('media_attributes', False), + params.get('media_attributes', False), "includeMetadata": - _params.get('metadata_fields', False), + params.get('metadata_fields', False), "includeDataRowDetails": - _params.get('data_row_details', False), + params.get('data_row_details', False), "includeProjectDetails": - _params.get('project_details', False), + params.get('project_details', False), "includeLabels": - _params.get('labels', False), + params.get('labels', False), "includePerformanceDetails": - _params.get('performance_details', False), + params.get('performance_details', False), }, } } From 35c03630018be18749436c6a8f762087fc510858 Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Tue, 7 Feb 2023 20:33:46 +0100 Subject: [PATCH 2/6] Add task result --- labelbox/schema/task.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/labelbox/schema/task.py b/labelbox/schema/task.py index 7ce4279e2..c86297266 100644 --- a/labelbox/schema/task.py +++ b/labelbox/schema/task.py @@ -2,6 +2,7 @@ import requests import time from typing import TYPE_CHECKING, Callable, Optional, Dict, Any, List +import ndjson from labelbox.exceptions import ResourceNotFoundError from labelbox.orm.db_object import DbObject @@ -99,12 +100,14 @@ def result(self) -> List[Dict[str, Any]]: raise ValueError(f"Job failed. Errors : {self.errors}") else: result = self._fetch_remote_json() - return [{ - 'id': data_row['id'], - 'external_id': data_row.get('externalId'), - 'row_data': data_row['rowData'], - 'global_key': data_row.get('globalKey'), - } for data_row in result['createdDataRows']] + if 'createdDataRows' in result: + return [{ + 'id': data_row['id'], + 'external_id': data_row.get('externalId'), + 'row_data': data_row['rowData'], + 'global_key': data_row.get('globalKey'), + } for data_row in result['createdDataRows']] + return result @property def failed_data_rows(self) -> Optional[Dict[str, Any]]: @@ -124,13 +127,16 @@ def _fetch_remote_json(self) -> Dict[str, Any]: def download_result(): response = requests.get(self.result_url) response.raise_for_status() - return response.json() - - if self.name != 'JSON Import': - raise ValueError( - "Task result is only supported for `JSON Import` tasks." - " Download task.result_url manually to access the result for other tasks." - ) + try: + return response.json() + except Exception as e: + pass + try: + return ndjson.loads(response.text) + except Exception as e: + raise ValueError( + "Unable to parse JSON or NDJSON. Please contact support for more details. Download task.result_url manually to access the result for other tasks." + ) if self.status != "IN_PROGRESS": return download_result() From a28b9eee5a8cbe9a871e69235c4d21841071b458 Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Tue, 7 Feb 2023 20:33:56 +0100 Subject: [PATCH 3/6] CR changes --- labelbox/schema/model_run.py | 16 +++++++++------- labelbox/schema/project.py | 35 ++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/labelbox/schema/model_run.py b/labelbox/schema/model_run.py index ac4b19c17..56a42c336 100644 --- a/labelbox/schema/model_run.py +++ b/labelbox/schema/model_run.py @@ -458,7 +458,7 @@ def export_labels( def export_v2(self, task_name: Optional[str] = None, - params: Optional[ModelRunExportParams] = {}) -> Task: + params: Optional[ModelRunExportParams] = None) -> Task: mutation_name = "exportDataRowsInModelRun" create_task_query_str = """mutation exportDataRowsInModelRunPyApi($input: ExportDataRowsInModelRunInput!){ %s(input: $input) {taskId} } @@ -466,7 +466,9 @@ def export_v2(self, if (task_name is None): task_name = f'Export Data Rows in Model Run - {self.name}' - params = { + _params = params or ModelRunExportParams() + + queryParams = { "input": { "taskName": task_name, "filters": { @@ -474,13 +476,13 @@ def export_v2(self, }, "params": { "includeAttachments": - params.get('attachments', False), + _params.get('attachments', False), "includeMediaAttributes": - params.get('media_attributes', False), + _params.get('media_attributes', False), "includeMetadata": - params.get('metadata_fields', False), + _params.get('metadata_fields', False), "includeDataRowDetails": - params.get('data_row_details', False), + _params.get('data_row_details', False), # Arguments locked based on exectuion context "includeProjectDetails": False, @@ -493,7 +495,7 @@ def export_v2(self, } res = self.client.execute( create_task_query_str, - params, + queryParams, ) res = res[mutation_name] task_id = res["taskId"] diff --git a/labelbox/schema/project.py b/labelbox/schema/project.py index e601b4dda..a7f596001 100644 --- a/labelbox/schema/project.py +++ b/labelbox/schema/project.py @@ -375,15 +375,6 @@ def _validate_datetime(string_date: str) -> bool: self.uid) time.sleep(sleep_time) - defaultExportParams: ProjectExportParams = { - "attachments": False, - "media_attributes": False, - "metadata_fields": False, - "data_row_details": False, - "project_details": False, - "labels": False, - "performance_details": False - } """ Creates a project run export task with the given params and returns the task. @@ -393,11 +384,21 @@ def _validate_datetime(string_date: str) -> bool: def export_v2(self, task_name: Optional[str] = None, - params: ProjectExportParams = defaultExportParams) -> Task: + params: Optional[ProjectExportParams] = None) -> Task: if (task_name is None): task_name = f'Export Data Rows in Project - {self.name}' + _params = params or ProjectExportParams({ + "attachments": False, + "media_attributes": False, + "metadata_fields": False, + "data_row_details": False, + "project_details": False, + "labels": False, + "performance_details": False + }) + mutation_name = "exportDataRowsInProject" create_task_query_str = """mutation exportDataRowsInProjectPyApi($input: ExportDataRowsInProjectInput!){ %s(input: $input) {taskId} } @@ -410,19 +411,19 @@ def export_v2(self, }, "params": { "includeAttachments": - params.get('attachments', False), + _params.get('attachments', False), "includeMediaAttributes": - params.get('media_attributes', False), + _params.get('media_attributes', False), "includeMetadata": - params.get('metadata_fields', False), + _params.get('metadata_fields', False), "includeDataRowDetails": - params.get('data_row_details', False), + _params.get('data_row_details', False), "includeProjectDetails": - params.get('project_details', False), + _params.get('project_details', False), "includeLabels": - params.get('labels', False), + _params.get('labels', False), "includePerformanceDetails": - params.get('performance_details', False), + _params.get('performance_details', False), }, } } From 687421e353c92c07e25c19e7554b5d35063cc148 Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Tue, 7 Feb 2023 23:00:50 +0100 Subject: [PATCH 4/6] Fix return type --- labelbox/schema/task.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/labelbox/schema/task.py b/labelbox/schema/task.py index c86297266..d40f10036 100644 --- a/labelbox/schema/task.py +++ b/labelbox/schema/task.py @@ -1,7 +1,7 @@ import logging import requests import time -from typing import TYPE_CHECKING, Callable, Optional, Dict, Any, List +from typing import TYPE_CHECKING, Callable, Optional, Dict, Any, List, Union import ndjson from labelbox.exceptions import ResourceNotFoundError @@ -93,7 +93,7 @@ def errors(self) -> Optional[Dict[str, Any]]: return None @property - def result(self) -> List[Dict[str, Any]]: + def result(self) -> Union[List[Dict[str, Any]], Dict[str, Any]]: """ Fetch the result for an import task. """ if self.status == "FAILED": From b1c55c2ccc515db777d08b3166897447c1333bec Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Thu, 9 Feb 2023 10:53:28 +0100 Subject: [PATCH 5/6] Handle export task type --- labelbox/schema/task.py | 28 +++++++++++-------- .../annotation_import/test_model_run.py | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/labelbox/schema/task.py b/labelbox/schema/task.py index d40f10036..31f3390ee 100644 --- a/labelbox/schema/task.py +++ b/labelbox/schema/task.py @@ -39,6 +39,7 @@ class Task(DbObject): status = Field.String("status") completion_percentage = Field.Float("completion_percentage") result_url = Field.String("result_url", "result") + type = Field.String("type") _user: Optional["User"] = None # Relationships @@ -100,14 +101,15 @@ def result(self) -> Union[List[Dict[str, Any]], Dict[str, Any]]: raise ValueError(f"Job failed. Errors : {self.errors}") else: result = self._fetch_remote_json() - if 'createdDataRows' in result: - return [{ - 'id': data_row['id'], - 'external_id': data_row.get('externalId'), - 'row_data': data_row['rowData'], - 'global_key': data_row.get('globalKey'), - } for data_row in result['createdDataRows']] - return result + if self.type == 'export-data-rows': + return result + + return [{ + 'id': data_row['id'], + 'external_id': data_row.get('externalId'), + 'row_data': data_row['rowData'], + 'global_key': data_row.get('globalKey'), + } for data_row in result['createdDataRows']] @property def failed_data_rows(self) -> Optional[Dict[str, Any]]: @@ -134,9 +136,13 @@ def download_result(): try: return ndjson.loads(response.text) except Exception as e: - raise ValueError( - "Unable to parse JSON or NDJSON. Please contact support for more details. Download task.result_url manually to access the result for other tasks." - ) + raise ValueError("Failed to parse task JSON/NDJSON result.") + + if self.name != 'JSON Import' and self.type != 'export-data-rows': + raise ValueError( + "Task result is only supported for `JSON Import` and `export` tasks." + " Download task.result_url manually to access the result for other tasks." + ) if self.status != "IN_PROGRESS": return download_result() diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 00f627eca..02b23c6fa 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -117,7 +117,7 @@ def test_model_run_export_labels(model_run_with_model_run_data_rows): assert len(labels) == 3 -@pytest.mark.skip(reason="feature under development") +# @pytest.mark.skip(reason="feature under development") def test_model_run_export_v2(model_run_with_model_run_data_rows, configured_project): task_name = "test_task" From e10b35af0d736d26c8f381207c9fdb08487f001a Mon Sep 17 00:00:00 2001 From: mnoszczak Date: Thu, 9 Feb 2023 22:41:03 +0100 Subject: [PATCH 6/6] Disable test --- tests/integration/annotation_import/test_model_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/annotation_import/test_model_run.py b/tests/integration/annotation_import/test_model_run.py index 02b23c6fa..00f627eca 100644 --- a/tests/integration/annotation_import/test_model_run.py +++ b/tests/integration/annotation_import/test_model_run.py @@ -117,7 +117,7 @@ def test_model_run_export_labels(model_run_with_model_run_data_rows): assert len(labels) == 3 -# @pytest.mark.skip(reason="feature under development") +@pytest.mark.skip(reason="feature under development") def test_model_run_export_v2(model_run_with_model_run_data_rows, configured_project): task_name = "test_task"