Skip to content

Commit 8eb16ad

Browse files
authored
Merge pull request #126 from superannotateai/SAS-3046
Sas 3046
2 parents 5a2dccb + a74bb61 commit 8eb16ad

File tree

7 files changed

+206
-137
lines changed

7 files changed

+206
-137
lines changed

docs/source/superannotate.sdk.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ ________
4646
.. autofunction:: superannotate.upload_images_from_folder_to_project
4747
.. autofunction:: superannotate.upload_video_to_project
4848
.. autofunction:: superannotate.upload_videos_from_folder_to_project
49+
.. autofunction:: superannotate.attach_video_urls_to_project
4950
.. _ref_upload_annotations_from_folder_to_project:
5051
.. autofunction:: superannotate.upload_annotations_from_folder_to_project
5152
.. autofunction:: superannotate.upload_preannotations_from_folder_to_project

superannotate/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def consensus(*args, **kwargs):
7575
upload_images_from_public_urls_to_project,
7676
upload_images_from_s3_bucket_to_project, upload_images_to_project,
7777
attach_image_urls_to_project, upload_preannotations_from_folder_to_project,
78-
upload_video_to_project, upload_videos_from_folder_to_project
78+
upload_video_to_project, upload_videos_from_folder_to_project,
79+
attach_video_urls_to_project
7980
)
8081
from .db.search_projects import search_projects
8182
from .db.teams import (

superannotate/__main__.py

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -48,37 +48,35 @@ def ask_token():
4848

4949

5050
def main():
51-
available_commands = "Available commands to superannotate CLI are: init version create-project create-folder upload-images upload-videos upload-preannotations upload-annotations export-project"
51+
available_commands = {
52+
"create-project": create_project,
53+
"create-folder": create_folder,
54+
"upload-images": image_upload,
55+
"attach-image-urls": attach_video_urls,
56+
"upload-videos": video_upload,
57+
"upload-preannotations": preannotations_upload,
58+
"upload-annotations": preannotations_upload,
59+
"init": lambda *args, **kwargs: ask_token(),
60+
"export-project": export_project,
61+
"attach-video-urls": attach_video_urls,
62+
"version": lambda *args, **kwargs: print(f"SuperAnnotate Python SDK version {sa.__version__}")
63+
}
5264
if len(sys.argv) == 1:
53-
raise SABaseException(
54-
0, "No command given to superannotate CLI. " + available_commands
65+
print(
66+
"No command given to superannotate CLI. Available commands to superannotate CLI are:"
67+
+ ", ".join(available_commands.keys())
5568
)
69+
5670
command = sys.argv[1]
5771
further_args = sys.argv[2:]
58-
59-
if command == "create-project":
60-
create_project(command, further_args)
61-
elif command == "create-folder":
62-
create_folder(command, further_args)
63-
elif command == "upload-images":
64-
image_upload(command, further_args)
65-
elif command == "attach-image-urls":
66-
attach_image_urls(command, further_args)
67-
elif command == "upload-videos":
68-
video_upload(command, further_args)
69-
elif command in ["upload-preannotations", "upload-annotations"]:
70-
preannotations_upload(command, further_args)
71-
elif command == "init":
72-
ask_token()
73-
elif command == "export-project":
74-
export_project(command, further_args)
75-
elif command == "version":
76-
print(f"SuperAnnotate Python SDK version {sa.__version__}")
77-
else:
78-
raise SABaseException(
79-
0, "Wrong command " + command + " to superannotate CLI. " +
80-
available_commands
81-
)
72+
try:
73+
available_commands[command](command, further_args)
74+
except KeyError:
75+
sys.stdout.write("Wrong command " + command + " to superannotate CLI. " + ", ".join(available_commands.keys()))
76+
except SABaseException as e:
77+
sys.stdout.write(e.message)
78+
except BaseException as e:
79+
sys.stdout.write(str(e))
8280

8381

8482
def _list_str(values):
@@ -364,5 +362,30 @@ def export_project(command_name, args):
364362
)
365363

366364

365+
def attach_video_urls(command_name, args):
366+
parser = argparse.ArgumentParser(prog=_CLI_COMMAND + " " + command_name)
367+
parser.add_argument(
368+
'--project', required=True, help='Project name to upload'
369+
)
370+
parser.add_argument(
371+
'--attachments',
372+
required=True,
373+
help='path to csv file on attachments metadata'
374+
)
375+
parser.add_argument(
376+
'--annotation_status',
377+
required=False,
378+
default="NotStarted",
379+
help=
380+
'Set images\' annotation statuses after upload. Default is NotStarted'
381+
)
382+
args = parser.parse_args(args)
383+
sa.attach_video_urls_to_project(
384+
project=args.project,
385+
attachments=args.attachments,
386+
annotation_status=args.annotation_status
387+
)
388+
389+
367390
if __name__ == "__main__":
368391
main()

superannotate/common.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
SPECIAL_CHARACTERS_IN_PROJECT_FOLDER_NAMES = set('/\\:*?"<>|')
2121

22-
_PROJECT_TYPES = {"Vector": 1, "Pixel": 2}
22+
_PROJECT_TYPES = {"Vector": 1, "Pixel": 2, "Video": 3}
2323

2424
_ANNOTATION_STATUSES = {
2525
"NotStarted": 1,
@@ -109,10 +109,11 @@ def project_type_int_to_str(project_type):
109109
:return: 'Vector' or 'Pixel'
110110
:rtype: str
111111
"""
112+
if project_type not in _PROJECT_TYPES.values():
113+
raise RuntimeError("NA Project type")
112114
for k, v in _PROJECT_TYPES.items():
113115
if v == project_type:
114116
return k
115-
raise RuntimeError("NA Project type")
116117

117118

118119
def user_role_str_to_int(user_role):

superannotate/db/project_images.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from .teams import get_team_metadata
2323
from ..mixp.decorators import Trackable
2424
from .utils import _unassign_images, _assign_images, _get_upload_auth_token, _get_boto_session_by_credentials, upload_image_array_to_s3, \
25-
get_image_array_to_upload, __create_image, __copy_images, __move_images, get_project_folder_string
25+
get_image_array_to_upload, __create_file, __copy_images, __move_images, get_project_folder_string
2626

2727
logger = logging.getLogger("superannotate-python-sdk")
2828
_api = API.get_instance()
@@ -118,7 +118,7 @@ def upload_image_to_project(
118118
except Exception as e:
119119
raise SABaseException(0, "Couldn't upload to data server.") from e
120120

121-
__create_image(
121+
__create_file(
122122
[img_name], [key],
123123
project,
124124
annotation_status,
@@ -640,8 +640,8 @@ def assign_folder(project, folder_name, users):
640640

641641
@Trackable
642642
def unassign_folder(project, folder_name):
643-
"""Removes assignment of given folder for all assignees.
644-
With SDK, the user can be assigned to a role in the project
643+
"""Removes assignment of given folder for all assignees.
644+
With SDK, the user can be assigned to a role in the project
645645
with the share_project function.
646646
647647
:param project: project name or folder path (e.g., "project1/folder1")

superannotate/db/projects.py

Lines changed: 80 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -761,51 +761,18 @@ def attach_image_urls_to_project(
761761
project, attachments, annotation_status="NotStarted"
762762
):
763763
"""Link images on external storage to SuperAnnotate.
764-
764+
765765
:param project: project name or project folder path
766766
:type project: str or dict
767767
:param attachments: path to csv file on attachments metadata
768768
:type attachments: Pathlike (str or Path)
769769
:param annotation_status: value to set the annotation statuses of the linked images: NotStarted InProgress QualityCheck Returned Completed Skipped
770770
:type annotation_status: str
771771
772-
:return: list of linked image names, list of failed image names, list of duplicate image names
773-
:rtype: tuple
772+
:return: attached images, failed images, skipped images
773+
:rtype: (list, list, list)
774774
"""
775-
project, folder = get_project_and_folder_metadata(project)
776-
folder_name = project["name"] + (f'/{folder["name"]}' if folder else "")
777-
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
778-
if upload_state == "Basic":
779-
raise SABaseException(
780-
0,
781-
"You cannot attach URLs in this type of project. Please attach it in an external storage project"
782-
)
783-
annotation_status = common.annotation_status_str_to_int(annotation_status)
784-
team_id, project_id = project["team_id"], project["id"]
785-
image_data = pd.read_csv(attachments, dtype=str)
786-
image_data = image_data[~image_data["url"].isnull()]
787-
for ind, _ in image_data[image_data["name"].isnull()].iterrows():
788-
name_try = str(uuid.uuid4())
789-
image_data.at[ind, "name"] = name_try
790-
image_data = pd.DataFrame(image_data, columns=["name", "url"])
791-
img_names_urls = image_data.values.tolist()
792-
793-
if folder:
794-
folder_id = folder["id"]
795-
else:
796-
folder_id = get_project_root_folder_id(project)
797-
798-
list_of_uploaded, list_of_not_uploaded, duplicate_images = _attach_urls(
799-
img_names_urls=img_names_urls,
800-
team_id=team_id,
801-
folder_id=folder_id,
802-
project_id=project_id,
803-
annotation_status=annotation_status,
804-
project=project,
805-
folder_name=folder_name
806-
)
807-
808-
return (list_of_uploaded, list_of_not_uploaded, duplicate_images)
775+
return attach_file_urls_to_project(project, attachments, annotation_status)
809776

810777

811778
@Trackable
@@ -1993,3 +1960,79 @@ def clone_project(
19931960
metadata["description"] = project_description
19941961

19951962
return create_project_from_metadata(metadata)
1963+
1964+
1965+
1966+
@Trackable
1967+
def attach_video_urls_to_project(project, attachments, annotation_status="NotStarted"):
1968+
"""Link videos on external storage to SuperAnnotate.
1969+
1970+
:param project: project name or project folder path
1971+
:type project: str or dict
1972+
1973+
:param attachments: path to csv file on attachments metadata
1974+
:type attachments: Path-like (str or Path)
1975+
1976+
:param annotation_status: value to set the annotation statuses of the linked videos: NotStarted InProgress QualityCheck Returned Completed Skipped
1977+
:type annotation_status: str
1978+
1979+
:return: attached videos, failed videos, skipped videos
1980+
:rtype: (list, list, list)
1981+
"""
1982+
return attach_file_urls_to_project(project, attachments, annotation_status)
1983+
1984+
1985+
def attach_file_urls_to_project(project, attachments, annotation_status):
1986+
"""Link files on external storage to SuperAnnotate.
1987+
1988+
:param project: project name or project folder path
1989+
:type project: str or dict
1990+
1991+
:param attachments: path to csv file on attachments metadata
1992+
:type attachments: Path-like (str or Path)
1993+
1994+
:param annotation_status: value to set the annotation statuses of the linked files: NotStarted InProgress QualityCheck Returned Completed Skipped
1995+
:type annotation_status: str
1996+
1997+
:return: attached files, failed files, skipped files
1998+
:rtype: (list, list, list)
1999+
"""
2000+
project, folder = get_project_and_folder_metadata(project)
2001+
folder_name = project["name"] + (f'/{folder["name"]}' if folder else "")
2002+
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
2003+
if upload_state == "Basic":
2004+
raise SABaseException(
2005+
0,
2006+
"You cannot attach URLs in this type of project. Please attach it in an external storage project"
2007+
)
2008+
annotation_status = common.annotation_status_str_to_int(annotation_status)
2009+
team_id, project_id = project["team_id"], project["id"]
2010+
df = pd.read_csv(attachments, dtype=str)
2011+
df = df[~df["url"].isnull()]
2012+
2013+
if "name" in df.columns:
2014+
df.loc[df["name"].isnull(), "name"] = [
2015+
str(uuid.uuid4()) for _ in range(df["name"].isnull().sum())
2016+
]
2017+
else:
2018+
df["name"] = [str(uuid.uuid4()) for _ in range(len(df.index))]
2019+
2020+
df = pd.DataFrame(df, columns=["name", "url"])
2021+
df_names_urls = df.values.tolist()
2022+
2023+
if folder:
2024+
folder_id = folder["id"]
2025+
else:
2026+
folder_id = get_project_root_folder_id(project)
2027+
2028+
list_of_uploaded, list_of_not_uploaded, list_of_duplicated = _attach_urls(
2029+
file_urls=df_names_urls,
2030+
team_id=team_id,
2031+
folder_id=folder_id,
2032+
project_id=project_id,
2033+
annotation_status=annotation_status,
2034+
project=project,
2035+
folder_name=folder_name
2036+
)
2037+
2038+
return list_of_uploaded, list_of_not_uploaded, list_of_duplicated

0 commit comments

Comments
 (0)