Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/source/superannotate.sdk.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ Exports
_______

.. autofunction:: superannotate.prepare_export
.. autofunction:: superannotate.get_annotations
.. autofunction:: superannotate.get_annotations_per_frame
.. _ref_download_export:
.. autofunction:: superannotate.download_export
.. autofunction:: superannotate.get_exports
Expand Down
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
minversion = 3.0
log_cli=true
python_files = test_*.py
addopts = -n auto --dist=loadscope
;addopts = -n auto --dist=loadscope
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ mixpanel==4.8.3
pydantic>=1.8.2
pydantic[email]
setuptools~=57.4.0
superannotate_schemas
aiohttp==3.8.1

8 changes: 1 addition & 7 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,2 @@
Sphinx==3.1.2
tox==3.24.2
pytest==6.2.4
pytest-xdist==2.3.0
pytest-parallel==0.1.0
pytest-rerunfailures==10.2
sphinx_rtd_theme==1.0.0
superannotate_schemas>=1.0.38.b1

5 changes: 5 additions & 0 deletions requirements_extra.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
Sphinx==3.1.2
tox==3.24.2
pytest==6.2.4
pytest-xdist==2.3.0
pytest-parallel==0.1.0
pytest-rerunfailures==10.2
sphinx_rtd_theme==1.0.0
1 change: 1 addition & 0 deletions requirements_prod.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
superannotate_schemas=1.0.39
17 changes: 8 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import sys

from packaging.version import parse
from setuptools import find_packages, setup


with open('src/superannotate/version.py') as f:
version = f.read().rstrip()[15:-1]

requirements_path = "requirements_{}.txt".format('dev' if parse(version).is_prerelease else 'prod')
requirements = []

with open('requirements.txt') as f:
requirements = f.read()
requirements = requirements.splitlines()
with open(requirements_path) as f:
requirements.extend(f.read().splitlines())

if sys.platform == 'linux':
with open('requirements_extra.txt') as f:
requirements_extra = f.read()

requirements_extra = requirements_extra.splitlines()
requirements += requirements_extra
with open("requirements.txt") as f:
requirements.extend(f.read().splitlines())

with open('README.md') as f:
readme = f.read()

readme = "\n".join(readme.split('\n')[2:])


Expand Down
6 changes: 4 additions & 2 deletions src/superannotate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import requests
import superannotate.lib.core as constances
from superannotate.lib import get_default_controller
from packaging.version import parse
from superannotate.lib import get_default_controller
from superannotate.lib.app.analytics.class_analytics import class_distribution
from superannotate.lib.app.exceptions import AppException
from superannotate.lib.app.input_converters.conversion import convert_json_version
Expand Down Expand Up @@ -50,6 +50,7 @@
from superannotate.lib.app.interface.sdk_interface import download_image
from superannotate.lib.app.interface.sdk_interface import download_image_annotations
from superannotate.lib.app.interface.sdk_interface import download_model
from superannotate.lib.app.interface.sdk_interface import get_annotations
from superannotate.lib.app.interface.sdk_interface import get_exports
from superannotate.lib.app.interface.sdk_interface import get_folder_metadata
from superannotate.lib.app.interface.sdk_interface import get_image_annotations
Expand Down Expand Up @@ -107,7 +108,6 @@
)
from superannotate.lib.app.interface.sdk_interface import validate_annotations
from superannotate.logger import get_default_logger
from superannotate.lib.infrastructure.controller import Controller
from superannotate.version import __version__


Expand All @@ -128,6 +128,8 @@
"class_distribution",
"aggregate_annotations_as_df",
"get_exports",
# annotations
"get_annotations",
# converters
"convert_json_version",
"import_annotation",
Expand Down
1 change: 0 additions & 1 deletion src/superannotate/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,3 @@
def get_default_controller():
from lib.infrastructure.controller import Controller
return Controller.get_default()

2 changes: 1 addition & 1 deletion src/superannotate/lib/app/analytics/aggregators.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import copy
import json
from dataclasses import dataclass
from pathlib import Path
from typing import List
from typing import Optional
from typing import Union

import lib.core as constances
import pandas as pd
from dataclasses import dataclass
from lib.app.exceptions import AppException
from lib.core import ATTACHED_VIDEO_ANNOTATION_POSTFIX
from lib.core import PIXEL_ANNOTATION_POSTFIX
Expand Down
3 changes: 1 addition & 2 deletions src/superannotate/lib/app/interface/cli_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from lib.app.interface.sdk_interface import upload_preannotations_from_folder_to_project
from lib.app.interface.sdk_interface import upload_videos_from_folder_to_project
from lib.core.entities import ConfigEntity
from lib.infrastructure.repositories import ConfigRepository
from lib.infrastructure.controller import Controller
from lib.infrastructure.repositories import ConfigRepository


controller = Controller.get_default()
Expand Down Expand Up @@ -72,7 +72,6 @@ def create_project(self, name: str, description: str, type: str):
To create a new project
"""
create_project(name, description, type)
sys.exit(0)

def create_folder(self, project: str, name: str):
"""
Expand Down
58 changes: 54 additions & 4 deletions src/superannotate/lib/app/interface/sdk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from pydantic import conlist
from pydantic import parse_obj_as
from pydantic import StrictBool
from pydantic.error_wrappers import ValidationError
from superannotate.logger import get_default_logger
from tqdm import tqdm

