Skip to content

Commit dc97f65

Browse files
authored
Merge pull request #725 from superannotateai/develop
Develop
2 parents 9ad6661 + 9fcac30 commit dc97f65

File tree

22 files changed

+825
-79
lines changed

22 files changed

+825
-79
lines changed

CHANGELOG.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ History
66

77
All release highlights of this project will be documented in this file.
88

9+
4.4.26 - Oct 29, 2024
10+
________________________
11+
12+
**Added**
13+
14+
- ``SAClient.copy_items/move_items`` method, added the ability to copy/move categories and duplicate strategies ("skip", "replace", "replace_annotations_only").
15+
**Updated**
16+
17+
- Fixed `SAClient.get_annotations() To handle annotations that contain all UTF-8 characters.`
18+
- Renamed project type GenAI to Multimodal
19+
20+
21+
922
4.4.25 - Oct 7, 2024
1023
________________________
1124

src/superannotate/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44

55

6-
__version__ = "4.4.25"
6+
__version__ = "4.4.26"
77

88
os.environ.update({"sa_version": __version__})
99
sys.path.append(os.path.split(os.path.realpath(__file__))[0])
@@ -18,7 +18,7 @@
1818
from lib.app.input_converters import convert_project_type
1919
from lib.app.input_converters import export_annotation
2020
from lib.app.input_converters import import_annotation
21-
from lib.app.interface.sdk_interface import SAClient
21+
from superannotate.lib.app.interface.sdk_interface import SAClient
2222

2323

2424
SESSIONS = {}

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"Document",
8080
"Tiled",
8181
"PointCloud",
82-
"GenAI",
82+
"Multimodal",
8383
]
8484

8585

