Skip to content

Commit b272672

Browse files
Merge pull request #220 from fastlabel/feature/add-multi-image-sdk
Added multi image classification sdk
2 parents 938d9c3 + 750c487 commit b272672

File tree

2 files changed

+327
-0
lines changed

2 files changed

+327
-0
lines changed

README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [Task](#task)
99
- [Image](#image)
1010
- [Image Classification](#image-classification)
11+
- [Multi Image Classification](#multi-image-classification)
1112
- [Sequential Image](#sequential-image)
1213
- [Video](#video)
1314
- [Video Classification](#video-classification)
@@ -560,6 +561,129 @@ Example of a single image classification task object
560561
}
561562
```
562563

564+
### Multi Image Classification
565+
566+
Supported following project types:
567+
568+
- Multi Image - Classification
569+
570+
#### Create Task
571+
572+
Create a new task.
573+
574+
```python
575+
task = client.create_multi_image_classification_task(
576+
project="YOUR_PROJECT_SLUG",
577+
name="sample",
578+
folder_path="./sample",
579+
priority=10, # (optional) none: 0, low: 10, medium: 20, high: 30
580+
attributes=[
581+
{
582+
"type": "text",
583+
"key": "attribute-key",
584+
"value": "attribute-value"
585+
}
586+
]
587+
)
588+
```
589+
590+
##### Limitation
591+
592+
593+
- You can upload up to a size of 20 MB.
594+
- You can upload up to a total size of 2 GB.
595+
- You can upload up to 6 files in total.
596+
597+
#### Find Task
598+
599+
Find a single task.
600+
601+
```python
602+
task = client.find_multi_image_classification_task(task_id="YOUR_TASK_ID")
603+
```
604+
605+
Find a single task by name.
606+
607+
```python
608+
tasks = client.find_multi_image_classification_task_by_name(project="YOUR_PROJECT_SLUG", task_name="YOUR_TASK_NAME")
609+
```
610+
611+
#### Get Tasks
612+
613+
Get tasks.
614+
615+
```python
616+
tasks = client.get_multi_image_classification_tasks(project="YOUR_PROJECT_SLUG")
617+
```
618+
619+
#### Update Task
620+
621+
Update a single task.
622+
623+
```python
624+
task_id = client.update_multi_image_classification_task(
625+
task_id="YOUR_TASK_ID",
626+
status="approved",
627+
assignee="USER_SLUG",
628+
tags=["tag1", "tag2"],
629+
priority=10, # (optional) none: 0, low: 10, medium: 20, high: 30
630+
attributes=[
631+
{
632+
"type": "text",
633+
"key": "attribute-key",
634+
"value": "attribute-value"
635+
}
636+
]
637+
)
638+
```
639+
640+
#### Response
641+
642+
Example of a single task object
643+
644+
```python
645+
{
646+
"id": "YOUR_TASK_ID",
647+
"name": "sample",
648+
"contents": [
649+
{
650+
"name": "content-name-1",
651+
"url": "content-url-1",
652+
"width": 100,
653+
"height": 100,
654+
},
655+
{
656+
"name": "content-name-2",
657+
"url": "content-url-2",
658+
"width": 100,
659+
"height": 100,
660+
}
661+
],
662+
"status": "registered",
663+
"externalStatus": "registered",
664+
"priority": 10,
665+
"tags": [],
666+
"assignee": "ASSIGNEE_NAME",
667+
"reviewer": "REVIEWER_NAME",
668+
"externalAssignee": "EXTERNAL_ASSIGNEE_NAME",
669+
"externalReviewer": "EXTERNAL_REVIEWER_NAME",
670+
"attributes": [
671+
{
672+
"type": "text",
673+
"key": "attribute-key-1",
674+
"value": "attribute-value-1"
675+
},
676+
{
677+
"type": "text",
678+
"key": "attribute-key-2",
679+
"value": "attribute-value-2"
680+
}
681+
],
682+
"createdAt": "2021-02-22T11:25:27.158Z",
683+
"updatedAt": "2021-02-22T11:25:27.158Z"
684+
}
685+
```
686+
563687
### Sequential Image
564688

565689
Supported following project types:

fastlabel/__init__.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ def find_image_classification_task(self, task_id: str) -> dict:
7070
"""
7171
endpoint = "tasks/image/classification/" + task_id
7272
return self.api.get_request(endpoint)
73+
74+
def find_multi_image_classification_task(self, task_id: str) -> dict:
75+
"""
76+
Find a single multi image classification task.
77+
"""
78+
endpoint = "tasks/multi-image/classification/" + task_id
79+
return self.api.get_request(endpoint)
80+
81+
def find_multi_image_classification_task_by_name(
82+
self, project: str, task_name: str
83+
) -> dict:
84+
"""
85+
Find a single multi image classification task by name.
86+
87+
project is slug of your project (Required).
88+
task_name is a task name (Required).
89+
"""
90+
tasks = self.get_multi_image_classification_tasks(
91+
project=project, task_name=task_name
92+
)
93+
if not tasks:
94+
return None
95+
return tasks[0]
7396

7497
def find_image_classification_task_by_name(
7598
self, project: str, task_name: str
@@ -409,6 +432,46 @@ def get_image_classification_tasks(
409432
if limit:
410433
params["limit"] = limit
411434
return self.api.get_request(endpoint, params=params)
435+
436+
def get_multi_image_classification_tasks(
437+
self,
438+
project: str,
439+
status: str = None,
440+
external_status: str = None,
441+
tags: list = [],
442+
task_name: str = None,
443+
offset: int = None,
444+
limit: int = 100,
445+
) -> list:
446+
"""
447+
Returns a list of multi image classification tasks.
448+
Returns up to 1000 at a time, to get more,
449+
set offset as the starting position to fetch.
450+
451+
project is slug of your project (Required).
452+
status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back',
453+
'approved', 'declined' (Optional).
454+
external_status can be 'registered', 'completed', 'skipped', 'reviewed',
455+
'sent_back', 'approved', 'declined', 'customer_declined' (Optional).
456+
tags is a list of tag (Optional).
457+
offset is the starting position number to fetch (Optional).
458+
limit is the max number to fetch (Optional).
459+
"""
460+
endpoint = "tasks/multi-image/classification"
461+
params = {"project": project}
462+
if status:
463+
params["status"] = status
464+
if external_status:
465+
params["externalStatus"] = external_status
466+
if tags:
467+
params["tags"] = tags
468+
if task_name:
469+
params["taskName"] = task_name
470+
if offset:
471+
params["offset"] = offset
472+
if limit:
473+
params["limit"] = limit
474+
return self.api.get_request(endpoint, params=params)
412475

413476
def get_sequential_image_tasks(
414477
self,
@@ -1134,6 +1197,96 @@ def create_integrated_image_classification_task(
11341197
self.__fill_assign_users(payload, **kwargs)
11351198

11361199
return self.api.post_request(endpoint, payload=payload)
1200+
1201+
def create_multi_image_classification_task(
1202+
self,
1203+
project: str,
1204+
name: str,
1205+
folder_path: str,
1206+
status: str = None,
1207+
external_status: str = None,
1208+
priority: Priority = None,
1209+
attributes: list = [],
1210+
tags: list = [],
1211+
is_delete_exif: bool = False,
1212+
**kwargs,
1213+
) -> str:
1214+
"""
1215+
Create a single multi image classification task.
1216+
1217+
project is slug of your project (Required).
1218+
name is an unique identifier of task in your project (Required).
1219+
folder_path is a path to data folder. Files should be under the folder.
1220+
status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back',
1221+
'approved', 'declined' (Optional).
1222+
external_status can be 'registered', 'completed', 'skipped', 'reviewed',
1223+
priority is the priority of the task (default: none) (Optional).
1224+
Set one of the numbers corresponding to:
1225+
none = 0,
1226+
low = 10,
1227+
medium = 20,
1228+
high = 30,
1229+
'sent_back', 'approved', 'declined', 'customer_declined' (Optional).
1230+
attributes is a list of attribute to be set in advance (Optional).
1231+
tags is a list of tag to be set in advance (Optional).
1232+
assignee is slug of assigned user (Optional).
1233+
reviewer is slug of review user (Optional).
1234+
approver is slug of approve user (Optional).
1235+
external_assignee is slug of external assigned user (Optional).
1236+
external_reviewer is slug of external review user (Optional).
1237+
external_approver is slug of external approve user (Optional).
1238+
"""
1239+
endpoint = "tasks/multi-image/classification"
1240+
if not os.path.isdir(folder_path):
1241+
raise FastLabelInvalidException("Folder does not exist.", 422)
1242+
file_paths = glob.glob(os.path.join(folder_path, "*"))
1243+
if not file_paths:
1244+
raise FastLabelInvalidException("Folder does not have any file.", 422)
1245+
contents = []
1246+
contents_size = 0
1247+
for file_path in file_paths:
1248+
if not utils.is_image_supported_ext(file_path):
1249+
raise FastLabelInvalidException(
1250+
"Supported extensions are png, jpg, jpeg.", 422
1251+
)
1252+
1253+
if not utils.is_image_supported_size(file_path):
1254+
raise FastLabelInvalidException(
1255+
"Supported image size is under 20 MB.", 422
1256+
)
1257+
1258+
if len(contents) == 6:
1259+
raise FastLabelInvalidException(
1260+
"The count of files should be under 6", 422
1261+
)
1262+
1263+
file = utils.base64_encode(file_path)
1264+
contents.append({"name": os.path.basename(file_path), "file": file})
1265+
contents_size += utils.get_json_length(contents[-1])
1266+
if contents_size > const.SUPPORTED_CONTENTS_SIZE:
1267+
raise FastLabelInvalidException(
1268+
"Supported contents size is under"
1269+
f" {const.SUPPORTED_CONTENTS_SIZE}.",
1270+
422,
1271+
)
1272+
1273+
payload = {"project": project, "name": name, "contents": contents}
1274+
if status:
1275+
payload["status"] = status
1276+
if external_status:
1277+
payload["externalStatus"] = external_status
1278+
if priority is not None:
1279+
payload["priority"] = priority
1280+
if attributes:
1281+
payload["attributes"] = delete_extra_attributes_parameter(attributes)
1282+
if tags:
1283+
payload["tags"] = tags
1284+
if is_delete_exif:
1285+
payload["isDeleteExif"] = is_delete_exif
1286+
1287+
self.__fill_assign_users(payload, **kwargs)
1288+
1289+
return self.api.post_request(endpoint, payload=payload)
11371290

11381291
def create_sequential_image_task(
11391292
self,
@@ -1982,6 +2135,56 @@ def update_image_classification_task(
19822135
self.__fill_assign_users(payload, **kwargs)
19832136

19842137
return self.api.put_request(endpoint, payload=payload)
2138+
2139+
def update_multi_image_classification_task(
2140+
self,
2141+
task_id: str,
2142+
status: str = None,
2143+
external_status: str = None,
2144+
priority: Priority = None,
2145+
attributes: list = [],
2146+
tags: list = [],
2147+
**kwargs,
2148+
) -> str:
2149+
"""
2150+
Update a single multi image classification task.
2151+
2152+
task_id is an id of the task (Required).
2153+
status can be 'registered', 'completed', 'skipped', 'reviewed', 'sent_back',
2154+
'approved', 'declined' (Optional).
2155+
external_status can be 'registered', 'completed', 'skipped', 'reviewed',
2156+
priority is the priority of the task (default: none) (Optional).
2157+
Set one of the numbers corresponding to:
2158+
none = 0,
2159+
low = 10,
2160+
medium = 20,
2161+
high = 30,
2162+
'sent_back', 'approved', 'declined', 'customer_declined' (Optional).
2163+
attributes is a list of attribute to be set in advance (Optional).
2164+
tags is a list of tag to be set in advance (Optional).
2165+
assignee is slug of assigned user (Optional).
2166+
reviewer is slug of review user (Optional).
2167+
approver is slug of approve user (Optional).
2168+
external_assignee is slug of external assigned user (Optional).
2169+
external_reviewer is slug of external review user (Optional).
2170+
external_approver is slug of external approve user (Optional).
2171+
"""
2172+
endpoint = "tasks/multi-image/classification/" + task_id
2173+
payload = {}
2174+
if status:
2175+
payload["status"] = status
2176+
if external_status:
2177+
payload["externalStatus"] = external_status
2178+
if priority is not None:
2179+
payload["priority"] = priority
2180+
if attributes:
2181+
payload["attributes"] = delete_extra_attributes_parameter(attributes)
2182+
if tags:
2183+
payload["tags"] = tags
2184+
2185+
self.__fill_assign_users(payload, **kwargs)
2186+
2187+
return self.api.put_request(endpoint, payload=payload)
19852188

19862189
def update_sequential_image_task(
19872190
self,

0 commit comments

Comments
 (0)