Skip to content

Commit 392148c

Browse files
authored
Merge pull request #420 from superannotateai/firday-integrations
Added integrations
2 parents 2750f6d + 5f6a7b1 commit 392148c

File tree

23 files changed

+731
-390
lines changed

23 files changed

+731
-390
lines changed

docs/source/superannotate.sdk.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ ________
3838
.. autofunction:: superannotate.attach_image_urls_to_project
3939
.. autofunction:: superannotate.upload_images_from_public_urls_to_project
4040
.. autofunction:: superannotate.attach_document_urls_to_project
41+
.. autofunction:: superannotate.attach_items_from_integrated_storage
4142
.. autofunction:: superannotate.upload_image_to_project
4243
.. autofunction:: superannotate.delete_annotations
4344
.. _ref_upload_images_from_folder_to_project:
@@ -108,10 +109,11 @@ __________________
108109

109110
----------
110111

111-
Team contributors
112+
Team
112113
_________________
113114

114115
.. autofunction:: superannotate.get_team_metadata
116+
.. autofunction:: superannotate.get_integrations
115117
.. autofunction:: superannotate.invite_contributors_to_team
116118
.. autofunction:: superannotate.search_team_contributors
117119

src/superannotate/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
attach_document_urls_to_project,
2626
)
2727
from superannotate.lib.app.interface.sdk_interface import attach_image_urls_to_project
28+
from superannotate.lib.app.interface.sdk_interface import attach_items_from_integrated_storage
2829
from superannotate.lib.app.interface.sdk_interface import attach_video_urls_to_project
2930
from superannotate.lib.app.interface.sdk_interface import benchmark
3031
from superannotate.lib.app.interface.sdk_interface import clone_project
@@ -56,6 +57,7 @@
5657
from superannotate.lib.app.interface.sdk_interface import get_folder_metadata
5758
from superannotate.lib.app.interface.sdk_interface import get_image_annotations
5859
from superannotate.lib.app.interface.sdk_interface import get_image_metadata
60+
from superannotate.lib.app.interface.sdk_interface import get_integrations
5961
from superannotate.lib.app.interface.sdk_interface import (
6062
get_project_and_folder_metadata,
6163
)
@@ -135,6 +137,9 @@
135137
# annotations
136138
"get_annotations",
137139
"get_annotations_per_frame",
140+
# integrations
141+
"get_integrations",
142+
"attach_items_from_integrated_storage",
138143
# converters
139144
"convert_json_version",
140145
"import_annotation",

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from lib.app.serializers import SettingsSerializer
3535
from lib.app.serializers import TeamSerializer
3636
from lib.core import LIMITED_FUNCTIONS
37+
from lib.core.entities.integrations import IntegrationEntity
3738
from lib.core.entities.project_entities import AnnotationClassEntity
3839
from lib.core.enums import ImageQuality
3940
from lib.core.exceptions import AppException
@@ -2923,3 +2924,49 @@ def upload_priority_scores(project: NotEmptyStr, scores: List[PriorityScore]):
29232924
if response.errors:
29242925
raise AppException(response.errors)
29252926
return response.data
2927+
2928+
2929+
@Trackable
2930+
@validate_arguments
2931+
def get_integrations():
2932+
"""Get all integrations per team
2933+
2934+
:return: metadata objects of all integrations of the team.
2935+
:rtype: list of dicts
2936+
"""
2937+
response = Controller.get_default().get_integrations()
2938+
if response.errors:
2939+
raise AppException(response.errors)
2940+
integrations = response.data
2941+
return BaseSerializers.serialize_iterable(integrations, ("name", "type", "root"))
2942+
2943+
2944+
@Trackable
2945+
@validate_arguments
2946+
def attach_items_from_integrated_storage(
2947+
project: NotEmptyStr,
2948+
integration: Union[NotEmptyStr, IntegrationEntity],
2949+
folder_path: Optional[NotEmptyStr] = None
2950+
):
2951+
"""Link images from integrated external storage to SuperAnnotate.
2952+
2953+
:param project: project name or folder path where items should be attached (e.g., “project1/folder1”).
2954+
:type project: str
2955+
2956+
:param project: project name or folder path where items should be attached (e.g., “project1/folder1”).
2957+
:type project: str
2958+
2959+
:param integration: existing integration name or metadata dict to pull items from.
2960+
Mandatory keys in integration metadata’s dict is “name”.
2961+
:type integration: str or dict
2962+
2963+
:param folder_path: Points to an exact folder/directory within given storage.
2964+
If None, items are fetched from the root directory.
2965+
:type folder_path: str
2966+
"""
2967+
project_name, folder_name = extract_project_folder(project)
2968+
if isinstance(integration, str):
2969+
integration = IntegrationEntity(name=integration)
2970+
response = Controller.get_default().attach_integrations(project_name, folder_name, integration, folder_path)
2971+
if response.errors:
2972+
raise AppException(response.errors)

src/superannotate/lib/app/mixp/utils/parsers.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import lib.core as constances
22
from lib.app.helpers import extract_project_folder
3+
from lib.core.entities import IntegrationEntity
34
from lib.core.enums import ProjectType
45
from lib.infrastructure.controller import Controller
56