@@ -307,7 +307,7 @@ def create_project(
307307
:param project_description: the new project's description
308308
:type project_description: str
309309
310-
:param project_type: the new project type, Vector, Pixel, Video, Document, Tiled, PointCloud, GenAI.
310+
:param project_type: the new project type, Vector, Pixel, Video, Document, Tiled, PointCloud, Multimodal.
311311
:type project_type: str
312312
313313
:param settings: list of settings objects
@@ -1215,11 +1215,11 @@ def prepare_export(
12151215
:param only_pinned: enable only pinned output in export. This option disables all other types of output.
12161216
:type only_pinned: bool
12171217
1218-
:param kwargs:
1219-
Arbitrary kwargs:
1218+
:param kwargs: Arbitrary keyword arguments:
12201219
1221-
- integration_name: can be provided which will be used as a storage to store export file
1222-
- format: can be CSV for the Gen AI projects
1220+
- integration_name: The name of the integration within the platform that is being used.
1221+
- format: The format in which the data will be exported in multimodal projects.
1222+
It can be either CSV or JSON. If None, the data will be exported in the default JSON format.
12231223
:return: metadata object of the prepared export
12241224
:rtype: dict
12251225
@@ -1232,7 +1232,7 @@ def prepare_export(
12321232
project = "Project Name",
12331233
folder_names = ["Folder 1", "Folder 2"],
12341234
annotation_statuses = ["Completed","QualityCheck"],
1235-
export_type = "CSV"
1235+
format = "CSV"
12361236
)
12371237
12381238
client.download_export("Project Name", export, "path_to_download")
@@ -2318,7 +2318,7 @@ def invite_contributors_to_team(
23182318

23192319
def get_annotations(
23202320
self,
2321-
project: Union[int, NotEmptyStr],
2321+
project: Union[NotEmptyStr, int],
23222322
items: Optional[Union[List[NotEmptyStr], List[int]]] = None,
23232323
):
23242324
"""Returns annotations for the given list of items.
@@ -2663,23 +2663,23 @@ def search_items(
26632663

26642664
def list_items(
26652665
self,
2666-
project: Union[int, str],
2667-
folder: Optional[Union[int, str]] = None,
2666+
project: Union[NotEmptyStr, int],
2667+
folder: Optional[Union[NotEmptyStr, int]] = None,
26682668
*,
26692669
include: List[Literal["custom_metadata"]] = None,
26702670
**filters,
26712671
):
26722672
"""
26732673
Search items by filtering criteria.
26742674
2675-
:param project: The project name, project ID, or folder path (e.g., "project1/folder1") to search within.
2675+
:param project: The project name, project ID, or folder path (e.g., "project1") to search within.
26762676
This can refer to the root of the project or a specific subfolder.
2677-
:type project: Union[int, str]
2677+
:type project: Union[NotEmptyStr, int]
26782678
26792679
:param folder: The folder name or ID to search within. If None, the search will be done in the root folder of
26802680
the project. If both “project” and “folder” specify folders, the “project”
26812681
value will take priority.
2682-
:type folder: Union[int, str], optional
2682+
:type folder: Union[NotEmptyStr, int], optional
26832683
26842684
:param include: Specifies additional fields to include in the response.
26852685
@@ -2694,8 +2694,8 @@ def list_items(
26942694
26952695
26962696
Supported operations:
2697-
- __ne: Value is in the list.
2698-
- __in: Value is not equal.
2697+
- __ne: Value is not equal.
2698+
- __in: Value is in the list.
26992699
- __notin: Value is not in the list.
27002700
- __contains: Value has the substring.
27012701
- __starts: Value starts with the prefix.
@@ -2710,6 +2710,9 @@ def list_items(
27102710
- name__contains: str
27112711
- name__starts: str
27122712
- name__ends: str
2713+
- annotation_status: str
2714+
- annotation_status__in: list[str]
2715+
- annotation_status__ne: list[str]
27132716
- approval_status: Literal["Approved", "Disapproved", None]
27142717
- assignments__user_id: str
27152718
- assignments__user_id__ne: str
@@ -2927,26 +2930,46 @@ def copy_items(
29272930
source: Union[NotEmptyStr, dict],
29282931
destination: Union[NotEmptyStr, dict],
29292932
items: Optional[List[NotEmptyStr]] = None,
2930-
include_annotations: Optional[bool] = True,
2933+
include_annotations: bool = True,
2934+
duplicate_strategy: Literal[
2935+
"skip", "replace", "replace_annotations_only"
2936+
] = "skip",
29312937
):
29322938
"""Copy images in bulk between folders in a project
29332939
2934-
:param source: project name or folder path to select items from (e.g., “project1/folder1”).
2940+
:param source: project name (root) or folder path to pick items from (e.g., “project1/folder1”).
29352941
:type source: str
29362942
2937-
:param destination: project name (root) or folder path to place copied items.
2943+
:param destination: project name (root) or folder path to place copied items (e.g., “project1/folder2”).
29382944
:type destination: str
29392945
29402946
:param items: names of items to copy. If None, all items from the source directory will be copied.
29412947
:type items: list of str
29422948
2943-
:param include_annotations: enables annotations copy
2949+
:param include_annotations: enables the copying of item data, including annotations, status, priority score,
2950+
approval state, and category. If set to False, only the items will be copied without additional data.
29442951
:type include_annotations: bool
29452952
2953+
:param duplicate_strategy: Specifies the strategy for handling duplicate items in the destination.
2954+
The default value is "skip".
2955+
2956+
- "skip": skips duplicate items in the destination and continues with the next item.
2957+
- "replace": replaces the annotations, status, priority score, approval state, and category of duplicate items.
2958+
- "replace_annotations_only": replaces only the annotations of duplicate items,
2959+
leaving other data (status, priority score, approval state, and category) unchanged.
2960+
2961+
:type duplicate_strategy: Literal["skip", "replace", "replace_annotations_only"]
2962+
29462963
:return: list of skipped item names
29472964
:rtype: list of strs
29482965
"""
29492966

2967+
if not include_annotations and duplicate_strategy != "skip":
2968+
duplicate_strategy = "skip"
2969+
logger.warning(
2970+
"Copy operation continuing without annotations and metadata due to include_annotations=False."
2971+
)
2972+
29502973
project_name, source_folder = extract_project_folder(source)
29512974
to_project_name, destination_folder = extract_project_folder(destination)
29522975
if project_name != to_project_name:
@@ -2960,6 +2983,7 @@ def copy_items(
29602983
to_folder=to_folder,
29612984
item_names=items,
29622985
include_annotations=include_annotations,
2986+
duplicate_strategy=duplicate_strategy,
29632987
)
29642988
if response.errors:
29652989
raise AppException(response.errors)
@@ -2971,18 +2995,31 @@ def move_items(
29712995
source: Union[NotEmptyStr, dict],
29722996
destination: Union[NotEmptyStr, dict],
29732997
items: Optional[List[NotEmptyStr]] = None,
2998+
duplicate_strategy: Literal[
2999+
"skip", "replace", "replace_annotations_only"
3000+
] = "skip",
29743001
):
29753002
"""Move images in bulk between folders in a project
29763003
2977-
:param source: project name or folder path to pick items from (e.g., “project1/folder1”).
3004+
:param source: project name (root) or folder path to pick items from (e.g., “project1/folder1”).
29783005
:type source: str
29793006
2980-
:param destination: project name (root) or folder path to move items to.
3007+
:param destination: project name (root) or folder path to move items to (e.g., “project1/folder2”).
29813008
:type destination: str
29823009
29833010
:param items: names of items to move. If None, all items from the source directory will be moved.
29843011
:type items: list of str
29853012
3013+
:param duplicate_strategy: Specifies the strategy for handling duplicate items in the destination.
3014+
The default value is "skip".
3015+
3016+
- "skip": skips duplicate items in the destination and continues with the next item.
3017+
- "replace": replaces the annotations, status, priority score, approval state, and category of duplicate items.
3018+
- "replace_annotations_only": replaces only the annotations of duplicate items,
3019+
leaving other data (status, priority score, approval state, and category) unchanged.
3020+
3021+
:type duplicate_strategy: Literal["skip", "replace", "replace_annotations_only"]
3022+
29863023
:return: list of skipped item names
29873024
:rtype: list of strs
29883025
"""
@@ -3000,6 +3037,7 @@ def move_items(
30003037
from_folder=source_folder,
30013038
to_folder=destination_folder,
30023039
item_names=items,
3040+
duplicate_strategy=duplicate_strategy,
30033041
)
30043042
if response.errors:
30053043
raise AppException(response.errors)

src/superannotate/lib/core/entities/project.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ class TeamEntity(BaseModel):
163163
users: Optional[List[UserEntity]]
164164
pending_invitations: Optional[List[Any]]
165165
creator_id: Optional[str]
166+
owner_id: Optional[str]
166167

167168
class Config:
168169
extra = Extra.ignore

src/superannotate/lib/core/enums.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class ProjectType(BaseTitledEnum):
108108
TILED = "Tiled", 5
109109
OTHER = "Other", 6
110110
POINT_CLOUD = "PointCloud", 7
111-
GEN_AI = "GenAI", 8
111+
MULTIMODAL = "Multimodal", 8
112112
UNSUPPORTED_TYPE_1 = "UnsupportedType", 9
113113
UNSUPPORTED_TYPE_2 = "UnsupportedType", 10
114114

src/superannotate/lib/core/service_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,5 +246,5 @@ class SettingsListResponse(ServiceResponse):
246246
ProjectType.PIXEL: ImageResponse,
247247
ProjectType.DOCUMENT: DocumentResponse,
248248
ProjectType.POINT_CLOUD: PointCloudResponse,
249-
ProjectType.GEN_AI: ImageResponse,
249+
ProjectType.MULTIMODAL: ImageResponse,
250250
}

src/superannotate/lib/core/serviceproviders.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Callable
66
from typing import Dict
77
from typing import List
8+
from typing import Literal
89

910
from lib.core import entities
1011
from lib.core.conditions import Condition
@@ -287,10 +288,30 @@ def copy_multiple(
287288
) -> ServiceResponse:
288289
raise NotImplementedError
289290

291+
@abstractmethod
292+
def copy_move_multiple(
293+
self,
294+
project: entities.ProjectEntity,
295+
from_folder: entities.FolderEntity,
296+
to_folder: entities.FolderEntity,
297+
item_names: List[str],
298+
duplicate_strategy: Literal["skip", "replace", "replace_annotations_only"],
299+
operation: Literal["copy", "move"],
300+
include_annotations: bool = True,
301+
include_pin: bool = False,
302+
) -> ServiceResponse:
303+
raise NotImplementedError
304+
290305
@abstractmethod
291306
def await_copy(self, project: entities.ProjectEntity, poll_id: int, items_count):
292307
raise NotImplementedError
293308

309+
@abstractmethod
310+
def await_copy_move(
311+
self, project: entities.ProjectEntity, poll_id: int, items_count
312+
):
313+
raise NotImplementedError
314+
294315
@abstractmethod
295316
def set_statuses(
296317
self,

0 commit comments

Comments
 (0)