From 0c280d7ca24e56b8e2f17a58cd1b00c1fbdc0bea Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Sun, 13 Mar 2022 20:42:18 +0900 Subject: [PATCH 1/9] Add text and audio api --- README.md | 342 ++++++++++++++++++++++ fastlabel/__init__.py | 664 ++++++++++++++++++++++++++++++++++++++++-- fastlabel/const.py | 189 +++++++++++- fastlabel/utils.py | 51 +++- 4 files changed, 1199 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index d2b00e1..d0a141e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ _If you are using FastLabel prototype, please install version 0.2.2._ - [Multi Image](#multi-image) - [Video](#video) - [Video Classification](#video-classification) + - [Text](#text) + - [Text Classification](#text-classification) + - [Audio](#audio) + - [Audio Classification](#audio-classification) - [Common](#common) - [Annotation](#annotation) - [Project](#project) @@ -789,6 +793,344 @@ task_id = client.update_video_classification_task( ) ``` +### Text + +Supported following project types: + +- Text - NER + +#### Create Task + +Create a new task. + +```python +task_id = client.create_video_task( + project="YOUR_PROJECT_SLUG", + name="sample.txt", + file_path="./sample.txt" +) +``` + +Create a new task with pre-defined annotations. (Class should be configured on your project in advance) + +```python +task_id = client.create_text_task( + project="YOUR_PROJECT_SLUG", + name="sample.txt", + file_path="./sample.txt", + annotations=[{ + "type": "ner", + "value": "person", + "points": { + "start": 0, + "end": 10, + "text": "1234567890" + } + }] +) +``` + +##### Limitation +* You can upload up to a size of 2 MB. + +#### Find Task + +Find a single task. + +```python +task = client.find_text_task(task_id="YOUR_TASK_ID") +``` + +#### Get Tasks + +Get tasks. (Up to 10 tasks) + +```python +tasks = client.get_text_tasks(project="YOUR_PROJECT_SLUG") +``` + +#### Update Task + +Update a single task. + +```python +task_id = client.update_text_task( + task_id="YOUR_TASK_ID", + status="approved", + assignee="USER_SLUG", + tags=["tag1", "tag2"], + annotations=[{ + "type": "bbox", + "value": "bird", + "points": { + "start": 0, + "end": 10, + "text": "0123456789" + } + }] +) +``` + +#### Response + +Example of a single vide task object + +```python +{ + "id": "YOUR_TASK_ID", + "name": "cat.txt", + "url": "YOUR_TASK_URL", + "status": "registered", + "externalStatus": "registered", + "tags": [], + "assignee": "ASSIGNEE_NAME", + "reviewer": "REVIEWER_NAME", + "externalAssignee": "EXTERNAL_ASSIGNEE_NAME", + "externalReviewer": "EXTERNAL_REVIEWER_NAME", + "annotations": [ + { + "attributes": [], + "color": "#b36d18", + "points": { + "start": 0, + "end": 10, + "text": "0123456789" + }, + "title": "Cat", + "type": "ner", + "value": "cat" + } + ], + "createdAt": "2021-02-22T11:25:27.158Z", + "updatedAt": "2021-02-22T11:25:27.158Z" +} +``` + +### Text Classification + +Supported following project types: + +- Text - Classification (Single) + +#### Create Task + +Create a new task. + +```python +task_id = client.create_text_classification_task( + project="YOUR_PROJECT_SLUG", + name="sample.txt", + file_path="./sample.txt", + attributes=[ + { + "key": "attribute-key", + "value": "attribute-value" + } + ], +) +``` + +##### Limitation +* You can upload up to a size of 2 MB. + +#### Find Task + +Find a single task. + +```python +task = client.find_text_classification_task(task_id="YOUR_TASK_ID") +``` + +#### Get Tasks + +Get tasks. (Up to 1000 tasks) + +```python +tasks = client.get_text_classification_tasks(project="YOUR_PROJECT_SLUG") +``` + +#### Update Tasks + +Update a single task. + +```python +task_id = client.update_text_classification_task( + task_id="YOUR_TASK_ID", + status="approved", + assignee="USER_SLUG", + tags=["tag1", "tag2"] + attributes=[ + { + "key": "attribute-key", + "value": "attribute-value" + } + ], +) +``` + +### Audio + +Supported following project types: + +- Audio - Segmentation + +#### Create Task + +Create a new task. + +```python +task_id = client.create_audio_task( + project="YOUR_PROJECT_SLUG", + name="sample.mp3", + file_path="./sample.mp3" +) +``` + +Create a new task with pre-defined annotations. (Class should be configured on your project in advance) + +```python +task_id = client.create_audio_task( + project="YOUR_PROJECT_SLUG", + name="sample.mp3", + file_path="./sample.mp3", + annotations=[{ + "type": "segmentation", + "value": "person", + "points": [100, 200] + }] +) +``` + +##### Limitation +* You can upload up to a size of 120 MB. + +#### Find Task + +Find a single task. + +```python +task = client.find_video_task(task_id="YOUR_TASK_ID") +``` + +#### Get Tasks + +Get tasks. (Up to 10 tasks) + +```python +tasks = client.get_audio_tasks(project="YOUR_PROJECT_SLUG") +``` + +#### Update Task + +Update a single task. + +```python +task_id = client.update_audio_task( + task_id="YOUR_TASK_ID", + status="approved", + assignee="USER_SLUG", + tags=["tag1", "tag2"], + annotations=[{ + "type": "segmentation", + "value": "bird", + "points": [100, 200] + }] +) +``` + +#### Response + +Example of a single audio task object + +```python +{ + "id": "YOUR_TASK_ID", + "name": "cat.mp3", + "url": "YOUR_TASK_URL", + "status": "registered", + "externalStatus": "registered", + "tags": [], + "assignee": "ASSIGNEE_NAME", + "reviewer": "REVIEWER_NAME", + "externalAssignee": "EXTERNAL_ASSIGNEE_NAME", + "externalReviewer": "EXTERNAL_REVIEWER_NAME", + "annotations": [ + { + "attributes": [], + "color": "#b36d18", + "points": [100, 200], + "title": "Cat", + "type": "segmentation", + "value": "cat" + } + ], + "createdAt": "2021-02-22T11:25:27.158Z", + "updatedAt": "2021-02-22T11:25:27.158Z" +} +``` + +### Audio Classification + +Supported following project types: + +- Audio - Classification (Single) + +#### Create Task + +Create a new task. + +```python +task_id = client.create_audio_classification_task( + project="YOUR_PROJECT_SLUG", + name="sample.mp3", + file_path="./sample.mp3", + attributes=[ + { + "key": "attribute-key", + "value": "attribute-value" + } + ], +) +``` + +##### Limitation +* You can upload up to a size of 120 MB. + +#### Find Task + +Find a single task. + +```python +task = client.find_audio_classification_task(task_id="YOUR_TASK_ID") +``` + +#### Get Tasks + +Get tasks. (Up to 1000 tasks) + +```python +tasks = client.get_audio_classification_tasks(project="YOUR_PROJECT_SLUG") +``` + +#### Update Tasks + +Update a single task. + +```python +task_id = client.update_audio_classification_task( + task_id="YOUR_TASK_ID", + status="approved", + assignee="USER_SLUG", + tags=["tag1", "tag2"] + attributes=[ + { + "key": "attribute-key", + "value": "attribute-value" + } + ], +) +``` + ### Common APIs for update and delete are same over all tasks. diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index e799a46..69f5dc5 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -115,6 +115,58 @@ def find_video_task_by_name(self, project: str, task_name: str) -> dict: return None return tasks[0] + def find_text_task(self, task_id: str) -> dict: + """ + Find a single text task. + """ + endpoint = "tasks/text/" + task_id + return self.api.get_request(endpoint) + + def find_text_classification_task(self, task_id: str) -> dict: + """ + Find a single text classification task. + """ + endpoint = "tasks/text/classification/" + task_id + return self.api.get_request(endpoint) + + def find_text_task_by_name(self, project: str, task_name: str) -> dict: + """ + Find a single text task by name. + + project is slug of your project (Required). + task_name is a task name (Required). + """ + tasks = self.get_text_tasks(project=project, task_name=task_name) + if not tasks: + return None + return tasks[0] + + def find_audio_task(self, task_id: str) -> dict: + """ + Find a single audio task. + """ + endpoint = "tasks/audio/" + task_id + return self.api.get_request(endpoint) + + def find_audio_classification_task(self, task_id: str) -> dict: + """ + Find a single audio classification task. + """ + endpoint = "tasks/video/classification/" + task_id + return self.api.get_request(endpoint) + + def find_audio_task_by_name(self, project: str, task_name: str) -> dict: + """ + Find a single audio task by name. + + project is slug of your project (Required). + task_name is a task name (Required). + """ + tasks = self.get_audio_tasks(project=project, task_name=task_name) + if not tasks: + return None + return tasks[0] + # Task Get def get_image_tasks( @@ -332,6 +384,176 @@ def get_video_classification_tasks( params["limit"] = limit return self.api.get_request(endpoint, params=params) + def get_text_tasks( + self, + project: str, + status: str = None, + external_status: str = None, + tags: list = [], + task_name: str = None, + offset: int = None, + limit: int = 10, + ) -> list: + """ + Returns a list of text tasks. + Returns up to 10 at a time, to get more, set offset as the starting position + to fetch. + + project is slug of your project (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + tags is a list of tag (Optional). + task_name is a task name (Optional). + offset is the starting position number to fetch (Optional). + limit is the max number to fetch (Optional). + """ + if limit > 10: + raise FastLabelInvalidException( + "Limit must be less than or equal to 10.", 422 + ) + endpoint = "tasks/video" + params = {"project": project} + if status: + params["status"] = status + if external_status: + params["externalStatus"] = external_status + if tags: + params["tags"] = tags + if task_name: + params["taskName"] = task_name + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.api.get_request(endpoint, params=params) + + def get_text_classification_tasks( + self, + project: str, + status: str = None, + external_status: str = None, + tags: list = [], + task_name: str = None, + offset: int = None, + limit: int = 100, + ) -> list: + """ + Returns a list of text classification tasks. + Returns up to 1000 at a time, to get more, set offset as the starting position + to fetch. + + project is slug of your project (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) + tags is a list of tag (Optional). + offset is the starting position number to fetch (Optional). + limit is the max number to fetch (Optional). + """ + endpoint = "tasks/text/classification" + params = {"project": project} + if status: + params["status"] = status + if external_status: + params["externalStatus"] = external_status + if tags: + params["tags"] = tags + if task_name: + params["taskName"] = task_name + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.api.get_request(endpoint, params=params) + + def get_audio_tasks( + self, + project: str, + status: str = None, + external_status: str = None, + tags: list = [], + task_name: str = None, + offset: int = None, + limit: int = 10, + ) -> list: + """ + Returns a list of audio tasks. + Returns up to 10 at a time, to get more, set offset as the starting position + to fetch. + + project is slug of your project (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + tags is a list of tag (Optional). + task_name is a task name (Optional). + offset is the starting position number to fetch (Optional). + limit is the max number to fetch (Optional). + """ + if limit > 10: + raise FastLabelInvalidException( + "Limit must be less than or equal to 10.", 422 + ) + endpoint = "tasks/audio" + params = {"project": project} + if status: + params["status"] = status + if external_status: + params["externalStatus"] = external_status + if tags: + params["tags"] = tags + if task_name: + params["taskName"] = task_name + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.api.get_request(endpoint, params=params) + + def get_audio_classification_tasks( + self, + project: str, + status: str = None, + external_status: str = None, + tags: list = [], + task_name: str = None, + offset: int = None, + limit: int = 100, + ) -> list: + """ + Returns a list of audio classification tasks. + Returns up to 1000 at a time, to get more, set offset as the starting position + to fetch. + + project is slug of your project (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) + tags is a list of tag (Optional). + offset is the starting position number to fetch (Optional). + limit is the max number to fetch (Optional). + """ + endpoint = "tasks/audio/classification" + params = {"project": project} + if status: + params["status"] = status + if external_status: + params["externalStatus"] = external_status + if tags: + params["tags"] = tags + if task_name: + params["taskName"] = task_name + if offset: + params["offset"] = offset + if limit: + params["limit"] = limit + return self.api.get_request(endpoint, params=params) + def get_task_id_name_map( self, project: str, @@ -662,25 +884,29 @@ def create_video_classification_task( return self.api.post_request(endpoint, payload=payload) - # Task Update - - def update_task( + def create_text_task( self, - task_id: str, + project: str, + name: str, + file_path: str, status: str = None, external_status: str = None, + annotations: list = [], tags: list = [], **kwargs, ) -> str: """ - Update a single task. + Create a single text task. - task_id is an id of the task (Required). + project is slug of your project (Required). + name is an unique identifier of task in your project (Required). + file_path is a path to data. Supported extensions are txt (Required). status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', 'approved', 'declined' (Optional). external_status can be 'registered', 'completed', 'skipped', 'reviewed', - 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) - tags is a list of tag to be set (Optional). + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + annotations is a list of annotation to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). assignee is slug of assigned user (Optional). reviewer is slug of review user (Optional). approver is slug of approve user (Optional). @@ -688,38 +914,52 @@ def update_task( external_reviewer is slug of external review user (Optional). external_approver is slug of external approve user (Optional). """ - endpoint = "tasks/" + task_id - payload = {} + endpoint = "tasks/text" + if not utils.is_text_supported_ext(file_path): + raise FastLabelInvalidException("Supported extensions are txt.", 422) + if not utils.is_text_supported_size(file_path): + raise FastLabelInvalidException("Supported video size is under 2 MB.", 422) + + file = utils.base64_encode(file_path) + payload = {"project": project, "name": name, "file": file} if status: payload["status"] = status if external_status: payload["externalStatus"] = external_status + if annotations: + for annotation in annotations: + annotation["content"] = name + payload["annotations"] = annotations if tags: payload["tags"] = tags self.__fill_assign_users(payload, **kwargs) - return self.api.put_request(endpoint, payload=payload) + return self.api.post_request(endpoint, payload=payload) - def update_image_task( + def create_text_classification_task( self, - task_id: str, + project: str, + name: str, + file_path: str, status: str = None, external_status: str = None, + attributes: list = [], tags: list = [], - annotations: List[dict] = [], **kwargs, ) -> str: """ - Update a single image task. + Create a single text classification task. - task_id is an id of the task (Required). + project is slug of your project (Required). + name is an unique identifier of task in your project (Required). + file_path is a path to data. Supported extensions are mp4 (Required). status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', 'approved', 'declined' (Optional). external_status can be 'registered', 'completed', 'skipped', 'reviewed', - 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) - tags is a list of tag to be set (Optional). - annotations is a list of annotation to be set (Optional). + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + attributes is a list of attribute to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). assignee is slug of assigned user (Optional). reviewer is slug of review user (Optional). approver is slug of approve user (Optional). @@ -727,26 +967,224 @@ def update_image_task( external_reviewer is slug of external review user (Optional). external_approver is slug of external approve user (Optional). """ - endpoint = "tasks/image/" + task_id - payload = {} + endpoint = "tasks/text/classification" + if not utils.is_text_supported_ext(file_path): + raise FastLabelInvalidException("Supported extensions are txt.", 422) + if not utils.is_text_supported_size(file_path): + raise FastLabelInvalidException("Supported video size is under 2 MB.", 422) + + file = utils.base64_encode(file_path) + payload = {"project": project, "name": name, "file": file} if status: payload["status"] = status if external_status: payload["externalStatus"] = external_status + if attributes: + payload["attributes"] = attributes if tags: payload["tags"] = tags - if annotations: - for annotation in annotations: - # Since the content name is not passed in the sdk update api, - # the content will be filled on the server side. - annotation["content"] = "" - payload["annotations"] = annotations self.__fill_assign_users(payload, **kwargs) - return self.api.put_request(endpoint, payload=payload) + return self.api.post_request(endpoint, payload=payload) - def update_image_classification_task( + def create_audio_task( + self, + project: str, + name: str, + file_path: str, + status: str = None, + external_status: str = None, + annotations: list = [], + tags: list = [], + **kwargs, + ) -> str: + """ + Create a single audio task. + + project is slug of your project (Required). + name is an unique identifier of task in your project (Required). + file_path is a path to data. Supported extensions are mp4 (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + annotations is a list of annotation to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/audio" + if not utils.is_audio_supported_ext(file_path): + raise FastLabelInvalidException( + "Supported extensions are mp3, wav and w4a.", 422 + ) + if not utils.is_audio_supported_size(file_path): + raise FastLabelInvalidException( + "Supported video size is under 120 MB.", 422 + ) + + file = utils.base64_encode(file_path) + payload = {"project": project, "name": name, "file": file} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if annotations: + for annotation in annotations: + annotation["content"] = name + payload["annotations"] = annotations + if tags: + payload["tags"] = tags + + self.__fill_assign_users(payload, **kwargs) + + return self.api.post_request(endpoint, payload=payload) + + def create_audio_classification_task( + self, + project: str, + name: str, + file_path: str, + status: str = None, + external_status: str = None, + attributes: list = [], + tags: list = [], + **kwargs, + ) -> str: + """ + Create a single audio classification task. + + project is slug of your project (Required). + name is an unique identifier of task in your project (Required). + file_path is a path to data. Supported extensions are mp4 (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + attributes is a list of attribute to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/audio/classification" + if not utils.is_audio_supported_ext(file_path): + raise FastLabelInvalidException( + "Supported extensions are mp3, wav and w4a.", 422 + ) + if not utils.is_audio_supported_size(file_path): + raise FastLabelInvalidException( + "Supported audio size is under 120 MB.", 422 + ) + + file = utils.base64_encode(file_path) + payload = {"project": project, "name": name, "file": file} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if attributes: + payload["attributes"] = attributes + if tags: + payload["tags"] = tags + + self.__fill_assign_users(payload, **kwargs) + + return self.api.post_request(endpoint, payload=payload) + + # Task Update + + def update_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + tags: list = [], + **kwargs, + ) -> str: + """ + Update a single task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) + tags is a list of tag to be set (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if tags: + payload["tags"] = tags + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + + def update_image_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + tags: list = [], + annotations: List[dict] = [], + **kwargs, + ) -> str: + """ + Update a single image task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined'. (Optional) + tags is a list of tag to be set (Optional). + annotations is a list of annotation to be set (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/image/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if tags: + payload["tags"] = tags + if annotations: + for annotation in annotations: + # Since the content name is not passed in the sdk update api, + # the content will be filled on the server side. + annotation["content"] = "" + payload["annotations"] = annotations + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + + def update_image_classification_task( self, task_id: str, status: str = None, @@ -912,6 +1350,174 @@ def update_video_classification_task( return self.api.put_request(endpoint, payload=payload) + def update_text_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + tags: list = [], + annotations: List[dict] = [], + **kwargs, + ) -> str: + """ + Update a single text task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + tags is a list of tag to be set (Optional). + annotations is a list of annotation to be set (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/text/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if tags: + payload["tags"] = tags + if annotations: + for annotation in annotations: + annotation["content"] = "" + payload["annotations"] = annotations + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + + def update_text_classification_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + attributes: list = [], + tags: list = [], + **kwargs, + ) -> str: + """ + Update a single text classification task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + attributes is a list of attribute to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/text/classification/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if attributes: + payload["attributes"] = attributes + if tags: + payload["tags"] = tags + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + + def update_audio_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + tags: list = [], + annotations: List[dict] = [], + **kwargs, + ) -> str: + """ + Update a single audio task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + tags is a list of tag to be set (Optional). + annotations is a list of annotation to be set (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/audio/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if tags: + payload["tags"] = tags + if annotations: + for annotation in annotations: + annotation["content"] = "" + payload["annotations"] = annotations + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + + def update_audio_classification_task( + self, + task_id: str, + status: str = None, + external_status: str = None, + attributes: list = [], + tags: list = [], + **kwargs, + ) -> str: + """ + Update a single audio classification task. + + task_id is an id of the task (Required). + status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back', + 'approved', 'declined' (Optional). + external_status can be 'registered', 'completed', 'skipped', 'reviewed', + 'sent_back', 'approved', 'declined', 'customer_declined' (Optional). + attributes is a list of attribute to be set in advance (Optional). + tags is a list of tag to be set in advance (Optional). + assignee is slug of assigned user (Optional). + reviewer is slug of review user (Optional). + approver is slug of approve user (Optional). + external_assignee is slug of external assigned user (Optional). + external_reviewer is slug of external review user (Optional). + external_approver is slug of external approve user (Optional). + """ + endpoint = "tasks/audio/classification/" + task_id + payload = {} + if status: + payload["status"] = status + if external_status: + payload["externalStatus"] = external_status + if attributes: + payload["attributes"] = attributes + if tags: + payload["tags"] = tags + + self.__fill_assign_users(payload, **kwargs) + + return self.api.put_request(endpoint, payload=payload) + # Task Delete def delete_task(self, task_id: str) -> None: diff --git a/fastlabel/const.py b/fastlabel/const.py index 9a20a07..9701141 100644 --- a/fastlabel/const.py +++ b/fastlabel/const.py @@ -2,10 +2,187 @@ from enum import Enum # only 57 types -COLOR_PALETTE = [0, 0, 0, 228, 26, 28, 55, 126, 184, 77, 175, 74, 152, 78, 163, 255, 127, 0, 255, 255, 51, 166, 86, 40, 247, 129, 191, 153, 153, 153, 102, 194, 165, 252, 141, 98, 141, 160, 203, 231, 138, 195, 166, 216, 84, 255, 217, 47, 229, 196, 148, 179, 179, 179, 141, 211, 199, 255, 255, 179, 190, 186, 218, 251, 128, 114, 128, 177, 211, 253, 180, 98, 179, 222, 105, 252, 205, 229, 217, 217, 217, 188, 128, 189, 204, 235, 197, 255, 237, 111, 166, 206, 227, 31, 120, 180, 178, 223, 138, 51, 160, 44, 251, 154, 153, 227, 26, 28, 253, 191, 111, 255, 127, 0, 202, 178, 214, 106, 61, 154, 255, 255, 153, 177, 89, 40, 127, 201, 127, 190, 174, 212, 253, 192, 134, 255, 255, 153, 56, 108, 176, 240, 2, 127, 191, 91, 22, 102, 102, 102, 27, 158, 119, 217, 95, 2, 117, 112, 179, 231, 41, 138, 102, 166, 30, 230, 171, 2, 166, 118, 29, 102, 102, 102] +COLOR_PALETTE = [ + 0, + 0, + 0, + 228, + 26, + 28, + 55, + 126, + 184, + 77, + 175, + 74, + 152, + 78, + 163, + 255, + 127, + 0, + 255, + 255, + 51, + 166, + 86, + 40, + 247, + 129, + 191, + 153, + 153, + 153, + 102, + 194, + 165, + 252, + 141, + 98, + 141, + 160, + 203, + 231, + 138, + 195, + 166, + 216, + 84, + 255, + 217, + 47, + 229, + 196, + 148, + 179, + 179, + 179, + 141, + 211, + 199, + 255, + 255, + 179, + 190, + 186, + 218, + 251, + 128, + 114, + 128, + 177, + 211, + 253, + 180, + 98, + 179, + 222, + 105, + 252, + 205, + 229, + 217, + 217, + 217, + 188, + 128, + 189, + 204, + 235, + 197, + 255, + 237, + 111, + 166, + 206, + 227, + 31, + 120, + 180, + 178, + 223, + 138, + 51, + 160, + 44, + 251, + 154, + 153, + 227, + 26, + 28, + 253, + 191, + 111, + 255, + 127, + 0, + 202, + 178, + 214, + 106, + 61, + 154, + 255, + 255, + 153, + 177, + 89, + 40, + 127, + 201, + 127, + 190, + 174, + 212, + 253, + 192, + 134, + 255, + 255, + 153, + 56, + 108, + 176, + 240, + 2, + 127, + 191, + 91, + 22, + 102, + 102, + 102, + 27, + 158, + 119, + 217, + 95, + 2, + 117, + 112, + 179, + 231, + 41, + 138, + 102, + 166, + 30, + 230, + 171, + 2, + 166, + 118, + 29, + 102, + 102, + 102, +] -# under 512 MB. Actual size is 536870888 bytes, but to consider other attributes, minus 888 bytes. -# Because of V8's limitation, API only can accept the JSON string that length is under this. +# under 512 MB. Actual size is 536870888 bytes, but to consider other attributes, +# minus 888 bytes. +# Because of V8's limitation, API only can accept the JSON string that length is +# under this. SUPPORTED_CONTENTS_SIZE = 536870000 # API can accept under 250 MB @@ -14,6 +191,12 @@ # API can accept under 20 MB SUPPORTED_IMAGE_SIZE = 20 * math.pow(1024, 2) +# API can accept under 2 MB +SUPPORTED_TEXT_SIZE = 2 * math.pow(1024, 2) + +# API can accept under 120 MB +SUPPORTED_AUDIO_SIZE = 120 * math.pow(1024, 2) + class AnnotationType(Enum): bbox = "bbox" diff --git a/fastlabel/utils.py b/fastlabel/utils.py index b717f6e..2680822 100644 --- a/fastlabel/utils.py +++ b/fastlabel/utils.py @@ -1,9 +1,11 @@ -import os import base64 -import numpy as np -import geojson import json +import os from typing import List + +import geojson +import numpy as np + from fastlabel import const @@ -13,11 +15,19 @@ def base64_encode(file_path: str) -> str: def is_image_supported_ext(file_path: str) -> bool: - return file_path.lower().endswith(('.png', '.jpg', '.jpeg')) + return file_path.lower().endswith((".png", ".jpg", ".jpeg")) def is_video_supported_ext(file_path: str) -> bool: - return file_path.lower().endswith('.mp4') + return file_path.lower().endswith(".mp4") + + +def is_text_supported_ext(file_path: str) -> bool: + return file_path.lower().endswith(".txt") + + +def is_audio_supported_ext(file_path: str) -> bool: + return file_path.lower().endswith((".mp3", ".wav", ".m4a")) def is_image_supported_size(file_path: str) -> bool: @@ -28,8 +38,16 @@ def is_video_supported_size(file_path: str) -> bool: return os.path.getsize(file_path) <= const.SUPPORTED_VIDEO_SIZE +def is_text_supported_size(file_path: str) -> bool: + return os.path.getsize(file_path) <= const.SUPPORTED_TEXT_SIZE + + +def is_audio_supported_size(file_path: str) -> bool: + return os.path.getsize(file_path) <= const.SUPPORTED_AUDIO_SIZE + + def is_json_ext(file_name: str) -> bool: - return file_name.lower().endswith('.json') + return file_name.lower().endswith(".json") def get_basename(file_path: str) -> str: @@ -52,10 +70,8 @@ def reverse_points(points: List[int]) -> List[int]: reversed_points = [] for index, _ in enumerate(points): if index % 2 == 0: - reversed_points.insert( - 0, points[index + 1]) - reversed_points.insert( - 0, points[index]) + reversed_points.insert(0, points[index + 1]) + reversed_points.insert(0, points[index]) return reversed_points @@ -63,20 +79,26 @@ def is_clockwise(points: list) -> bool: """ points: [x1, y1, x2, y2, x3, y3, ... xn, yn] Sum over the edges, (x2 − x1)(y2 + y1). - If the result is positive the curve is clockwise, if it's negative the curve is counter-clockwise. + If the result is positive the curve is clockwise, + if it's negative the curve is counter-clockwise. The above is assumes a normal Cartesian coordinate system. HTML5 canvas, use an inverted Y-axis. Therefore If the area is negative, the curve is clockwise. """ - points_splitted = [points[idx:idx + 2] - for idx in range(0, len(points), 2)] + points_splitted = [points[idx : idx + 2] for idx in range(0, len(points), 2)] polygon_geo = geojson.Polygon(points_splitted) coords = np.array(list(geojson.utils.coords(polygon_geo))) xs, ys = map(list, zip(*coords)) xs.append(xs[0]) ys.append(ys[0]) - sum_edges = sum((xs[i] - xs[i - 1]) * (ys[i] + ys[i - 1]) for i in range(1, len(points_splitted))) / 2.0 + sum_edges = ( + sum( + (xs[i] - xs[i - 1]) * (ys[i] + ys[i - 1]) + for i in range(1, len(points_splitted)) + ) + / 2.0 + ) if sum_edges < 0: return True @@ -86,4 +108,3 @@ def is_clockwise(points: list) -> bool: def get_json_length(value) -> int: json_str = json.dumps(value) return len(json_str) - From f530b8a6fbf57e15c0a37ae1a8de8cde440b4e6f Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Mon, 14 Mar 2022 11:16:45 +0900 Subject: [PATCH 2/9] Update README --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d0a141e..cb0683d 100644 --- a/README.md +++ b/README.md @@ -891,11 +891,9 @@ Example of a single vide task object { "attributes": [], "color": "#b36d18", - "points": { - "start": 0, - "end": 10, - "text": "0123456789" - }, + "text": "0123456789", + "start": 0, + "end": 10, "title": "Cat", "type": "ner", "value": "cat" From b610252aad84937192de32e2978efba5bbba8eab Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Mon, 14 Mar 2022 11:17:01 +0900 Subject: [PATCH 3/9] Fixed bug --- fastlabel/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 69f5dc5..0b79037 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -413,7 +413,7 @@ def get_text_tasks( raise FastLabelInvalidException( "Limit must be less than or equal to 10.", 422 ) - endpoint = "tasks/video" + endpoint = "tasks/text" params = {"project": project} if status: params["status"] = status From a6bcd14939ae4ca685187df6381321fd3c9fa4ee Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Wed, 16 Mar 2022 06:22:51 +0900 Subject: [PATCH 4/9] Add find_by_task_name --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index cb0683d..9f74532 100644 --- a/README.md +++ b/README.md @@ -340,6 +340,12 @@ Find a single task. task = client.find_image_classification_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_image_classification_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 1000 tasks) @@ -457,6 +463,12 @@ Find a single task. task = client.find_multi_image_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_multi_image_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. @@ -613,6 +625,12 @@ Find a single task. task = client.find_video_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_video_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 10 tasks) @@ -766,6 +784,12 @@ Find a single task. task = client.find_video_classification_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_video_classification_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 1000 tasks) @@ -841,6 +865,12 @@ Find a single task. task = client.find_text_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_text_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 10 tasks) @@ -939,6 +969,12 @@ Find a single task. task = client.find_text_classification_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_text_classification_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 1000 tasks) @@ -1010,6 +1046,12 @@ Find a single task. task = client.find_video_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_video_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 10 tasks) @@ -1102,6 +1144,12 @@ Find a single task. task = client.find_audio_classification_task(task_id="YOUR_TASK_ID") ``` +Find a single task by name. + +```python +tasks = client.find_audio_classification_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +``` + #### Get Tasks Get tasks. (Up to 1000 tasks) From ede3277a94cfdc4eeecf6a3ba2e3173927e14b62 Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Wed, 16 Mar 2022 06:26:53 +0900 Subject: [PATCH 5/9] Reformat request/response format --- README.md | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 9f74532..48d5687 100644 --- a/README.md +++ b/README.md @@ -845,11 +845,9 @@ task_id = client.create_text_task( annotations=[{ "type": "ner", "value": "person", - "points": { - "start": 0, - "end": 10, - "text": "1234567890" - } + "start": 0, + "end": 10, + "text": "1234567890" }] ) ``` @@ -892,11 +890,9 @@ task_id = client.update_text_task( annotations=[{ "type": "bbox", "value": "bird", - "points": { - "start": 0, - "end": 10, - "text": "0123456789" - } + "start": 0, + "end": 10, + "text": "0123456789" }] ) ``` @@ -1030,7 +1026,8 @@ task_id = client.create_audio_task( annotations=[{ "type": "segmentation", "value": "person", - "points": [100, 200] + "start": 100, + "end": 200 }] ) ``` @@ -1073,7 +1070,8 @@ task_id = client.update_audio_task( annotations=[{ "type": "segmentation", "value": "bird", - "points": [100, 200] + "start": 100, + "end": 200 }] ) ``` @@ -1098,10 +1096,11 @@ Example of a single audio task object { "attributes": [], "color": "#b36d18", - "points": [100, 200], - "title": "Cat", + "start": 100, + "end": 200, + "title": "Bird", "type": "segmentation", - "value": "cat" + "value": "bird" } ], "createdAt": "2021-02-22T11:25:27.158Z", From f365fc3c2326f3052f5a1aca3891cc48c9c8eddb Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Wed, 16 Mar 2022 10:42:17 +0900 Subject: [PATCH 6/9] Fixed bug. --- fastlabel/__init__.py | 54 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index 0b79037..c6c7ab1 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -115,6 +115,22 @@ def find_video_task_by_name(self, project: str, task_name: str) -> dict: return None return tasks[0] + def find_video_classification_task_by_name( + self, project: str, task_name: str + ) -> dict: + """ + Find a single video classification task by name. + + project is slug of your project (Required). + task_name is a task name (Required). + """ + tasks = self.get_video_classification_tasks( + project=project, task_name=task_name + ) + if not tasks: + return None + return tasks[0] + def find_text_task(self, task_id: str) -> dict: """ Find a single text task. @@ -141,6 +157,20 @@ def find_text_task_by_name(self, project: str, task_name: str) -> dict: return None return tasks[0] + def find_text_classification_task_by_name( + self, project: str, task_name: str + ) -> dict: + """ + Find a single text classification task by name. + + project is slug of your project (Required). + task_name is a task name (Required). + """ + tasks = self.get_text_classification_tasks(project=project, task_name=task_name) + if not tasks: + return None + return tasks[0] + def find_audio_task(self, task_id: str) -> dict: """ Find a single audio task. @@ -152,7 +182,7 @@ def find_audio_classification_task(self, task_id: str) -> dict: """ Find a single audio classification task. """ - endpoint = "tasks/video/classification/" + task_id + endpoint = "tasks/audio/classification/" + task_id return self.api.get_request(endpoint) def find_audio_task_by_name(self, project: str, task_name: str) -> dict: @@ -167,6 +197,22 @@ def find_audio_task_by_name(self, project: str, task_name: str) -> dict: return None return tasks[0] + def find_audio_classification_task_by_name( + self, project: str, task_name: str + ) -> dict: + """ + Find a single audio classification task by name. + + project is slug of your project (Required). + task_name is a task name (Required). + """ + tasks = self.get_audio_classification_tasks( + project=project, task_name=task_name + ) + if not tasks: + return None + return tasks[0] + # Task Get def get_image_tasks( @@ -918,7 +964,7 @@ def create_text_task( if not utils.is_text_supported_ext(file_path): raise FastLabelInvalidException("Supported extensions are txt.", 422) if not utils.is_text_supported_size(file_path): - raise FastLabelInvalidException("Supported video size is under 2 MB.", 422) + raise FastLabelInvalidException("Supported text size is under 2 MB.", 422) file = utils.base64_encode(file_path) payload = {"project": project, "name": name, "file": file} @@ -971,7 +1017,7 @@ def create_text_classification_task( if not utils.is_text_supported_ext(file_path): raise FastLabelInvalidException("Supported extensions are txt.", 422) if not utils.is_text_supported_size(file_path): - raise FastLabelInvalidException("Supported video size is under 2 MB.", 422) + raise FastLabelInvalidException("Supported text size is under 2 MB.", 422) file = utils.base64_encode(file_path) payload = {"project": project, "name": name, "file": file} @@ -1025,7 +1071,7 @@ def create_audio_task( ) if not utils.is_audio_supported_size(file_path): raise FastLabelInvalidException( - "Supported video size is under 120 MB.", 422 + "Supported audio size is under 120 MB.", 422 ) file = utils.base64_encode(file_path) From c02dd3ac611d33356de73fb65e243c910236e823 Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Wed, 16 Mar 2022 10:42:59 +0900 Subject: [PATCH 7/9] Update README --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 48d5687..b366876 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,7 @@ task_id = client.update_image_classification_task( task_id="YOUR_TASK_ID", status="approved", assignee="USER_SLUG", - tags=["tag1", "tag2"] + tags=["tag1", "tag2"], attributes=[ { "key": "attribute-key", @@ -807,7 +807,7 @@ task_id = client.update_video_classification_task( task_id="YOUR_TASK_ID", status="approved", assignee="USER_SLUG", - tags=["tag1", "tag2"] + tags=["tag1", "tag2"], attributes=[ { "key": "attribute-key", @@ -828,7 +828,7 @@ Supported following project types: Create a new task. ```python -task_id = client.create_video_task( +task_id = client.create_text_task( project="YOUR_PROJECT_SLUG", name="sample.txt", file_path="./sample.txt" @@ -899,7 +899,7 @@ task_id = client.update_text_task( #### Response -Example of a single vide task object +Example of a single text task object ```python { @@ -988,7 +988,7 @@ task_id = client.update_text_classification_task( task_id="YOUR_TASK_ID", status="approved", assignee="USER_SLUG", - tags=["tag1", "tag2"] + tags=["tag1", "tag2"], attributes=[ { "key": "attribute-key", @@ -1026,8 +1026,8 @@ task_id = client.create_audio_task( annotations=[{ "type": "segmentation", "value": "person", - "start": 100, - "end": 200 + "start": 0.4, + "end": 0.5 }] ) ``` @@ -1040,13 +1040,13 @@ task_id = client.create_audio_task( Find a single task. ```python -task = client.find_video_task(task_id="YOUR_TASK_ID") +task = client.find_audio_task(task_id="YOUR_TASK_ID") ``` Find a single task by name. ```python -tasks = client.find_video_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") +tasks = client.find_audio_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME") ``` #### Get Tasks @@ -1070,8 +1070,8 @@ task_id = client.update_audio_task( annotations=[{ "type": "segmentation", "value": "bird", - "start": 100, - "end": 200 + "start": 0.4, + "end": 0.5 }] ) ``` @@ -1096,8 +1096,8 @@ Example of a single audio task object { "attributes": [], "color": "#b36d18", - "start": 100, - "end": 200, + "start": 0.4, + "end": 0.5, "title": "Bird", "type": "segmentation", "value": "bird" From 5245d0596f9b0cf092c17e737176012eb87322a3 Mon Sep 17 00:00:00 2001 From: Takafumi Iju Date: Wed, 16 Mar 2022 19:46:23 +0900 Subject: [PATCH 8/9] Fixed request format --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b366876..2836d77 100644 --- a/README.md +++ b/README.md @@ -1166,7 +1166,7 @@ task_id = client.update_audio_classification_task( task_id="YOUR_TASK_ID", status="approved", assignee="USER_SLUG", - tags=["tag1", "tag2"] + tags=["tag1", "tag2"], attributes=[ { "key": "attribute-key", From 71e168b5ee8d23eeadee179a6c1189f177bd435f Mon Sep 17 00:00:00 2001 From: ryoKaz Date: Thu, 17 Mar 2022 11:44:03 +0900 Subject: [PATCH 9/9] fix limit --- fastlabel/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fastlabel/__init__.py b/fastlabel/__init__.py index c6c7ab1..c94be6b 100644 --- a/fastlabel/__init__.py +++ b/fastlabel/__init__.py @@ -438,7 +438,7 @@ def get_text_tasks( tags: list = [], task_name: str = None, offset: int = None, - limit: int = 10, + limit: int = 100, ) -> list: """ Returns a list of text tasks. @@ -455,9 +455,9 @@ def get_text_tasks( offset is the starting position number to fetch (Optional). limit is the max number to fetch (Optional). """ - if limit > 10: + if limit > 1000: raise FastLabelInvalidException( - "Limit must be less than or equal to 10.", 422 + "Limit must be less than or equal to 1000.", 422 ) endpoint = "tasks/text" params = {"project": project} @@ -523,7 +523,7 @@ def get_audio_tasks( tags: list = [], task_name: str = None, offset: int = None, - limit: int = 10, + limit: int = 100, ) -> list: """ Returns a list of audio tasks. @@ -540,9 +540,9 @@ def get_audio_tasks( offset is the starting position number to fetch (Optional). limit is the max number to fetch (Optional). """ - if limit > 10: + if limit > 1000: raise FastLabelInvalidException( - "Limit must be less than or equal to 10.", 422 + "Limit must be less than or equal to 1000.", 422 ) endpoint = "tasks/audio" params = {"project": project}