@@ -1238,3 +1239,29 @@ def upload_priority_scores(*args, **kwargs):
12381239
"event_name": "upload_priority_scores",
12391240
"properties": {"Score Count": len(scores)},
12401241
}
1242+
1243+
1244+
def get_integrations(*args, **kwargs):
1245+
return {
1246+
"event_name": "get_integrations",
1247+
"properties": {},
1248+
}
1249+
1250+
1251+
def attach_items_from_integrated_storage(*args, **kwargs):
1252+
project = kwargs.get("project", args[0])
1253+
integration = kwargs.get("integration", args[1])
1254+
folder_path = kwargs.get("folder_path", args[2])
1255+
1256+
project_name, _ = extract_project_folder(project)
1257+
if isinstance(integration, str):
1258+
integration = IntegrationEntity(name=integration)
1259+
project = Controller.get_default().get_project_metadata(project_name)
1260+
return {
1261+
"event_name": "attach_items_from_integrated_storage",
1262+
"properties": {
1263+
"project_type": ProjectType.get_name(project.project_type),
1264+
"integration_name": integration.name,
1265+
"folder_path": bool(folder_path)
1266+
},
1267+
}

src/superannotate/lib/app/serializers.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
from abc import ABC
2+
from typing import Any
3+
from typing import List
4+
from typing import Set
5+
from typing import Union
26

37
import superannotate.lib.core as constance
48
from pydantic import BaseModel
@@ -11,12 +15,36 @@ class BaseSerializers(ABC):
1115
def __init__(self, entity: BaseEntity):
1216
self._entity = entity
1317

14-
def serialize(self):
15-
if isinstance(self._entity, dict):
16-
return self._entity
17-
if isinstance(self._entity, BaseModel):
18-
return self._entity.dict(by_alias=True)
19-
return self._entity.to_dict()
18+
def serialize(self, fields: List[str] = None, by_alias: bool = True, flat: bool = False):
19+
return self._serialize(self._entity, fields, by_alias, flat)
20+
21+
@staticmethod
22+
def _serialize(entity: Any, fields: List[str] = None, by_alias: bool = False, flat: bool = False):
23+
if isinstance(entity, dict):
24+
return entity
25+
if isinstance(entity, BaseModel):
26+
if fields:
27+
fields = set(fields)
28+
if len(fields) == 1:
29+
if flat:
30+
return entity.dict(include=fields, by_alias=by_alias)[next(iter(fields))]
31+
return entity.dict(include=fields, by_alias=by_alias)
32+
return entity.dict(include=fields, by_alias=by_alias)
33+
return entity.dict(by_alias=by_alias)
34+
return entity.to_dict()
35+
36+
@classmethod
37+
def serialize_iterable(
38+
cls,
39+
data: List[Any],
40+
fields: Union[List[str], Set[str]] = None,
41+
by_alias: bool = False,
42+
flat: bool = False
43+
) -> List[Any]:
44+
serialized_data = []
45+
for i in data:
46+
serialized_data.append(cls._serialize(i, fields, by_alias, flat))
47+
return serialized_data
2048

2149

2250
class UserSerializer(BaseSerializers):

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from lib.core.entities.integrations import IntegrationEntity
12
from lib.core.entities.project_entities import AnnotationClassEntity
23
from lib.core.entities.project_entities import BaseEntity
34
from lib.core.entities.project_entities import ConfigEntity
@@ -33,6 +34,7 @@
3334
"UserEntity",
3435
"TeamEntity",
3536
"MLModelEntity",
37+
"IntegrationEntity",
3638
# annotations
3739
"DocumentAnnotation",
3840
"VideoAnnotation",
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from datetime import datetime
2+
3+
from pydantic import BaseModel
4+
from pydantic import Field
5+
6+
7+
class TimedBaseModel(BaseModel):
8+
created_at: datetime = Field(None, alias="createdAt")
9+
updated_at: datetime = Field(None, alias="updatedAt")
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from lib.core.entities.base import TimedBaseModel
2+
from pydantic import Field
3+
4+
5+
class IntegrationEntity(TimedBaseModel):
6+
id: int = None
7+
user_id: str = None
8+
name: str
9+
type: str = "aws"
10+
root: str = Field(None, alias="bucket_name")
11+
source: int = None
12+
13+
class Config:
14+
arbitrary_types_allowed = True

src/superannotate/lib/core/repositories.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from lib.core.conditions import Condition
99
from lib.core.entities import BaseEntity
1010
from lib.core.entities import ProjectEntity
11-
from lib.core.serviceproviders import SuerannotateServiceProvider
11+
from lib.core.serviceproviders import SuperannotateServiceProvider
1212

1313

1414
class BaseReadOnlyRepository(ABC):
@@ -50,7 +50,7 @@ def _drop_nones(data: dict):
5050

5151

5252
class BaseProjectRelatedManageableRepository(BaseManageableRepository):
53-
def __init__(self, service: SuerannotateServiceProvider, project: ProjectEntity):
53+
def __init__(self, service: SuperannotateServiceProvider, project: ProjectEntity):
5454
self._service = service
5555
self._project = project
5656

0 commit comments

Comments
 (0)