|
| 1 | +import collections |
1 | 2 | import io |
2 | 3 | import json |
3 | 4 | import os |
|
17 | 18 | from lib.app.annotation_helpers import add_annotation_point_to_json |
18 | 19 | from lib.app.helpers import extract_project_folder |
19 | 20 | from lib.app.helpers import get_annotation_paths |
| 21 | +from lib.app.helpers import get_name_url_duplicated_from_csv |
20 | 22 | from lib.app.helpers import get_paths_and_duplicated_from_csv |
21 | 23 | from lib.app.interface.types import AnnotationStatuses |
22 | 24 | from lib.app.interface.types import AnnotationType |
23 | 25 | from lib.app.interface.types import AnnotatorRole |
| 26 | +from lib.app.interface.types import AttachmentArg |
| 27 | +from lib.app.interface.types import AttachmentDict |
24 | 28 | from lib.app.interface.types import ClassType |
25 | 29 | from lib.app.interface.types import EmailStr |
26 | 30 | from lib.app.interface.types import ImageQualityChoices |
|
36 | 40 | from lib.app.serializers import SettingsSerializer |
37 | 41 | from lib.app.serializers import TeamSerializer |
38 | 42 | from lib.core import LIMITED_FUNCTIONS |
| 43 | +from lib.core.entities import AttachmentEntity |
39 | 44 | from lib.core.entities.integrations import IntegrationEntity |
40 | 45 | from lib.core.entities.project_entities import AnnotationClassEntity |
41 | 46 | from lib.core.enums import ImageQuality |
@@ -588,15 +593,20 @@ def copy_images( |
588 | 593 | :return: list of skipped image names |
589 | 594 | :rtype: list of strs |
590 | 595 | """ |
591 | | - |
| 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) |
592 | 602 | project_name, source_folder_name = extract_project_folder(source_project) |
593 | 603 |
|
594 | 604 | to_project_name, destination_folder_name = extract_project_folder( |
595 | 605 | destination_project |
596 | 606 | ) |
597 | 607 | if project_name != to_project_name: |
598 | 608 | raise AppException( |
599 | | - "Source and destination projects should be the same for copy_images" |
| 609 | + "Source and destination projects should be the same" |
600 | 610 | ) |
601 | 611 | if not image_names: |
602 | 612 | images = ( |
@@ -652,6 +662,12 @@ def move_images( |
652 | 662 | :return: list of skipped image names |
653 | 663 | :rtype: list of strs |
654 | 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) |
655 | 671 | project_name, source_folder_name = extract_project_folder(source_project) |
656 | 672 |
|
657 | 673 | project = Controller.get_default().get_project_metadata(project_name).data |
@@ -1811,6 +1827,12 @@ def attach_image_urls_to_project( |
1811 | 1827 | :return: list of linked image names, list of failed image names, list of duplicate image names |
1812 | 1828 | :rtype: tuple |
1813 | 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) |
1814 | 1836 | project_name, folder_name = extract_project_folder(project) |
1815 | 1837 | project = Controller.get_default().get_project_metadata(project_name).data |
1816 | 1838 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -1878,6 +1900,12 @@ def attach_video_urls_to_project( |
1878 | 1900 | :return: attached videos, failed videos, skipped videos |
1879 | 1901 | :rtype: (list, list, list) |
1880 | 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) |
1881 | 1909 | project_name, folder_name = extract_project_folder(project) |
1882 | 1910 | project = Controller.get_default().get_project_metadata(project_name).data |
1883 | 1911 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -2480,8 +2508,10 @@ def search_images_all_folders( |
2480 | 2508 |
|
2481 | 2509 | :param project: project name |
2482 | 2510 | :type project: str |
| 2511 | +
|
2483 | 2512 | :param image_name_prefix: image name prefix for search |
2484 | 2513 | :type image_name_prefix: str |
| 2514 | +
|
2485 | 2515 | :param annotation_status: if not None, annotation statuses of images to filter, |
2486 | 2516 | should be one of NotStarted InProgress QualityCheck Returned Completed Skipped |
2487 | 2517 | :type annotation_status: str |
@@ -2741,6 +2771,12 @@ def attach_document_urls_to_project( |
2741 | 2771 | :return: list of attached documents, list of not attached documents, list of skipped documents |
2742 | 2772 | :rtype: tuple |
2743 | 2773 | """ |
| 2774 | + warning_msg = ( |
| 2775 | + "We're deprecating the attach_document_urls_to_project function. Please use attach_items instead. Learn more." |
| 2776 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 2777 | + ) |
| 2778 | + logger.warning(warning_msg) |
| 2779 | + warnings.warn(warning_msg, DeprecationWarning) |
2744 | 2780 | project_name, folder_name = extract_project_folder(project) |
2745 | 2781 | project = Controller.get_default().get_project_metadata(project_name).data |
2746 | 2782 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -3093,3 +3129,127 @@ def search_items( |
3093 | 3129 | if response.errors: |
3094 | 3130 | raise AppException(response.errors) |
3095 | 3131 | return BaseSerializer.serialize_iterable(response.data) |
| 3132 | + |
| 3133 | + |
| 3134 | +@Trackable |
| 3135 | +@validate_arguments |
| 3136 | +def attach_items( |
| 3137 | + project: Union[NotEmptyStr, dict], |
| 3138 | + attachments: AttachmentArg, |
| 3139 | + annotation_status: Optional[AnnotationStatuses] = "NotStarted" |
| 3140 | +): |
| 3141 | + attachments = attachments.data |
| 3142 | + project_name, folder_name = extract_project_folder(project) |
| 3143 | + if attachments and isinstance(attachments[0], AttachmentDict): |
| 3144 | + unique_attachments = set(attachments) |
| 3145 | + duplicate_attachments = [item for item, count in collections.Counter(attachments).items() if count > 1] |
| 3146 | + else: |
| 3147 | + unique_attachments, duplicate_attachments = get_name_url_duplicated_from_csv(attachments) |
| 3148 | + if duplicate_attachments: |
| 3149 | + logger.info("Dropping duplicates.") |
| 3150 | + unique_attachments = parse_obj_as(List[AttachmentEntity], unique_attachments) |
| 3151 | + uploaded, fails, duplicated = [], [], [] |
| 3152 | + if unique_attachments: |
| 3153 | + logger.info(f"Attaching {len(unique_attachments)} file(s) to project {project}.") |
| 3154 | + response = Controller.get_default().attach_items( |
| 3155 | + project_name=project_name, |
| 3156 | + folder_name=folder_name, |
| 3157 | + attachments=unique_attachments, |
| 3158 | + annotation_status=annotation_status, |
| 3159 | + ) |
| 3160 | + if response.errors: |
| 3161 | + raise AppException(response.errors) |
| 3162 | + uploaded, duplicated = response.data |
| 3163 | + uploaded = [i["name"] for i in uploaded] |
| 3164 | + fails = [ |
| 3165 | + attachment.name |
| 3166 | + for attachment in unique_attachments |
| 3167 | + if attachment.name not in uploaded and attachment.name not in duplicated |
| 3168 | + ] |
| 3169 | + return uploaded, fails, duplicated |
| 3170 | + |
| 3171 | + |
| 3172 | +@Trackable |
| 3173 | +@validate_arguments |
| 3174 | +def copy_items( |
| 3175 | + source: Union[NotEmptyStr, dict], |
| 3176 | + destination: Union[NotEmptyStr, dict], |
| 3177 | + items: Optional[List[NotEmptyStr]] = None, |
| 3178 | + include_annotations: Optional[StrictBool] = True, |
| 3179 | +): |
| 3180 | + """Copy images in bulk between folders in a project |
| 3181 | +
|
| 3182 | + :param source: project name or folder path to select items from (e.g., “project1/folder1”). |
| 3183 | + :type source: str |
| 3184 | +
|
| 3185 | + :param destination: project name (root) or folder path to place copied items. |
| 3186 | + :type destination: str |
| 3187 | +
|
| 3188 | + :param items: names of items to copy. If None, all items from the source directory will be copied. |
| 3189 | + :type itmes: list of str |
| 3190 | +
|
| 3191 | + :param include_annotations: enables annotations copy |
| 3192 | + :type include_annotations: bool |
| 3193 | +
|
| 3194 | + :return: list of skipped item names |
| 3195 | + :rtype: list of strs |
| 3196 | + """ |
| 3197 | + |
| 3198 | + project_name, source_folder = extract_project_folder(source) |
| 3199 | + |
| 3200 | + to_project_name, destination_folder = extract_project_folder(destination) |
| 3201 | + if project_name != to_project_name: |
| 3202 | + raise AppException( |
| 3203 | + "Source and destination projects should be the same for copy_images" |
| 3204 | + ) |
| 3205 | + |
| 3206 | + response = Controller.get_default().copy_items( |
| 3207 | + project_name=project_name, |
| 3208 | + from_folder=source_folder, |
| 3209 | + to_folder=destination_folder, |
| 3210 | + items=items, |
| 3211 | + include_annotations=include_annotations, |
| 3212 | + ) |
| 3213 | + if response.errors: |
| 3214 | + raise AppException(response.errors) |
| 3215 | + |
| 3216 | + return response.data |
| 3217 | + |
| 3218 | + |
| 3219 | +@Trackable |
| 3220 | +@validate_arguments |
| 3221 | +def move_items( |
| 3222 | + source: Union[NotEmptyStr, dict], |
| 3223 | + destination: Union[NotEmptyStr, dict], |
| 3224 | + items: Optional[List[NotEmptyStr]] = None, |
| 3225 | +): |
| 3226 | + """Copy images in bulk between folders in a project |
| 3227 | +
|
| 3228 | + :param source: project name or folder path to pick items from (e.g., “project1/folder1”). |
| 3229 | + :type source: str |
| 3230 | +
|
| 3231 | + :param destination: project name (root) or folder path to move items to. |
| 3232 | + :type destination: str |
| 3233 | +
|
| 3234 | + :param items: names of items to move. If None, all items from the source directory will be moved. |
| 3235 | + :type items: list of str |
| 3236 | +
|
| 3237 | + :return: list of skipped item names |
| 3238 | + :rtype: list of strs |
| 3239 | + """ |
| 3240 | + |
| 3241 | + project_name, source_folder = extract_project_folder(source) |
| 3242 | + to_project_name, destination_folder = extract_project_folder(destination) |
| 3243 | + if project_name != to_project_name: |
| 3244 | + raise AppException( |
| 3245 | + "Source and destination projects should be the same" |
| 3246 | + ) |
| 3247 | + response = Controller.get_default().move_items( |
| 3248 | + project_name=project_name, |
| 3249 | + from_folder=source_folder, |
| 3250 | + to_folder=destination_folder, |
| 3251 | + items=items, |
| 3252 | + ) |
| 3253 | + if response.errors: |
| 3254 | + raise AppException(response.errors) |
| 3255 | + return response.data |
0 commit comments