|
| 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 |
@@ -297,6 +302,7 @@ def search_images( |
297 | 302 | "We're deprecating the search_images function. Please use search_items instead. Learn more." |
298 | 303 | "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.search_items" |
299 | 304 | ) |
| 305 | + logger.warning(warning_msg) |
300 | 306 | warnings.warn(warning_msg, DeprecationWarning) |
301 | 307 | project_name, folder_name = extract_project_folder(project) |
302 | 308 | project = Controller.get_default()._get_project(project_name) |
@@ -1810,6 +1816,12 @@ def attach_image_urls_to_project( |
1810 | 1816 | :return: list of linked image names, list of failed image names, list of duplicate image names |
1811 | 1817 | :rtype: tuple |
1812 | 1818 | """ |
| 1819 | + warning_msg = ( |
| 1820 | + "We're deprecating the attach_image_urls_to_project function. Please use attach_items instead. Learn more." |
| 1821 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 1822 | + ) |
| 1823 | + logger.warning(warning_msg) |
| 1824 | + warnings.warn(warning_msg, DeprecationWarning) |
1813 | 1825 | project_name, folder_name = extract_project_folder(project) |
1814 | 1826 | project = Controller.get_default().get_project_metadata(project_name).data |
1815 | 1827 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -1877,6 +1889,12 @@ def attach_video_urls_to_project( |
1877 | 1889 | :return: attached videos, failed videos, skipped videos |
1878 | 1890 | :rtype: (list, list, list) |
1879 | 1891 | """ |
| 1892 | + warning_msg = ( |
| 1893 | + "We're deprecating the attach_video_urls_to_project function. Please use attach_items instead. Learn more." |
| 1894 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 1895 | + ) |
| 1896 | + logger.warning(warning_msg) |
| 1897 | + warnings.warn(warning_msg, DeprecationWarning) |
1880 | 1898 | project_name, folder_name = extract_project_folder(project) |
1881 | 1899 | project = Controller.get_default().get_project_metadata(project_name).data |
1882 | 1900 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -2479,8 +2497,10 @@ def search_images_all_folders( |
2479 | 2497 |
|
2480 | 2498 | :param project: project name |
2481 | 2499 | :type project: str |
| 2500 | +
|
2482 | 2501 | :param image_name_prefix: image name prefix for search |
2483 | 2502 | :type image_name_prefix: str |
| 2503 | +
|
2484 | 2504 | :param annotation_status: if not None, annotation statuses of images to filter, |
2485 | 2505 | should be one of NotStarted InProgress QualityCheck Returned Completed Skipped |
2486 | 2506 | :type annotation_status: str |
@@ -2735,6 +2755,12 @@ def attach_document_urls_to_project( |
2735 | 2755 | :return: list of attached documents, list of not attached documents, list of skipped documents |
2736 | 2756 | :rtype: tuple |
2737 | 2757 | """ |
| 2758 | + warning_msg = ( |
| 2759 | + "We're deprecating the attach_document_urls_to_project function. Please use attach_items instead. Learn more." |
| 2760 | + "https://superannotate.readthedocs.io/en/stable/superannotate.sdk.html#superannotate.attach_items" |
| 2761 | + ) |
| 2762 | + logger.warning(warning_msg) |
| 2763 | + warnings.warn(warning_msg, DeprecationWarning) |
2738 | 2764 | project_name, folder_name = extract_project_folder(project) |
2739 | 2765 | project = Controller.get_default().get_project_metadata(project_name).data |
2740 | 2766 | project_folder_name = project_name + (f"/{folder_name}" if folder_name else "") |
@@ -3087,3 +3113,64 @@ def search_items( |
3087 | 3113 | if response.errors: |
3088 | 3114 | raise AppException(response.errors) |
3089 | 3115 | return BaseSerializer.serialize_iterable(response.data) |
| 3116 | + |
| 3117 | + |
| 3118 | +@Trackable |
| 3119 | +@validate_arguments |
| 3120 | +def attach_items( |
| 3121 | + project: Union[NotEmptyStr, dict], |
| 3122 | + attachments: AttachmentArg, |
| 3123 | + annotation_status: Optional[AnnotationStatuses] = "NotStarted" |
| 3124 | +): |
| 3125 | + """Link items from external storage to SuperAnnotate using URLs. |
| 3126 | +
|
| 3127 | + :param project: project name or folder path (e.g., “project1/folder1”) |
| 3128 | + :type project: str |
| 3129 | +
|
| 3130 | + :param attachments: path to CSV file or list of dicts containing attachments URLs. |
| 3131 | + :type attachments: path-like (str or Path) or list of dicts |
| 3132 | +
|
| 3133 | + :param annotation_status: value to set the annotation statuses of the |
| 3134 | + linked items: |
| 3135 | + “NotStarted” |
| 3136 | + “InProgress” |
| 3137 | + “QualityCheck” |
| 3138 | + “Returned” |
| 3139 | + “Completed” |
| 3140 | + “Skipped” |
| 3141 | + :type annotation_status: str |
| 3142 | +
|
| 3143 | + :return: list of attached item names, list of not attached item names, list of duplicate item names |
| 3144 | + that are already in SuperAnnotate. |
| 3145 | + :rtype: tuple |
| 3146 | + """ |
| 3147 | + attachments = attachments.__root__ |
| 3148 | + project_name, folder_name = extract_project_folder(project) |
| 3149 | + if attachments and isinstance(attachments[0], AttachmentDict): |
| 3150 | + unique_attachments = set(attachments) |
| 3151 | + duplicate_attachments = [item for item, count in collections.Counter(attachments).items() if count > 1] |
| 3152 | + else: |
| 3153 | + unique_attachments, duplicate_attachments = get_name_url_duplicated_from_csv(attachments) |
| 3154 | + |
| 3155 | + if duplicate_attachments: |
| 3156 | + logger.info("Dropping duplicates.") |
| 3157 | + unique_attachments = parse_obj_as(List[AttachmentEntity], unique_attachments) |
| 3158 | + if unique_attachments: |
| 3159 | + logger.info(f"Attaching {len(unique_attachments)} file(s) to project {project}.") |
| 3160 | + response = Controller.get_default().attach_items( |
| 3161 | + project_name=project_name, |
| 3162 | + folder_name=folder_name, |
| 3163 | + attachments=unique_attachments, |
| 3164 | + annotation_status=annotation_status, |
| 3165 | + ) |
| 3166 | + if response.errors: |
| 3167 | + raise AppException(response.errors) |
| 3168 | + |
| 3169 | + uploaded, duplicated = response.data |
| 3170 | + uploaded = [i["name"] for i in uploaded] |
| 3171 | + fails = [ |
| 3172 | + attachment.name |
| 3173 | + for attachment in unique_attachments |
| 3174 | + if attachment.name not in uploaded and attachment.name not in duplicated |
| 3175 | + ] |
| 3176 | + return uploaded, fails, duplicated |
0 commit comments