@@ -298,16 +298,25 @@ def get_component_config(self, project: Union[NotEmptyStr, int], component_id: s
298298 def retrieve_context (
299299 component_data : List [dict ], component_pk : str
300300 ) -> Tuple [bool , typing .Any ]:
301- for component in component_data :
302- if (
303- component ["type" ] == "webComponent"
304- and component ["id" ] == component_pk
305- ):
306- return True , component .get ("context" )
307- if component ["type" ] == "group" and "children" in component :
308- found , val = retrieve_context (component ["children" ], component_pk )
309- if found :
310- return found , val
301+ try :
302+ for component in component_data :
303+ if (
304+ component ["type" ] == "webComponent"
305+ and component ["id" ] == component_pk
306+ ):
307+ return True , component .get ("context" )
308+ if (
309+ component ["type" ] in ("group" , "grid" )
310+ and "children" in component
311+ ):
312+ found , val = retrieve_context (
313+ component ["children" ], component_pk
314+ )
315+ if found :
316+ return found , val
317+ except KeyError as e :
318+ logger .debug ("Got key error:" , component_data )
319+ raise e
311320 return False , None
312321
313322 project = (
@@ -1968,33 +1977,70 @@ def download_image(
19681977 return response .data
19691978
19701979 def upload_annotations (
1971- self , project : NotEmptyStr , annotations : List [dict ], keep_status : bool = None
1980+ self ,
1981+ project : NotEmptyStr ,
1982+ annotations : List [dict ],
1983+ keep_status : bool = None ,
1984+ * ,
1985+ data_spec : Literal ["default" , "multimodal" ] = "default" ,
19721986 ):
1973- """Uploads a list of annotation dicts as annotations to the SuperAnnotate directory .
1987+ """Uploads a list of annotation dictionaries to the specified SuperAnnotate project or folder .
19741988
1975- :param project: project name or folder path (e.g., "project1/folder1")
1976- :type project: str or dict
1989+ :param project: The project name or folder path where annotations will be uploaded
1990+ (e.g., "project1/folder1").
1991+ :type project: str
19771992
1978- :param annotations: list of annotation dictionaries corresponding to SuperAnnotate format
1979- :type annotations: list of dicts
1993+ :param annotations: A list of annotation dictionaries formatted according to the SuperAnnotate standards.
1994+ :type annotations: list of dict
19801995
1981- :param keep_status: If False, the annotation status will be automatically
1982- updated to "InProgress," otherwise the current status will be kept.
1983- :type keep_status: bool
1996+ :param keep_status: If False, the annotation status will be automatically updated to "InProgress."
1997+ If True, the current status will remain unchanged.
1998+ :type keep_status: bool, optional
1999+
2000+ :param data_spec: Specifies the format for processing and transforming annotations before upload.
19842001
2002+ Options are:
2003+ - default: Retains the annotations in their original format.
2004+ - multimodal: Converts annotations for multimodal projects, optimizing for
2005+ compact and modality-specific data representation.
2006+ :type data_spec: str, optional
19852007
1986- :return: a dictionary containing lists of successfully uploaded, failed and skipped name
2008+ :return: A dictionary containing the results of the upload, categorized into successfully uploaded,
2009+ failed, and skipped annotations.
19872010 :rtype: dict
19882011
1989- Response Example:
1990- ::
2012+ Response Example::
19912013
19922014 {
19932015 "succeeded": [],
1994- "failed":[],
2016+ "failed": [],
19952017 "skipped": []
19962018 }
19972019
2020+ Example Usage with JSONL Upload for Multimodal Projects::
2021+
2022+ import json
2023+ from pathlib import Path
2024+ from superannotate import SAClient
2025+
2026+ annotations_path = Path("annotations.jsonl")
2027+ annotations = []
2028+
2029+ # Reading the JSONL file and converting it into a list of dictionaries
2030+ with annotations_path.open("r", encoding="utf-8") as f:
2031+ for line in f:
2032+ annotations.append(json.loads(line))
2033+
2034+ # Initialize the SuperAnnotate client
2035+ sa = SAClient()
2036+
2037+ # Call the upload_annotations function
2038+ response = sa.upload_annotations(
2039+ project="project1/folder1",
2040+ annotations=annotations,
2041+ keep_status=True,
2042+ data_spec='multimodal'
2043+ )
19982044 """
19992045 if keep_status is not None :
20002046 warnings .warn (
@@ -2010,6 +2056,7 @@ def upload_annotations(
20102056 annotations = annotations ,
20112057 keep_status = keep_status ,
20122058 user = self .controller .current_user ,
2059+ output_format = data_spec ,
20132060 )
20142061 if response .errors :
20152062 raise AppException (response .errors )
@@ -2484,6 +2531,8 @@ def get_annotations(
24842531 self ,
24852532 project : Union [NotEmptyStr , int ],
24862533 items : Optional [Union [List [NotEmptyStr ], List [int ]]] = None ,
2534+ * ,
2535+ data_spec : Literal ["default" , "multimodal" ] = "default" ,
24872536 ):
24882537 """Returns annotations for the given list of items.
24892538
@@ -2493,6 +2542,29 @@ def get_annotations(
24932542 :param items: item names. If None, all the items in the specified directory will be used.
24942543 :type items: list of strs or list of ints
24952544
2545+ :param data_spec: Specifies the format for processing and transforming annotations before upload.
2546+
2547+ Options are:
2548+ - default: Retains the annotations in their original format.
2549+ - multimodal: Converts annotations for multimodal projects, optimizing for
2550+ compact and modality-specific data representation.
2551+
2552+ :type data_spec: str, optional
2553+
2554+ Example Usage of Multimodal Projects::
2555+
2556+ from superannotate import SAClient
2557+
2558+
2559+ sa = SAClient()
2560+
2561+ # Call the upload_annotations function
2562+ response = sa.upload_annotations(
2563+ project="project1/folder1",
2564+ items=["item_1", "item_2"],
2565+ data_spec='multimodal'
2566+ )
2567+
24962568 :return: list of annotations
24972569 :rtype: list of dict
24982570 """
@@ -2503,7 +2575,12 @@ def get_annotations(
25032575 folder = self .controller .get_folder_by_id (
25042576 project_id = project .id , folder_id = project .folder_id
25052577 ).data
2506- response = self .controller .annotations .list (project , folder , items )
2578+ response = self .controller .annotations .list (
2579+ project ,
2580+ folder ,
2581+ items ,
2582+ transform_version = "llmJsonV2" if data_spec == "multimodal" else None ,
2583+ )
25072584 if response .errors :
25082585 raise AppException (response .errors )
25092586 return response .data
@@ -2826,7 +2903,7 @@ def list_items(
28262903 project : Union [NotEmptyStr , int ],
28272904 folder : Optional [Union [NotEmptyStr , int ]] = None ,
28282905 * ,
2829- include : List [Literal ["custom_metadata" ]] = None ,
2906+ include : List [Literal ["custom_metadata" , "category" ]] = None ,
28302907 ** filters ,
28312908 ):
28322909 """
@@ -2963,9 +3040,8 @@ def list_items(
29633040 for i in res :
29643041 i .custom_metadata = item_custom_fields [i .id ]
29653042 exclude = {"meta" , "annotator_email" , "qa_email" }
2966- if include :
2967- if "custom_metadata" not in include :
2968- exclude .add ("custom_metadata" )
3043+ if not include_custom_metadata :
3044+ exclude .add ("custom_metadata" )
29693045 return BaseSerializer .serialize_iterable (res , exclude = exclude )
29703046
29713047 def list_projects (
0 commit comments