Skip to content

Commit 64558bc

Browse files
committed
Added explore service path handling
1 parent b8ff51e commit 64558bc

File tree

4 files changed

+149
-15
lines changed

4 files changed

+149
-15
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def execute(self) -> Response:
147147
# tmp wrapper
148148
if "assignment" in item:
149149
item["assignments"] = item.pop("assignment")
150-
item["url"] = item["path"]
150+
item["url"] = item.get("path", None)
151151
item["path"] = (
152152
f"{self._project.name}"
153153
f"{'/' + item['folder_name'] if not item['is_root_folder'] else ''}"

tests/applicatoin/custom_workflow.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import os
2+
import time
3+
from typing import List
4+
import contextvars
5+
from functools import lru_cache
6+
7+
from tests import DATA_SET_PATH
8+
from tests.base import BaseApplicationTestCase
9+
from src.superannotate import SAClient, AppException
10+
11+
sa = SAClient()
12+
13+
attached_item_names = contextvars.ContextVar('attached_item_names')
14+
attached_items_status = contextvars.ContextVar('attached_items_status')
15+
contributors_role = contextvars.ContextVar('contributors_role')
16+
annotation_classes_count = contextvars.ContextVar('annotation_classes_count')
17+
18+
19+
class TestWorkflow(BaseApplicationTestCase):
20+
PROJECT_NAME = 'TestWorkflow'
21+
22+
CLASSES_PATH = "sample_project_vector/classes/classes.json"
23+
ANNOTATIONS_PATH = "sample_project_vector"
24+
PROJECT_TYPE = "GenAi"
25+
26+
@property
27+
def classes_path(self):
28+
return os.path.join(DATA_SET_PATH, self.CLASSES_PATH)
29+
30+
@property
31+
def annotations_path(self):
32+
return os.path.join(DATA_SET_PATH, self.ANNOTATIONS_PATH)
33+
34+
@lru_cache()
35+
def get_not_admin_contributor_emails(self) -> List[str]:
36+
contributor_emails = []
37+
for i in sa.search_team_contributors():
38+
if i['user_role'] != 2: # skipping admins etc
39+
contributor_emails.append(i['email'])
40+
return contributor_emails
41+
42+
def step_1_add_contributors(self):
43+
contributor_emails = self.get_not_admin_contributor_emails()
44+
assert len(contributor_emails) > 0, "for the test there should be more then 1 contributor"
45+
# Give the backend time to think
46+
time.sleep(3)
47+
contributors_role.set("Annotator")
48+
added, skipped, = sa.add_contributors_to_project(self.PROJECT_NAME, contributor_emails, "Annotator")
49+
assert len(skipped) == 0
50+
assert len(added) == len(contributor_emails)
51+
52+
53+
def step_3_attach_items(self):
54+
name_prefix, count = 'example_image_', 4
55+
items_to_attach = [
56+
{"name": f"{name_prefix}{i}.jpg", "url": f"url_{i}"}
57+
for i in range(1, count + 1)
58+
]
59+
attached_item_names.set([i['name'] for i in items_to_attach])
60+
attached_items_status.set("Completed")
61+
uploaded, _, __ = sa.attach_items(
62+
self.PROJECT_NAME,
63+
items_to_attach, # noqa
64+
annotation_status="Completed"
65+
)
66+
assert len(uploaded) == count
67+
items = sa.search_items(self.PROJECT_NAME)
68+
assert all(i['annotation_status'] == 'Completed' for i in items)
69+
70+
def step_4_create_annotation_classes(self):
71+
created = sa.create_annotation_classes_from_classes_json(self.PROJECT_NAME, self.classes_path)
72+
annotation_classes_count.set(4)
73+
assert len(created) == 4
74+
75+
def step_5_upload_annotations(self):
76+
uploaded, _, __ = sa.upload_annotations_from_folder_to_project(
77+
self.PROJECT_NAME, self.annotations_path, keep_status=True
78+
)
79+
attached_items_count = len(attached_item_names.get())
80+
assert len(uploaded) == attached_items_count
81+
# assert that all items have a status of "attached_items_status"
82+
items = sa.search_items(self.PROJECT_NAME, annotation_status=attached_items_status.get())
83+
assert len(items) == attached_items_count
84+
85+
def step_6_get_annotations(self):
86+
annotations = sa.get_annotations(self.PROJECT_NAME)
87+
assert len(annotations) == len(attached_item_names.get())
88+
assert all(i['metadata']['status'] == attached_items_status.get() for i in annotations)
89+
90+
def step_7_assign_item(self):
91+
item = attached_item_names.get()[0]
92+
user = self.get_not_admin_contributor_emails()[0]
93+
sa.assign_items(self.PROJECT_NAME, [item], user)
94+
item_data = sa.get_item_metadata(self.PROJECT_NAME, item)
95+
if contributors_role.get() in ("Annotator", 'QA'):
96+
assert item_data[f"{contributors_role.get().lower()}_email"] == user
97+
assert len(item_data['assignments']) == 1
98+
assert item_data['assignments'][0]['user_role'] == contributors_role.get()
99+
100+
def step_8_unassign_item(self):
101+
item = attached_item_names.get()[0]
102+
sa.unassign_items(self.PROJECT_NAME, [item])
103+
item_data = sa.get_item_metadata(self.PROJECT_NAME, item)
104+
if contributors_role.get() in ("Annotator", 'QA'):
105+
assert item_data[f"{contributors_role.get().lower()}_email"] is None
106+
assert len(item_data['assignments']) == 0
107+
108+
def step_999_clone(self):
109+
new_name = 'step_clone'
110+
try:
111+
sa.delete_project(new_name)
112+
except AppException:
113+
...
114+
project = sa.clone_project(new_name, self.PROJECT_NAME, copy_contributors=True)
115+
contributors = self.get_not_admin_contributor_emails()
116+
assert len(contributors) == len(project['users'])
117+
assert all(i['user_role'] == contributors_role.get() for i in project['users'])
118+
assert len(project['classes']) == annotation_classes_count.get()
119+
120+
121+
def _steps(self):
122+
for name in dir(self):
123+
if name.startswith("step"):
124+
yield name, getattr(self, name)
125+
126+
def test_steps(self):
127+
for name, step in self._steps():
128+
try:
129+
step()
130+
except Exception as e:
131+
self.fail("{} failed ({}: {})".format(step, type(e), e))

tests/applicatoin/base.py renamed to tests/base.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,30 @@
22

33
from src.superannotate import SAClient
44

5-
65
sa = SAClient()
76

87

9-
class BaseTestCase(TestCase):
8+
class BaseApplicationTestCase(TestCase):
109
PROJECT_NAME = ""
1110
PROJECT_DESCRIPTION = "Desc"
1211
PROJECT_TYPE = "Vector"
1312
TEST_FOLDER_PATH = "data_set"
1413

1514
def __init__(self, *args, **kwargs):
1615
super().__init__(*args, **kwargs)
17-
BaseTestCase.PROJECT_NAME = BaseTestCase.__class__.__name__
16+
BaseApplicationTestCase.PROJECT_NAME = BaseApplicationTestCase.__class__.__name__
1817

19-
def setUp(self, *args, **kwargs):
20-
self.tearDown()
21-
self._project = sa.create_project(
22-
self.PROJECT_NAME, self.PROJECT_DESCRIPTION, self.PROJECT_TYPE
18+
@classmethod
19+
def setUpClass(cls, *args, **kwargs):
20+
cls.tearDownClass()
21+
cls._project = sa.create_project(
22+
cls.PROJECT_NAME, cls.PROJECT_DESCRIPTION, cls.PROJECT_TYPE
2323
)
2424

25-
def tearDown(self) -> None:
25+
@classmethod
26+
def tearDownClass(cls) -> None:
2627
try:
27-
projects = sa.search_projects(self.PROJECT_NAME, return_metadata=True)
28+
projects = sa.search_projects(cls.PROJECT_NAME, return_metadata=True)
2829
for project in projects:
2930
try:
3031
sa.delete_project(project)
@@ -33,14 +34,16 @@ def tearDown(self) -> None:
3334
except Exception as e:
3435
print(str(e))
3536

36-
def _attach_items(self, count=5, folder=None):
37+
def attach_random_items(self, count=5, name_prefix='example_image_', folder=None):
3738
path = self.PROJECT_NAME
3839
if folder:
3940
path = f"{self.PROJECT_NAME}/{folder}"
40-
sa.attach_items(
41+
uploaded, _, __ = sa.attach_items(
4142
path,
4243
[
43-
{"name": f"example_image_{i}.jpg", "url": f"url_{i}"}
44+
{"name": f"{name_prefix}{i}.jpg", "url": f"url_{i}"}
4445
for i in range(1, count + 1)
4546
], # noqa
4647
)
48+
assert len(uploaded) == count
49+

tests/integration/export/test_export.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ def test_upload_s3(self):
114114

115115
def test_export_with_statuses(self):
116116
with tempfile.TemporaryDirectory() as tmpdir_name:
117-
export = sa.prepare_export(self.PROJECT_NAME, annotation_statuses=['NotStarted'], include_fuse=True)
117+
export = sa.prepare_export(self.PROJECT_NAME, annotation_statuses=['NotStarted', 'InProgress'], include_fuse=True)
118118
sa.download_export(self.PROJECT_NAME, export, tmpdir_name)
119119
assert not filecmp.dircmp(tmpdir_name, self.TEST_FOLDER_PATH).left_only
120-
assert not filecmp.dircmp(tmpdir_name, self.TEST_FOLDER_PATH).right_only
120+
assert filecmp.dircmp(tmpdir_name, self.TEST_FOLDER_PATH).right_only

0 commit comments

Comments
 (0)