Skip to content
5 changes: 1 addition & 4 deletions src/superannotate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import os
import sys



__version__ = "4.4.7dev2"

__version__ = "4.4.7dev6"

sys.path.append(os.path.split(os.path.realpath(__file__))[0])

Expand Down
30 changes: 15 additions & 15 deletions src/superannotate/lib/app/analytics/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pandas as pd
import plotly.express as px
from lib.app.exceptions import AppException
from lib.core import DEPRICATED_DOCUMENT_VIDEO_MESSAGE
from superannotate.logger import get_default_logger


Expand Down Expand Up @@ -44,14 +43,6 @@ def aggregate_image_annotations_as_df(
:rtype: pandas DataFrame
"""

json_paths = list(Path(str(project_root)).glob("*.json"))
if (
json_paths
and "___pixel.json" not in json_paths[0].name
and "___objects.json" not in json_paths[0].name
):
raise AppException(DEPRICATED_DOCUMENT_VIDEO_MESSAGE)

logger.info("Aggregating annotations from %s as pandas DataFrame", project_root)

annotation_data = {
Expand Down Expand Up @@ -101,12 +92,16 @@ def aggregate_image_annotations_as_df(
classes_json = json.load(open(classes_path))
class_name_to_color = {}
class_group_name_to_values = {}
freestyle_attributes = set()
for annotation_class in classes_json:
name = annotation_class["name"]
color = annotation_class["color"]
class_name_to_color[name] = color
class_group_name_to_values[name] = {}
for attribute_group in annotation_class["attribute_groups"]:
group_type = attribute_group.get("group_type")
if group_type and group_type in ["text", "numeric"]:
freestyle_attributes.add(attribute_group["name"])
class_group_name_to_values[name][attribute_group["name"]] = []
for attribute in attribute_group["attributes"]:
class_group_name_to_values[name][attribute_group["name"]].append(
Expand Down Expand Up @@ -175,10 +170,14 @@ def __get_user_metadata(annotation):

if not annotations_paths:
logger.warning(f"Could not find annotations in {project_root}.")
if len(list(Path(project_root).rglob("*___objects.json"))) > 0:

if "___objects.json" in annotations_paths[0].name:
type_postfix = "___objects.json"
else:
elif "___pixel.json" in annotations_paths[0].name:
type_postfix = "___pixel.json"
else:
type_postfix = ".json"

for annotation_path in annotations_paths:
annotation_json = json.load(open(annotation_path))
parts = annotation_path.name.split(type_postfix)
Expand Down Expand Up @@ -294,6 +293,7 @@ def __get_user_metadata(annotation):
not in class_group_name_to_values[annotation_class_name][
attribute_group
]
and attribute_group not in freestyle_attributes
):
logger.warning(
"Annotation class group value %s not in classes json. Skipping.",
Expand Down Expand Up @@ -383,9 +383,9 @@ def instance_consensus(inst_1, inst_2):

:param inst_1: First instance for consensus score.
:type inst_1: shapely object

:param inst_2: Second instance for consensus score.
:type inst_2: shapely object

"""
if inst_1.type == inst_2.type == "Polygon":
intersect = inst_1.intersection(inst_2)
Expand All @@ -404,19 +404,19 @@ def image_consensus(df, image_name, annot_type):

:param df: Annotation data of all images
:type df: pandas.DataFrame

:param image_name: The image name for which the consensus score will be computed
:type image_name: str

:param annot_type: Type of annotation instances to consider. Available candidates are: ["bbox", "polygon", "point"]
:type dataset_format: str

"""

try:
from shapely.geometry import Point, Polygon, box
except ImportError:
raise ImportError(
"To use superannotate.benchmark or superannotate.consensus functions please install "
"shapely package in Anaconda enviornment with # conda install shapely"
"To use superannotate.benchmark or superannotate.consensus functions please install shapely package."
)

image_df = df[df["imageName"] == image_name]
Expand Down
4 changes: 3 additions & 1 deletion src/superannotate/lib/app/interface/sdk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -2176,8 +2176,10 @@ def attach_items_from_integrated_storage(
"""
project, folder = self.controller.get_project_folder_by_path(project)
_integration = None
if isinstance(integration, str):
integration = IntegrationEntity(name=integration)
for i in self.controller.integrations.list().data:
if integration == i.name:
if integration.name == i.name:
_integration = i
break
else:
Expand Down
9 changes: 4 additions & 5 deletions src/superannotate/lib/core/usecases/items.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import copy
import traceback
from collections import defaultdict
from concurrent.futures import as_completed
from concurrent.futures import ThreadPoolExecutor
Expand Down Expand Up @@ -942,12 +943,10 @@ def execute(
for future in as_completed(futures):
try:
ids = future.result()
except Exception as e:
continue

self.item_ids.extend(ids)
self.item_ids.extend(ids)
except Exception:
logger.debug(traceback.format_exc())

futures = []
subsets = self._service_provider.subsets.list(self.project).data
subset = None
for s in subsets:
Expand Down
7 changes: 5 additions & 2 deletions src/superannotate/lib/core/usecases/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def execute(self):


class DownloadExportUseCase(BaseReportableUseCase):
FORBIDDEN_CHARS = "*./\[]:;|,\"\'"
def __init__(
self,
service_provider: BaseServiceProvider,
Expand Down Expand Up @@ -204,8 +205,10 @@ def download_to_local_storage(self, destination: str, extract_zip=False):
raise AppException("Couldn't download export.")
time.sleep(1)
self.reporter.stop_spinner()
filename = Path(export["path"]).name
filepath = Path(destination) / filename
filename = Path(export["path"]).stem
for char in DownloadExportUseCase.FORBIDDEN_CHARS:
filename=filename.replace(char, "_")
filepath = Path(destination) / (filename+'.zip')
with requests.get(export["download"], stream=True) as response:
response.raise_for_status()
with open(filepath, "wb") as f:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import math
import os
from os.path import dirname
from pathlib import Path

from src.superannotate import SAClient
from tests.integration.base import BaseTestCase
Expand All @@ -24,7 +23,7 @@ class TestGetAnnotations(BaseTestCase):

@property
def csv_path(self):
return os.path.join(dirname(dirname(dirname(__file__))), self.PATH_TO_URLS)
return os.path.join(DATA_SET_PATH, self.PATH_TO_URLS)

@property
def classes_path(self):
Expand Down
15 changes: 0 additions & 15 deletions tests/integration/classes/test_create_annotation_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,6 @@ def test_create_annotation_class(self):
msg = str(e)
self.assertEqual(msg, "Predefined tagging functionality is not supported for projects of type Video.")

def test_create_supported_annotation_class(self):
msg = ""
try:
sa.create_annotation_class(
self.PROJECT_NAME, "test_add", "#FF0000",
attribute_groups=[
{
"group_type": "text",
"name": "name",
}
]
)
except Exception as e:
msg = str(e)
self.assertEqual(msg, "This project type doesn't support the attribute group types 'text' and 'numeric'.")

def test_create_radio_annotation_class_attr_required(self):
msg = ""
Expand Down
28 changes: 28 additions & 0 deletions tests/integration/subsets/test_subsets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from src.superannotate import SAClient

from tests.integration.base import BaseTestCase

sa = SAClient()


class TestSubSets(BaseTestCase):
PROJECT_NAME = "Test-TestSubSets"
PROJECT_DESCRIPTION = "Desc"
PROJECT_TYPE = "Vector"
SUBSET_NAME = "SUBSET"

def test_add_items_to_subset(self):
item_names = [{"name": f"earth_mov_00{i}.jpg", "url": f"url_{i}"} for i in range(1, 6)]
sa.attach_items(
self.PROJECT_NAME,
item_names # noqa
)
subset_data = []
for i in item_names:
subset_data.append(
{
"name": i['name'],
"path": self.PROJECT_NAME
}
)
sa.add_items_to_subset(self.PROJECT_NAME, self.SUBSET_NAME, subset_data)