Skip to content
Open
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [PCD](#pcd)
- [Sequential PCD](#sequential-pcd)
- [DICOM](#dicom)
- [Robotics](#robotics)
- [Common](#common)
- [Appendix](#appendix)
- [Annotation](#annotation)
Expand Down Expand Up @@ -2064,6 +2065,34 @@ Example of a single dicom task object
}
```

### Robotics

Supported following project types:

- Robotics - Task Classification

#### Create Tasks

Create a new task (Content creation is required separately).

```python
task_id = client.create_robotics_task(
project="YOUR_PROJECT_SLUG",
name="TASK_NAME",
)
```

#### Import Contents

Import contents zip file

```python
history = client.import_robotics_contents_file(
project="YOUR_PROJECT_SLUG",
file_path="ZIP_FILE_PATH",
)
```

### Common

APIs for update and delete and count are same over all tasks.
Expand Down Expand Up @@ -2104,6 +2133,31 @@ client.delete_task_annotations(task_id="YOUR_TASK_ID")
id_name_map = client.get_task_id_name_map(project="YOUR_PROJECT_SLUG")
```

#### Get Task Appendix Data

Get appendix data (URLs and parameters) for tasks.

```python
appendix_data = client.get_task_appendix_data(project="YOUR_PROJECT_SLUG")
```

Filter by task name:

```python
appendix_data = client.get_task_appendix_data(
project="YOUR_PROJECT_SLUG",
task_name="YOUR_TASK_NAME"
)
```

Response includes:

- `id`: UUID of the appendix
- `url`: Image file URL
- `name`: Format is `{task_name}/{content_name}/{file_name}`
- `format`: `yml`, `kitti`, or `none`
- `calibration`: Calibration data

#### Count Task

```python
Expand Down
110 changes: 110 additions & 0 deletions fastlabel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,53 @@ def create_sequential_pcd_task(

return self.api.post_request(endpoint, payload=payload)

def create_robotics_task(
self,
project: str,
name: str,
status: Optional[str] = None,
external_status: Optional[str] = None,
priority: Optional[Priority] = None,
tags: Optional[list[str]] = None,
**kwargs,
) -> str:
"""
Create a single robotics task without content.

project is slug of your project (Required).
name is an unique identifier of task in 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).
priority is the priority of the task (default: none) (Optional).
Set one of the numbers corresponding to:
none = 0,
low = 10,
medium = 20,
high = 30,
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/robotics"

payload = {"project": project, "name": name}
if status:
payload["status"] = status
if external_status:
payload["externalStatus"] = external_status
if priority is not None:
payload["priority"] = priority
if tags:
payload["tags"] = tags or []

self.__fill_assign_users(payload, **kwargs)

return self.api.post_request(endpoint, payload=payload)

def import_appendix_file(
self,
project: str,
Expand Down Expand Up @@ -2053,6 +2100,69 @@ def get_appendix_data(

return self.api.get_request(endpoint, params=params)

def get_task_appendix_data(
self,
project: str,
task_name: Optional[str] = None,
offset: Optional[int] = None,
limit: int = 10000,
) -> list:
"""
Returns a list of appendixes urls and params.
params = {
id: uuid,
url: image file url,
name: {task_name}/{content_name}/{file_name},
format: yml or kitti or none,
calibration: calibration data,
}

project is slug of your project (Required).
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 > 10000:
raise FastLabelInvalidException(
"Limit must be less than or equal to 10000.", 422
)
endpoint = "tasks/appendix"
params = {"project": project}
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 import_robotics_contents_file(
self,
project: str,
file_path: str,
) -> list:
"""
Import robotics contents file zip.
project is slug of your project (Required).
file_path is a path to data. Supported extensions are zip (Required).
"""

if not utils.is_robotics_contents_supported_ext(file_path):
raise FastLabelInvalidException("Supported extensions are zip.", 422)

endpoint = "contents/imports/robotics-contents/batch"
payload = {"project": project}
signed_url = self.__get_signed_path(
project=project,
file_name=os.path.basename(file_path),
file_type="application/zip",
)
self.api.upload_zipfile(url=signed_url["url"], file_path=file_path)
payload["fileKey"] = signed_url["name"]

return self.api.post_request(endpoint, payload=payload)

# Task Update

def update_task(
Expand Down
4 changes: 4 additions & 0 deletions fastlabel/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def is_appendix_supported_ext(file_path: str) -> bool:
return file_path.lower().endswith((".zip"))


def is_robotics_contents_supported_ext(file_path: str) -> bool:
return file_path.lower().endswith((".zip"))


def is_pcd_supported_ext(file_path: str) -> bool:
# .ply is not yet supported. To support it, modification of the API
# needs to be considered as well.
Expand Down
Loading