Skip to content

Commit 6783ea7

Browse files
authored
Merge pull request #398 from superannotateai/friday-fix-annotations
Adjust convertor and tessts
2 parents 220fd61 + 18e393e commit 6783ea7

File tree

9 files changed

+65
-20
lines changed

9 files changed

+65
-20
lines changed

src/superannotate/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from superannotate.lib.app.interface.sdk_interface import download_image_annotations
5252
from superannotate.lib.app.interface.sdk_interface import download_model
5353
from superannotate.lib.app.interface.sdk_interface import get_annotations
54+
from superannotate.lib.app.interface.sdk_interface import get_annotations_per_frame
5455
from superannotate.lib.app.interface.sdk_interface import get_exports
5556
from superannotate.lib.app.interface.sdk_interface import get_folder_metadata
5657
from superannotate.lib.app.interface.sdk_interface import get_image_annotations
@@ -130,6 +131,7 @@
130131
"get_exports",
131132
# annotations
132133
"get_annotations",
134+
"get_annotations_per_frame",
133135
# converters
134136
"convert_json_version",
135137
"import_annotation",

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1561,7 +1561,6 @@ def create_annotation_class(
15611561
attribute_groups=attribute_groups,
15621562
class_type=type,
15631563
)
1564-
return response.data.dict()
15651564
return BaseSerializers(response.data).serialize()
15661565

15671566

src/superannotate/lib/core/usecases/annotations.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,9 @@ def execute(self):
554554
return self._response
555555
if not response.data:
556556
self._response.errors = AppException(f"Video {self._video_name} not found.")
557-
558-
self._response.data = VideoFrameGenerator(response.data[1], fps=self._fps)
557+
annotations = response.data
558+
if annotations:
559+
self._response.data = list(VideoFrameGenerator(response.data[0], fps=self._fps))
560+
else:
561+
self._response.data = []
559562
return self._response

src/superannotate/lib/core/usecases/images.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2822,7 +2822,7 @@ def execute(self):
28222822
str(self._download_path),
28232823
)
28242824
classes = self._annotation_classes_repo.get_all()
2825-
classes = [entity.dict() for entity in classes]
2825+
classes = [entity.dict(by_alias=True) for entity in classes]
28262826
json_path = f"{self._download_path}/classes.json"
28272827
json.dump(classes, open(json_path, "w"), indent=4)
28282828
self._response.data = json_path

src/superannotate/lib/core/video_convertor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,5 +141,5 @@ def _process(self):
141141
)
142142

143143
def __iter__(self):
144-
for frame_no in range(1, int(self._frames_count)):
144+
for frame_no in range(1, int(self._frames_count) + 1):
145145
yield self.get_frame(frame_no).dict()

src/superannotate/lib/infrastructure/controller.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ def __init__(self, config_path: str = None, token: str = None):
5555
self._user_id = None
5656
self._team_name = None
5757
self._reporter = None
58-
self._ssl_verify = not (os.environ.get("SA_TESTING", "False").lower() == "false")
59-
58+
self._testing = os.getenv("SA_TESTING", 'False').lower() in ('true', '1', 't')
59+
self._ssl_verify = not self._testing
6060
self._backend_url = os.environ.get("SA_URL", constances.BACKEND_URL)
6161

6262
if token:
@@ -114,6 +114,7 @@ def initialize_backend_client(self):
114114
auth_token=self._token,
115115
logger=self._logger,
116116
verify_ssl=self._ssl_verify,
117+
testing=self._testing
117118
)
118119
self._backend_client.get_session.cache_clear()
119120
return self._backend_client

src/superannotate/lib/infrastructure/services.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,23 @@ class BaseBackendService(SuerannotateServiceProvider):
4343
"""
4444

4545
def __init__(
46-
self, api_url: str, auth_token: str, logger, paginate_by=None, verify_ssl=False
46+
self, api_url: str, auth_token: str, logger, paginate_by=None, verify_ssl=False, testing: bool = False
4747
):
4848
self.api_url = api_url
4949
self._auth_token = auth_token
5050
self.logger = logger
5151
self._paginate_by = paginate_by
52-
self._verify_ssl = False # TODO fix False
52+
self._verify_ssl = verify_ssl
5353
self.team_id = auth_token.split("=")[-1]
54+
self._testing = testing
5455
self.get_session()
5556

57+
@property
58+
def assets_provider_url(self):
59+
if self._testing:
60+
return "https://assets-provider.devsuperannotate.com/api/v1/"
61+
return "https://assets-provider.superannotate.com/api/v1/"
62+
5663
@timed_lru_cache(seconds=360)
5764
def get_session(self):
5865
session = requests.Session()
@@ -166,7 +173,6 @@ class SuperannotateBackendService(BaseBackendService):
166173
Manage projects, images and team in the Superannotate
167174
"""
168175
DEFAULT_CHUNK_SIZE = 1000
169-
STREAMED_DATA_PROVIDER_URL = "https://assets-provider.devsuperannotate.com"
170176