Expand Down Expand Up @@ -193,7 +194,7 @@ def create_project_from_metadata(project_metadata: Project):
contributors=project_metadata.get("contributors", []),
settings=project_metadata.get("settings", []),
annotation_classes=project_metadata.get("classes", []),
workflows=project_metadata.get("workflow", []),
workflows=project_metadata.get("workflows", []),
)
if response.errors:
raise AppException(response.errors)
Expand Down Expand Up @@ -1634,7 +1635,10 @@ def create_annotation_classes_from_classes_json(
else:
data = open(classes_json)
classes_json = json.load(data)
annotation_classes = parse_obj_as(List[AnnotationClassEntity], classes_json)
try:
annotation_classes = parse_obj_as(List[AnnotationClassEntity], classes_json)
except ValidationError:
raise AppException("Couldn't validate annotation classes.")
logger.info(
"Creating annotation classes in project %s from %s.",
project,
Expand All @@ -1645,8 +1649,7 @@ def create_annotation_classes_from_classes_json(
)
if response.errors:
raise AppException(response.errors)

return [i.dict() for i in response.data]
return [BaseSerializers(i).serialize() for i in response.data]


@Trackable
Expand Down Expand Up @@ -2844,3 +2847,50 @@ def invite_contributors_to_team(
if response.errors:
raise AppException(response.errors)
return response.data


@Trackable
@validate_arguments
def get_annotations(project: NotEmptyStr, items: Optional[List[NotEmptyStr]]):
"""Returns annotations for the given list of items.

:param project: project name
:type project: str

:param items: item names. If None all items in the project will be exported
:type items: list of strs

:return: list of annotations
:rtype: list of strs
"""
project_name, folder_name = extract_project_folder(project)
response = Controller.get_default().get_annotations(project_name, folder_name, items)
if response.errors:
raise AppException(response.errors)
return response.data


@Trackable
@validate_arguments
def get_annotations_per_frame(project: NotEmptyStr, video: NotEmptyStr, fps: int = 1):
"""Returns per frame annotations for the given video.


:param project: project name
:type project: str

:param video: video name
:type video: str

:param fps: how many frames per second needs to be extracted from the video.
Will extract 1 frame per second by default.
:type fps: str

:return: list of annotation objects
:rtype: list of dicts
"""
project_name, folder_name = extract_project_folder(project)
response = Controller.get_default().get_annotations_per_frame(project_name, folder_name, video_name=video, fps=fps)
if response.errors:
raise AppException(response.errors)
return response.data
14 changes: 11 additions & 3 deletions src/superannotate/lib/app/mixp/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,17 @@ def track(self, *args, **kwargs):

def __call__(self, *args, **kwargs):
try:
self.__class__.TEAM_DATA = get_default_controller().get_team()
result = self.function(*args, **kwargs)
self._success = True
controller = get_default_controller()
if controller:
self.__class__.TEAM_DATA = controller.get_team()
result = self.function(*args, **kwargs)
self._success = True
else:
raise Exception(
"SuperAnnotate config file not found."
f" Please provide correct config file location to sa.init(<path>) or use "
f"CLI's superannotate init to generate default location config file."
)
except Exception as e:
self._success = False
logger.debug(str(e), exc_info=True)
Expand Down
25 changes: 24 additions & 1 deletion src/superannotate/lib/app/mixp/utils/parsers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import lib.core as constances
from lib import get_default_controller
from lib.app.helpers import extract_project_folder
from lib.core.enums import ProjectType
from lib.infrastructure.controller import Controller
Expand Down Expand Up @@ -1204,3 +1203,27 @@ def add_contributors_to_project(*args, **kwargs):
"event_name": "add_contributors_to_project",
"properties": {"User Role": user_role},
}


def get_annotations(*args, **kwargs):
project = kwargs.get("project", args[0])
items = kwargs.get("items", args[1])

return {
"event_name": "get_annotations",
"properties": {"Project": project, "items_count": len(items)},
}


def get_annotations_per_frame(*args, **kwargs):
project = kwargs.get("project", args[0])
fps = kwargs.get("fps")
if not fps:
try:
fps = args[2]
except IndexError:
fps = 1
return {
"event_name": "get_annotations_per_frame",
"properties": {"Project": project, "fps": fps},
}
2 changes: 1 addition & 1 deletion src/superannotate/lib/app/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def serialize(self):
if isinstance(self._entity, dict):
return self._entity
if isinstance(self._entity, BaseModel):
return self._entity.dict()
return self._entity.dict(by_alias=True)
return self._entity.to_dict()


Expand Down
Empty file.
7 changes: 7 additions & 0 deletions src/superannotate/lib/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ class ClassTypeEnum(BaseTitledEnum):
OBJECT = "object", 1
TAG = "tag", 2

@classmethod
def get_value(cls, name):
for enum in list(cls):
if enum.name.lower() == name.lower():
return enum.value
return "object"


class TrainingStatus(BaseTitledEnum):
NOT_STARTED = "NotStarted", 1
Expand Down
4 changes: 4 additions & 0 deletions src/superannotate/lib/core/serviceproviders.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,7 @@ def get_limitations(
self, team_id: int, project_id: int, folder_id: int = None
) -> ServiceResponse:
raise NotImplementedError

@abstractmethod
def get_annotations(self, project_id: int, team_id: int, folder_id: int, items: List[str]) -> List[dict]:
raise NotImplementedError
Loading