|
12 | 12 | from typing import Union |
13 | 13 |
|
14 | 14 | import boto3 |
15 | | -from pydantic import StrictBool |
16 | | -from pydantic import conlist |
17 | | -from pydantic import parse_obj_as |
18 | | -from pydantic.error_wrappers import ValidationError |
19 | | -from tqdm import tqdm |
20 | | - |
21 | 15 | import lib.core as constances |
22 | 16 | from lib.app.annotation_helpers import add_annotation_bbox_to_json |
23 | 17 | from lib.app.annotation_helpers import add_annotation_comment_to_json |
|
56 | 50 | from lib.core.types import PriorityScore |
57 | 51 | from lib.core.types import Project |
58 | 52 | from lib.infrastructure.controller import Controller |
| 53 | +from pydantic import conlist |
| 54 | +from pydantic import parse_obj_as |
| 55 | +from pydantic import StrictBool |
| 56 | +from pydantic.error_wrappers import ValidationError |
59 | 57 | from superannotate.logger import get_default_logger |
| 58 | +from tqdm import tqdm |
60 | 59 |
|
61 | 60 | logger = get_default_logger() |
62 | 61 |
|
@@ -594,15 +593,20 @@ def copy_images( |
594 | 593 | :return: list of skipped image names |
595 | 594 | :rtype: list of strs |
596 | 595 | """ |
597 | | - |
| 596 | + warning_msg = ( |
| 597 | + "We're deprecating the copy_images function. Please use copy_items instead. Learn more. \n" |
| 598 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.copy_items" |
| 599 | + ) |
| 600 | + logger.warning(warning_msg) |
| 601 | + warnings.warn(warning_msg, DeprecationWarning) |
598 | 602 | project_name, source_folder_name = extract_project_folder(source_project) |
599 | 603 |
|
600 | 604 | to_project_name, destination_folder_name = extract_project_folder( |
601 | 605 | destination_project |
602 | 606 | ) |
603 | 607 | if project_name != to_project_name: |
604 | 608 | raise AppException( |
605 | | - "Source and destination projects should be the same for copy_images" |
| 609 | + "Source and destination projects should be the same" |
606 | 610 | ) |
607 | 611 | if not image_names: |
608 | 612 | images = ( |
@@ -658,6 +662,12 @@ def move_images( |
658 | 662 | :return: list of skipped image names |
659 | 663 | :rtype: list of strs |
660 | 664 | """ |
| 665 | + warning_msg = ( |
| 666 | + "We're deprecating the move_images function. Please use move_items instead. Learn more." |
| 667 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.move_items" |
| 668 | + ) |
| 669 | + logger.warning(warning_msg) |
| 670 | + warnings.warn(warning_msg, DeprecationWarning) |
661 | 671 | project_name, source_folder_name = extract_project_folder(source_project) |
662 | 672 |
|
663 | 673 | project = Controller.get_default().get_project_metadata(project_name).data |
@@ -1817,6 +1827,12 @@ def attach_image_urls_to_project( |
1817 | 1827 | :return: list of linked image names, list of failed image names, list of duplicate image names |
1818 | 1828 | :rtype: tuple |
1819 | 1829 | """ |
| 1830 | + warning_msg = ( |
| 1831 | + "We're deprecating the attach_image_urls_to_project function. Please use attach_items instead. Learn more." |
| 1832 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 1833 | + ) |
| 1834 | + logger.warning(warning_msg) |
| 1835 | + warnings.warn(warning_msg, DeprecationWarning) |
1820 | 1836 | project_name, folder_name = extract_project_folder(project) |
1821 | 1837 | project = Controller.get_default().get_project_metadata(project_name).data |
1822 | 1838 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -1884,6 +1900,12 @@ def attach_video_urls_to_project( |
1884 | 1900 | :return: attached videos, failed videos, skipped videos |
1885 | 1901 | :rtype: (list, list, list) |
1886 | 1902 | """ |
| 1903 | + warning_msg = ( |
| 1904 | + "We're deprecating the attach_video_urls_to_project function. Please use attach_items instead. Learn more." |
| 1905 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 1906 | + ) |
| 1907 | + logger.warning(warning_msg) |
| 1908 | + warnings.warn(warning_msg, DeprecationWarning) |
1887 | 1909 | project_name, folder_name = extract_project_folder(project) |
1888 | 1910 | project = Controller.get_default().get_project_metadata(project_name).data |
1889 | 1911 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -2744,6 +2766,12 @@ def attach_document_urls_to_project( |
2744 | 2766 | :return: list of attached documents, list of not attached documents, list of skipped documents |
2745 | 2767 | :rtype: tuple |
2746 | 2768 | """ |
| 2769 | + warning_msg = ( |
| 2770 | + "We're deprecating the attach_document_urls_to_project function. Please use attach_items instead. Learn more." |
| 2771 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 2772 | + ) |
| 2773 | + logger.warning(warning_msg) |
| 2774 | + warnings.warn(warning_msg, DeprecationWarning) |
2747 | 2775 | project_name, folder_name = extract_project_folder(project) |
2748 | 2776 | project = Controller.get_default().get_project_metadata(project_name).data |
2749 | 2777 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -3102,66 +3130,121 @@ def search_items( |
3102 | 3130 | @validate_arguments |
3103 | 3131 | def attach_items( |
3104 | 3132 | project: Union[NotEmptyStr, dict], |
3105 | | - attachments, |
3106 | | - annotation_status="NotStarted" |
| 3133 | + attachments: AttachmentArg, |
| 3134 | + annotation_status: Optional[AnnotationStatuses] = "NotStarted" |
3107 | 3135 | ): |
3108 | | - """Link items from external storage to SuperAnnotate using URLs. |
| 3136 | + attachments = attachments.data |
| 3137 | + project_name, folder_name = extract_project_folder(project) |
| 3138 | + if attachments and isinstance(attachments[0], AttachmentDict): |
| 3139 | + unique_attachments = set(attachments) |
| 3140 | + duplicate_attachments = [item for item, count in collections.Counter(attachments).items() if count > 1] |
| 3141 | + else: |
| 3142 | + unique_attachments, duplicate_attachments = get_name_url_duplicated_from_csv(attachments) |
| 3143 | + if duplicate_attachments: |
| 3144 | + logger.info("Dropping duplicates.") |
| 3145 | + unique_attachments = parse_obj_as(List[AttachmentEntity], unique_attachments) |
| 3146 | + uploaded, fails, duplicated = [], [], [] |
| 3147 | + if unique_attachments: |
| 3148 | + logger.info(f"Attaching {len(unique_attachments)} file(s) to project {project}.") |
| 3149 | + response = Controller.get_default().attach_items( |
| 3150 | + project_name=project_name, |
| 3151 | + folder_name=folder_name, |
| 3152 | + attachments=unique_attachments, |
| 3153 | + annotation_status=annotation_status, |
| 3154 | + ) |
| 3155 | + if response.errors: |
| 3156 | + raise AppException(response.errors) |
| 3157 | + uploaded, duplicated = response.data |
| 3158 | + uploaded = [i["name"] for i in uploaded] |
| 3159 | + fails = [ |
| 3160 | + attachment.name |
| 3161 | + for attachment in unique_attachments |
| 3162 | + if attachment.name not in uploaded and attachment.name not in duplicated |
| 3163 | + ] |
| 3164 | + return uploaded, fails, duplicated |
3109 | 3165 |
|
3110 | | - :param project: project name or folder path (e.g., “project1/folder1”) |
3111 | | - :type project: str |
3112 | 3166 |
|
3113 | | - :param attachments: path to CSV file or list of dicts containing attachments URLs. |
3114 | | - :type attachments: path-like (str or Path) or list of dicts |
| 3167 | +@Trackable |
| 3168 | +@validate_arguments |
| 3169 | +def copy_items( |
| 3170 | + source: Union[NotEmptyStr, dict], |
| 3171 | + destination: Union[NotEmptyStr, dict], |
| 3172 | + items: Optional[List[NotEmptyStr]] = None, |
| 3173 | + include_annotations: Optional[StrictBool] = True, |
| 3174 | +): |
| 3175 | + """Copy images in bulk between folders in a project |
3115 | 3176 |
|
3116 | | - :param annotation_status: value to set the annotation statuses of the |
3117 | | - linked items: |
3118 | | - “NotStarted” |
3119 | | - “InProgress” |
3120 | | - “QualityCheck” |
3121 | | - “Returned” |
3122 | | - “Completed” |
3123 | | - “Skipped” |
3124 | | - :type annotation_status: str |
| 3177 | + :param source: project name or folder path to select items from (e.g., “project1/folder1”). |
| 3178 | + :type source: str |
3125 | 3179 |
|
3126 | | - :return: list of attached item names, list of not attached item names, list of duplicate item names |
3127 | | - that are already in SuperAnnotate. |
3128 | | - :rtype: tuple |
| 3180 | + :param destination: project name (root) or folder path to place copied items. |
| 3181 | + :type destination: str |
| 3182 | +
|
| 3183 | + :param items: names of items to copy. If None, all items from the source directory will be copied. |
| 3184 | + :type itmes: list of str |
| 3185 | +
|
| 3186 | + :param include_annotations: enables annotations copy |
| 3187 | + :type include_annotations: bool |
| 3188 | +
|
| 3189 | + :return: list of skipped item names |
| 3190 | + :rtype: list of strs |
3129 | 3191 | """ |
3130 | | - project_name, folder_name = extract_project_folder(project) |
3131 | 3192 |
|
3132 | | - images_to_upload, duplicate_images = get_paths_and_duplicated_from_csv(attachments) |
| 3193 | + project_name, source_folder = extract_project_folder(source) |
3133 | 3194 |
|
3134 | | - attachments_data |
| 3195 | + to_project_name, destination_folder = extract_project_folder(destination) |
| 3196 | + if project_name != to_project_name: |
| 3197 | + raise AppException( |
| 3198 | + "Source and destination projects should be the same for copy_images" |
| 3199 | + ) |
3135 | 3200 |
|
3136 | | - use_case = Controller.get_default().attach_items( |
| 3201 | + response = Controller.get_default().copy_items( |
3137 | 3202 | project_name=project_name, |
3138 | | - folder_name=folder_name, |
3139 | | - files=ImageSerializer.deserialize(images_to_upload), # noqa: E203 |
3140 | | - annotation_status=annotation_status, |
| 3203 | + from_folder=source_folder, |
| 3204 | + to_folder=destination_folder, |
| 3205 | + items=items, |
| 3206 | + include_annotations=include_annotations, |
3141 | 3207 | ) |
3142 | | - if len(duplicate_images): |
3143 | | - logger.warning( |
3144 | | - constances.ALREADY_EXISTING_FILES_WARNING.format(len(duplicate_images)) |
3145 | | - ) |
| 3208 | + if response.errors: |
| 3209 | + raise AppException(response.errors) |
3146 | 3210 |
|
3147 | | - if use_case.is_valid(): |
3148 | | - logger.info( |
3149 | | - constances.ATTACHING_FILES_MESSAGE.format( |
3150 | | - len(images_to_upload), project |
3151 | | - ) |
| 3211 | + return response.data |
| 3212 | + |
| 3213 | + |
| 3214 | +@Trackable |
| 3215 | +@validate_arguments |
| 3216 | +def move_items( |
| 3217 | + source: Union[NotEmptyStr, dict], |
| 3218 | + destination: Union[NotEmptyStr, dict], |
| 3219 | + items: Optional[List[NotEmptyStr]] = None, |
| 3220 | +): |
| 3221 | + """Copy images in bulk between folders in a project |
| 3222 | +
|
| 3223 | + :param source: project name or folder path to pick items from (e.g., “project1/folder1”). |
| 3224 | + :type source: str |
| 3225 | +
|
| 3226 | + :param destination: project name (root) or folder path to move items to. |
| 3227 | + :type destination: str |
| 3228 | +
|
| 3229 | + :param items: names of items to move. If None, all items from the source directory will be moved. |
| 3230 | + :type items: list of str |
| 3231 | +
|
| 3232 | + :return: list of skipped item names |
| 3233 | + :rtype: list of strs |
| 3234 | + """ |
| 3235 | + |
| 3236 | + project_name, source_folder = extract_project_folder(source) |
| 3237 | + to_project_name, destination_folder = extract_project_folder(destination) |
| 3238 | + if project_name != to_project_name: |
| 3239 | + raise AppException( |
| 3240 | + "Source and destination projects should be the same" |
3152 | 3241 | ) |
3153 | | - with tqdm( |
3154 | | - total=use_case.attachments_count, desc="Attaching urls" |
3155 | | - ) as progress_bar: |
3156 | | - for attached in use_case.execute(): |
3157 | | - progress_bar.update(attached) |
3158 | | - uploaded, duplications = use_case.data |
3159 | | - uploaded = [i["name"] for i in uploaded] |
3160 | | - duplications.extend(duplicate_images) |
3161 | | - failed_images = [ |
3162 | | - image["name"] |
3163 | | - for image in images_to_upload |
3164 | | - if image["name"] not in uploaded + duplications |
3165 | | - ] |
3166 | | - return uploaded, failed_images, duplications |
3167 | | - raise AppException(use_case.response.errors) |
| 3242 | + response = Controller.get_default().move_items( |
| 3243 | + project_name=project_name, |
| 3244 | + from_folder=source_folder, |
| 3245 | + to_folder=destination_folder, |
| 3246 | + items=items, |
| 3247 | + ) |
| 3248 | + if response.errors: |
| 3249 | + raise AppException(response.errors) |
| 3250 | + return response.data |
0 commit comments