Skip to content

Commit bb68f0f

Browse files
authored
Merge pull request #33 from superannotateai/folder-assign-fix
Folder assign fix
2 parents f6b1e00 + 7e808bf commit bb68f0f

File tree

3 files changed

+157
-58
lines changed

3 files changed

+157
-58
lines changed

superannotate/db/project_images.py

Lines changed: 38 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
get_project_default_image_quality_in_editor, _get_available_image_counts,
2020
get_project_metadata
2121
)
22+
from .teams import get_team_metadata
2223
from ..mixp.decorators import Trackable
2324
from .utils import _get_upload_auth_token, _get_boto_session_by_credentials, upload_image_array_to_s3, get_image_array_to_upload, __create_image, __copy_images, __move_images, get_project_folder_string
2425

@@ -564,25 +565,28 @@ def assign_images(project, image_names, user):
564565
:param user: user email
565566
:type user: str
566567
"""
567-
568-
# TODO:
569-
# logger.info("Assign %s images to user %s", len(image_names), user)
570-
# if len(image_names) == 0:
571-
# return
568+
logger.info("Assign %s images to user %s", len(image_names), user)
569+
if len(image_names) == 0:
570+
return
572571

573572
project, folder = get_project_and_folder_metadata(project)
574-
if not folder:
575-
folder = 'root'
576573

577-
project_meta = get_project_metadata(project)
578-
params = {
579-
"project_id": project_meta['id'],
580-
"team_id": project_meta["team_id"]
581-
}
574+
verified_users = get_team_metadata()["users"]
575+
verified_users = [i['id'] for i in verified_users]
576+
if user not in verified_users:
577+
logging.warn(
578+
f'Skipping {user}. {user} is not a verified contributor for the {project["name"]}'
579+
)
580+
581+
folder_name = 'root'
582+
if folder:
583+
folder_name = folder['name']
584+
585+
params = {"project_id": project['id'], "team_id": project["team_id"]}
582586
json_req = {
583587
"image_names": image_names,
584588
"assign_user_id": user,
585-
"folder_name": folder,
589+
"folder_name": folder_name,
586590
}
587591
response = _api.send_request(
588592
req_type='PUT',
@@ -596,30 +600,6 @@ def assign_images(project, image_names, user):
596600
response.status_code, "Couldn't assign images " + response.text
597601
)
598602

599-
# images = search_images((project, project_folder), return_metadata=True)
600-
# image_dict = {}
601-
# for image in images:
602-
# image_dict[image["name"]] = image["id"]
603-
604-
# image_ids = []
605-
# for image_name in image_names:
606-
# image_ids.append(image_dict[image_name])
607-
# team_id, project_id = project["team_id"], project["id"]
608-
# params = {"team_id": team_id, "project_id": project_id}
609-
# if project_folder is not None:
610-
# params['folder_id'] = project_folder['id']
611-
# json_req = {"user_id": user, "image_ids": image_ids}
612-
# response = _api.send_request(
613-
# req_type='POST',
614-
# path='/images/assign',
615-
# params=params,
616-
# json_req=json_req
617-
# )
618-
# if not response.ok:
619-
# raise SABaseException(
620-
# response.status_code, "Couldn't assign images " + response.text
621-
# )
622-
623603

624604
def assign_folder(project, folder_name, users):
625605
"""Assigns folder to users. With SDK, the user can be
@@ -634,25 +614,24 @@ def assign_folder(project, folder_name, users):
634614
"""
635615

636616
project_meta = get_project_metadata(project, include_contributors=True)
637-
project_users = project_meta["contributors"]
617+
verified_users = get_team_metadata()["users"]
638618
project_name = project_meta['name']
639-
project_users = [i['user_id'] for i in project_users]
640-
verified_contributor = []
619+
verified_users = [i['id'] for i in verified_users]
620+
verified_users = set(users).intersection(set(verified_users))
621+
unverified_contributor = set(users) - verified_users
641622

642-
for user in users:
643-
if user not in project_users:
644-
logging.warn(
645-
f'Skipping {user} from assignees. {user} is not a verified contributor for the {project_name}'
646-
)
647-
continue
648-
verified_contributor.append(user)
623+
for user in unverified_contributor:
624+
logging.warn(
625+
f'Skipping {user} from assignees. {user} is not a verified contributor for the {project_name}'
626+
)
627+
continue
649628

650629
params = {
651630
"project_id": project_meta['id'],
652631
"team_id": project_meta["team_id"]
653632
}
654633
json_req = {
655-
"assign_user_ids": verified_contributor,
634+
"assign_user_ids": list(verified_users),
656635
"folder_name": folder_name
657636
}
658637
response = _api.send_request(
@@ -666,7 +645,7 @@ def assign_folder(project, folder_name, users):
666645
raise SABaseException(
667646
response.status_code, "Couldn't assign folder " + response.text
668647
)
669-
logger.info(f'Assigned {folder_name} to users: {verified_contributor}')
648+
logger.info(f'Assigned {folder_name} to users: {list(verified_users)}')
670649

671650

672651
def unassign_folder(project, folder_name):
@@ -697,7 +676,6 @@ def unassign_folder(project, folder_name):
697676
raise SABaseException(
698677
response.status_code, "Couldn't unassign folder " + response.text
699678
)
700-
print('unassign_folder>>>>>>', response.text)
701679

702680

703681
def unassign_images(project, image_names):
@@ -711,11 +689,20 @@ def unassign_images(project, image_names):
711689
:type image_names: list of str
712690
"""
713691
project_meta = get_project_metadata(project)
692+
project, folder = get_project_and_folder_metadata(project)
693+
folder_name = 'root'
694+
if folder:
695+
folder_name = folder['name']
714696
params = {
715697
"project_id": project_meta['id'],
716698
"team_id": project_meta["team_id"]
717699
}
718-
json_req = {"image_names": image_names, "remove_user_ids": ["all"]}
700+
json_req = {
701+
"image_names": image_names,
702+
"remove_user_ids": ["all"],
703+
"folder_name": folder_name
704+
}
705+
719706
response = _api.send_request(
720707
req_type='PUT',
721708
path='/images/editAssignment',
@@ -727,5 +714,3 @@ def unassign_images(project, image_names):
727714
raise SABaseException(
728715
response.status_code, "Couldn't unassign images " + response.text
729716
)
730-
731-
print('unassign_images>>>>>>', response.text)

superannotate/db/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ..exceptions import SABaseException, SAImageSizeTooLarge, SANonExistingProjectNameException
1414
import datetime
1515
import boto3
16+
from .project_api import get_project_metadata_bare
1617

1718
_api = API.get_instance()
1819
logger = logging.getLogger("superannotate-python-sdk")

tests/test_assign_images.py

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from pathlib import Path
22
import time
3-
43
import pytest
5-
64
import superannotate as sa
5+
from superannotate.api import API
6+
7+
_api = API.get_instance()
78

89
PROJECT_NAME_VECTOR1 = "test assign images1"
910
PROJECT_NAME_VECTOR2 = "test assign images2"
@@ -16,7 +17,7 @@ def test_assign_images(tmpdir):
1617
projects = sa.search_projects(PROJECT_NAME_VECTOR1, return_metadata=True)
1718
for project in projects:
1819
sa.delete_project(project)
19-
20+
time.sleep(1)
2021
project = sa.create_project(PROJECT_NAME_VECTOR1, "test", "Vector")
2122
email = sa.get_team_metadata()["users"][0]["email"]
2223
sa.share_project(project, email, "QA")
@@ -70,7 +71,7 @@ def test_assign_images_folder(tmpdir):
7071
projects = sa.search_projects(PROJECT_NAME_VECTOR2, return_metadata=True)
7172
for project in projects:
7273
sa.delete_project(project)
73-
74+
time.sleep(1)
7475
project = sa.create_project(PROJECT_NAME_VECTOR2, "test", "Vector")
7576
email = sa.get_team_metadata()["users"][0]["email"]
7677
sa.share_project(project, email, "QA")
@@ -118,4 +119,116 @@ def test_assign_images_folder(tmpdir):
118119
assert im1_metadata["annotator_id"] == email
119120
assert im2_metadata["annotator_id"] == email
120121
assert im1_metadata["qa_id"] is None
121-
assert im2_metadata["qa_id"] is None
122+
assert im2_metadata["qa_id"] is None
123+
124+
125+
def test_unassign_images(tmpdir):
126+
tmpdir = Path(tmpdir)
127+
projects = sa.search_projects(PROJECT_NAME_VECTOR1, return_metadata=True)
128+
for project in projects:
129+
sa.delete_project(project)
130+
time.sleep(1)
131+
project = sa.create_project(PROJECT_NAME_VECTOR1, "test", "Vector")
132+
email = sa.get_team_metadata()["users"][0]["email"]
133+
sa.share_project(project, email, "QA")
134+
sa.upload_images_from_folder_to_project(
135+
project, "./tests/sample_project_vector"
136+
)
137+
sa.assign_images(
138+
project, ["example_image_1.jpg", "example_image_2.jpg"], email
139+
)
140+
sa.unassign_images(
141+
project,
142+
["example_image_1.jpg", "example_image_2.jpg"],
143+
)
144+
145+
im1_metadata = sa.get_image_metadata(project, "example_image_1.jpg")
146+
im2_metadata = sa.get_image_metadata(project, "example_image_2.jpg")
147+
148+
assert im1_metadata["qa_id"] == None
149+
assert im2_metadata["qa_id"] == None
150+
151+
152+
def test_assign_folder(tmpdir):
153+
tmpdir = Path(tmpdir)
154+
projects = sa.search_projects(PROJECT_NAME_VECTOR1, return_metadata=True)
155+
for project in projects:
156+
sa.delete_project(project)
157+
time.sleep(1)
158+
project = sa.create_project(PROJECT_NAME_VECTOR1, "test", "Vector")
159+
folder_name = "assign_folder"
160+
sa.create_folder(project, folder_name)
161+
email = sa.get_team_metadata()["users"][1]["email"]
162+
sa.share_project(project, email, "QA")
163+
sa.assign_folder(project, folder_name, [email])
164+
folders = _search_folders(project, includeUsers=True)
165+
assert len(folders["data"][0]['folder_users']) > 0
166+
167+
168+
def test_unassign_folder(tmpdir):
169+
tmpdir = Path(tmpdir)
170+
projects = sa.search_projects(PROJECT_NAME_VECTOR1, return_metadata=True)
171+
for project in projects:
172+
sa.delete_project(project)
173+
time.sleep(1)
174+
project = sa.create_project(PROJECT_NAME_VECTOR1, "test", "Vector")
175+
folder_name = "assign_folder"
176+
sa.create_folder(project, folder_name)
177+
email = sa.get_team_metadata()["users"][1]["email"]
178+
sa.share_project(project, email, "QA")
179+
sa.assign_folder(project, folder_name, [email])
180+
folders = _search_folders(project, includeUsers=True)
181+
assert len(folders["data"][0]['folder_users']) > 0
182+
sa.unassign_folder(project, folder_name)
183+
folders = _search_folders(project, includeUsers=True)
184+
assert len(folders["data"][0]['folder_users']) == 0
185+
186+
187+
def _search_folders(project, folder_name=None, includeUsers=False):
188+
team_id, project_id = project["team_id"], project["id"]
189+
params = {
190+
'team_id': team_id,
191+
'project_id': project_id,
192+
'offset': 0,
193+
'name': folder_name,
194+
'is_root': 0,
195+
'includeUsers': includeUsers
196+
}
197+
198+
response = _api.send_request(req_type='GET', path='/folders', params=params)
199+
response = response.json()
200+
return response
201+
202+
203+
def test_assign_folder_unverified_users(tmpdir, caplog):
204+
tmpdir = Path(tmpdir)
205+
projects = sa.search_projects(PROJECT_NAME_VECTOR1, return_metadata=True)
206+
for project in projects:
207+
sa.delete_project(project)
208+
time.sleep(1)
209+
project = sa.create_project(PROJECT_NAME_VECTOR1, "test", "Vector")
210+
folder_name = "assign_folder"
211+
sa.create_folder(project, folder_name)
212+
email = "unverified_user@mail.com"
213+
sa.assign_folder(project, folder_name, [email])
214+
"Skipping unverified_user@mail.com from assignees." in caplog.text
215+
216+
217+
def test_assign_images_unverified_user(tmpdir, caplog):
218+
tmpdir = Path(tmpdir)
219+
220+
projects = sa.search_projects(PROJECT_NAME_VECTOR2, return_metadata=True)
221+
for project in projects:
222+
sa.delete_project(project)
223+
time.sleep(1)
224+
project = sa.create_project(PROJECT_NAME_VECTOR2, "test", "Vector")
225+
sa.create_folder(project, FOLDER2)
226+
project_folder = project["name"] + "/" + FOLDER2
227+
sa.upload_images_from_folder_to_project(
228+
project_folder, "./tests/sample_project_vector"
229+
)
230+
email = "unverified_user@email.com"
231+
sa.assign_images(
232+
project_folder, ["example_image_1.jpg", "example_image_2.jpg"], email
233+
)
234+
"Skipping unverified_user@mail.com from assignees." in caplog.text

0 commit comments

Comments
 (0)