171177
URL_USERS = "users"
172178
URL_LIST_PROJECTS = "projects"
@@ -215,7 +221,7 @@ class SuperannotateBackendService(BaseBackendService):
215221
URL_DELETE_ANNOTATIONS = "annotations/remove"
216222
URL_DELETE_ANNOTATIONS_PROGRESS = "annotations/getRemoveStatus"
217223
URL_GET_LIMITS = "project/{}/limitationDetails"
218-
URL_GET_ANNOTATIONS = "api/v1/images/annotations/stream"
224+
URL_GET_ANNOTATIONS = "images/annotations/stream"
219225

220226
def get_project(self, uuid: int, team_id: int):
221227
get_project_url = urljoin(self.api_url, self.URL_GET_PROJECT.format(uuid))
@@ -1011,7 +1017,7 @@ def get_limitations(
10111017
)
10121018

10131019
def get_annotations(self, project_id: int, team_id: int, folder_id: int, items: List[str]) -> List[dict]:
1014-
get_limits_url = urljoin(self.STREAMED_DATA_PROVIDER_URL, self.URL_GET_ANNOTATIONS)
1020+
get_limits_url = urljoin(self.assets_provider_url, self.URL_GET_ANNOTATIONS)
10151021
query_params = {
10161022
"team_id": team_id,
10171023
"project_id": project_id,

src/superannotate/lib/infrastructure/stream_data_handler.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,22 @@ def map_image_names_to_fetch_streamed_data(data: List[str]):
1313

1414

1515
class StreamedAnnotations:
16-
DELIMITER = b";)"
16+
DELIMITER = b';)'
1717

1818
def __init__(self, headers: dict):
1919
self._headers = headers
2020
self._annotations = []
2121

22-
async def fetch(self, method: str, session: aiohttp.ClientSession, url: str, data: dict = None, params: dict = None):
22+
async def fetch(self, method: str, session: aiohttp.ClientSession, url: str, data: dict = None,
23+
params: dict = None):
2324
response = await session._request(method, url, json=data, params=params)
2425
buffer = b""
2526
async for line in response.content:
2627
slices = line.split(self.DELIMITER)
27-
if slices[0]:
28+
if len(slices) == 1:
29+
buffer += slices[0]
30+
continue
31+
elif slices[0]:
2832
self._annotations.append(json.loads(buffer + slices[0]))
2933
for data in slices[1:-1]:
3034
self._annotations.append(json.loads(data))
@@ -43,7 +47,6 @@ async def get_data(
4347
map_function: Callable = lambda x: x,
4448
verify_ssl: bool = False,
4549
):
46-
4750
async with aiohttp.ClientSession(raise_for_status=True, headers=self._headers,
4851
connector=aiohttp.TCPConnector(ssl=verify_ssl)) as session:
4952

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,52 @@
1+
import json
12
import os
23
from os.path import dirname
4+
from pathlib import Path
5+
6+
import src.superannotate as sa
37
from tests.integration.base import BaseTestCase
48

59

610
class TestGetAnnotations(BaseTestCase):
711
PROJECT_NAME = "test attach video urls"
8-
PATH_TO_URLS = "data_set/attach_urls.csv"
12+
PATH_TO_URLS = "data_set/attach_video_for_annotation.csv"
913
PATH_TO_URLS_WITHOUT_NAMES = "data_set/attach_urls_with_no_name.csv"
1014
PATH_TO_50K_URLS = "data_set/501_urls.csv"
1115
PROJECT_DESCRIPTION = "desc"
16+
ANNOTATIONS_PATH = "data_set/video_annotation"
17+
VIDEO_NAME = "video.mp4"
18+
CLASSES_PATH = "data_set/video_annotation/classes/classes.json"
1219
PROJECT_TYPE = "Video"
1320

1421
@property
1522
def csv_path(self):
16-
return os.path.join(dirname(dirname(__file__)), self.PATH_TO_URLS)
23+
return os.path.join(dirname(dirname(dirname(__file__))), self.PATH_TO_URLS)
24+
25+
@property
26+
def classes_path(self):
27+
return os.path.join(self.folder_path, self.CLASSES_PATH)
1728

1829
@property
19-
def csv_path_without_name_column(self):
20-
return os.path.join(dirname(dirname(__file__)), self.PATH_TO_URLS_WITHOUT_NAMES)
30+
def folder_path(self):
31+
return Path(__file__).parent.parent.parent
32+
33+
@property
34+
def annotations_path(self):
35+
return os.path.join(self.folder_path, self.ANNOTATIONS_PATH)
36+
37+
def test_video_annotation_upload(self):
38+
sa.create_annotation_classes_from_classes_json(self.PROJECT_NAME, self.classes_path)
2139

40+
_, _, _ = sa.attach_video_urls_to_project(
41+
self.PROJECT_NAME,
42+
self.csv_path,
43+
)
44+
sa.upload_annotations_from_folder_to_project(self.PROJECT_NAME, self.annotations_path)
45+
annotations = sa.get_annotations_per_frame(self.PROJECT_NAME, self.VIDEO_NAME, 1)
46+
self.assertEqual(
47+
len(annotations),
48+
int(
49+
json.load(open(f"{self.annotations_path}/{self.VIDEO_NAME}.json"))["metadata"]["duration"] / (
50+
1000 * 1000)
51+
)
52+
)

0 commit comments

Comments
 (0)