diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 5681df929..06ece19b0 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -84,6 +84,7 @@ ActionModeType, StreamType, EntityListAttributeDefinitionDict, + AdvancedFilterDict, ) from ._api_helpers.links import CreateLinkData @@ -4218,6 +4219,7 @@ def get_folders( tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, has_links: Optional[bool] = None, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False, ) -> Generator[FolderDict, None, None]: @@ -4259,6 +4261,7 @@ def get_folders( Both are returned if is set to None. has_links (Optional[Literal[IN, OUT, ANY]]): Filter representations with IN/OUT/ANY links. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -4286,6 +4289,7 @@ def get_folders( tags=tags, active=active, has_links=has_links, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -4571,6 +4575,7 @@ def get_tasks( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False, ) -> Generator[TaskDict, None, None]: @@ -4595,6 +4600,7 @@ def get_tasks( filtering. active (Optional[bool]): Filter active/inactive tasks. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -4617,6 +4623,7 @@ def get_tasks( statuses=statuses, tags=tags, active=active, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -4695,6 +4702,7 @@ def get_tasks_by_folder_paths( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False, ) -> dict[str, list[TaskDict]]: @@ -4717,6 +4725,7 @@ def get_tasks_by_folder_paths( filtering. active (Optional[bool]): Filter active/inactive tasks. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -4739,6 +4748,7 @@ def get_tasks_by_folder_paths( statuses=statuses, tags=tags, active=active, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -4988,6 +4998,7 @@ def get_products( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER, ) -> Generator[ProductDict, None, None]: @@ -5019,6 +5030,7 @@ def get_products( for filtering. active (Optional[bool]): Filter active/inactive products. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -5043,6 +5055,7 @@ def get_products( statuses=statuses, tags=tags, active=active, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -5325,6 +5338,7 @@ def get_versions( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER, ) -> Generator[VersionDict, None, None]: @@ -5351,6 +5365,7 @@ def get_versions( for filtering. active (Optional[bool]): Receive active/inactive entities. Both are returned when 'None' is passed. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for version. All possible folder fields are returned if 'None' is passed. @@ -5374,6 +5389,7 @@ def get_versions( statuses=statuses, tags=tags, active=active, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -5819,6 +5835,7 @@ def get_representations( tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, has_links: Optional[str] = None, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER, ) -> Generator[RepresentationDict, None, None]: @@ -5849,6 +5866,7 @@ def get_representations( Both are returned when 'None' is passed. has_links (Optional[Literal[IN, OUT, ANY]]): Filter representations with IN/OUT/ANY links. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for representation. All possible fields are returned if 'None' is passed. @@ -5871,6 +5889,7 @@ def get_representations( tags=tags, active=active, has_links=has_links, + filters=filters, fields=fields, own_attributes=own_attributes, ) @@ -7282,7 +7301,15 @@ def get_entity_lists( active: Optional[bool] = None, fields: Optional[Iterable[str]] = None, ) -> Generator[dict[str, Any], None, None]: - """Fetch entity lists from server. + """Fetch entity lists from AYON server. + + Warnings: + You can't get list items for lists with different 'entityType' in + one call. + + Notes: + To get list items, you have to pass 'items' field or + 'items.{sub-fields you want}' to 'fields' argument. Args: project_name (str): Project name where entity lists are. diff --git a/ayon_api/_api_helpers/base.py b/ayon_api/_api_helpers/base.py index bf209a0a1..0a3d19550 100644 --- a/ayon_api/_api_helpers/base.py +++ b/ayon_api/_api_helpers/base.py @@ -2,7 +2,7 @@ import logging import typing -from typing import Optional, Any, Iterable +from typing import Optional, Any, Iterable, Union import requests @@ -142,6 +142,11 @@ def _prepare_fields( ): raise NotImplementedError() + def _prepare_advanced_filters( + self, filters: Union[str, dict[str, Any], None] + ) -> Optional[str]: + raise NotImplementedError() + def _convert_entity_data(self, entity: AnyEntityDict): raise NotImplementedError() diff --git a/ayon_api/_api_helpers/folders.py b/ayon_api/_api_helpers/folders.py index fbef4e485..71535bdd5 100644 --- a/ayon_api/_api_helpers/folders.py +++ b/ayon_api/_api_helpers/folders.py @@ -21,6 +21,7 @@ FolderDict, FlatFolderDict, ProjectHierarchyDict, + AdvancedFilterDict, ) @@ -216,6 +217,7 @@ def get_folders( tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, has_links: Optional[bool] = None, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False ) -> Generator[FolderDict, None, None]: @@ -257,6 +259,7 @@ def get_folders( Both are returned if is set to None. has_links (Optional[Literal[IN, OUT, ANY]]): Filter representations with IN/OUT/ANY links. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -270,11 +273,11 @@ def get_folders( if not project_name: return - filters = { + graphql_filters = { "projectName": project_name } if not prepare_list_filters( - filters, + graphql_filters, ("folderIds", folder_ids), ("folderPaths", folder_paths), ("folderNames", folder_names), @@ -291,9 +294,10 @@ def get_folders( ("folderHasTasks", has_tasks), ("folderHasLinks", has_links), ("folderHasChildren", has_children), + ("filter", self._prepare_advanced_filters(filters)), ): if filter_value is not None: - filters[filter_key] = filter_value + graphql_filters[filter_key] = filter_value if parent_ids is not None: parent_ids = set(parent_ids) @@ -313,7 +317,7 @@ def get_folders( parent_ids.remove(project_name) parent_ids.add("root") - filters["parentFolderIds"] = list(parent_ids) + graphql_filters["parentFolderIds"] = list(parent_ids) if not fields: fields = self.get_default_fields_for_type("folder") @@ -328,7 +332,7 @@ def get_folders( fields.add("ownAttrib") query = folders_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) for parsed_data in query.continuous_query(self): diff --git a/ayon_api/_api_helpers/products.py b/ayon_api/_api_helpers/products.py index 353d36005..db1e29a0c 100644 --- a/ayon_api/_api_helpers/products.py +++ b/ayon_api/_api_helpers/products.py @@ -18,7 +18,11 @@ from .base import BaseServerAPI, _PLACEHOLDER if typing.TYPE_CHECKING: - from ayon_api.typing import ProductDict, ProductTypeDict + from ayon_api.typing import ( + ProductDict, + ProductTypeDict, + AdvancedFilterDict, + ) class ProductsAPI(BaseServerAPI): @@ -43,6 +47,7 @@ def get_products( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER ) -> Generator[ProductDict, None, None]: @@ -74,6 +79,7 @@ def get_products( for filtering. active (Optional[bool]): Filter active/inactive products. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -145,18 +151,18 @@ def get_products( fields.add("folderId") # Prepare filters for query - filters = { + graphql_filters = { "projectName": project_name } if filter_folder_ids: - filters["folderIds"] = list(filter_folder_ids) + graphql_filters["folderIds"] = list(filter_folder_ids) if filter_product_names: - filters["productNames"] = list(filter_product_names) + graphql_filters["productNames"] = list(filter_product_names) if not prepare_list_filters( - filters, + graphql_filters, ("productIds", product_ids), ("productTypes", product_types), ("productBaseTypes", product_base_types), @@ -168,12 +174,13 @@ def get_products( for filter_key, filter_value in ( ("productNameRegex", product_name_regex), ("productPathRegex", product_path_regex), + ("filter", self._prepare_advanced_filters(filters)), ): if filter_value: - filters[filter_key] = filter_value + graphql_filters[filter_key] = filter_value query = products_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) parsed_data = query.query(self) diff --git a/ayon_api/_api_helpers/representations.py b/ayon_api/_api_helpers/representations.py index 2eb9814cb..56ac8e372 100644 --- a/ayon_api/_api_helpers/representations.py +++ b/ayon_api/_api_helpers/representations.py @@ -20,7 +20,7 @@ from .base import BaseServerAPI, _PLACEHOLDER if typing.TYPE_CHECKING: - from ayon_api.typing import RepresentationDict + from ayon_api.typing import RepresentationDict, AdvancedFilterDict class RepresentationsAPI(BaseServerAPI): @@ -42,6 +42,7 @@ def get_representations( tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, has_links: Optional[str] = None, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER, ) -> Generator[RepresentationDict, None, None]: @@ -72,6 +73,7 @@ def get_representations( Both are returned when 'None' is passed. has_links (Optional[Literal[IN, OUT, ANY]]): Filter representations with IN/OUT/ANY links. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for representation. All possible fields are returned if 'None' is passed. @@ -106,7 +108,7 @@ def get_representations( fields.discard("files") fields |= REPRESENTATION_FILES_FIELDS - filters = { + graphql_filters = { "projectName": project_name } @@ -114,7 +116,7 @@ def get_representations( representation_ids = set(representation_ids) if not representation_ids: return - filters["representationIds"] = list(representation_ids) + graphql_filters["representationIds"] = list(representation_ids) version_ids_filter = None representation_names_filter = None @@ -140,29 +142,35 @@ def get_representations( return if version_ids_filter: - filters["versionIds"] = list(version_ids_filter) + graphql_filters["versionIds"] = list(version_ids_filter) if representation_names_filter: - filters["representationNames"] = list(representation_names_filter) + graphql_filters["representationNames"] = list( + representation_names_filter + ) if statuses is not None: statuses = set(statuses) if not statuses: return - filters["representationStatuses"] = list(statuses) + graphql_filters["representationStatuses"] = list(statuses) if tags is not None: tags = set(tags) if not tags: return - filters["representationTags"] = list(tags) + graphql_filters["representationTags"] = list(tags) if has_links is not None: - filters["representationHasLinks"] = has_links.upper() + graphql_filters["representationHasLinks"] = has_links.upper() + + filters = self._prepare_advanced_filters(filters) + if filters: + graphql_filters["filter"] = filters query = representations_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) for parsed_data in query.continuous_query(self): diff --git a/ayon_api/_api_helpers/tasks.py b/ayon_api/_api_helpers/tasks.py index aa984032b..4109d72ed 100644 --- a/ayon_api/_api_helpers/tasks.py +++ b/ayon_api/_api_helpers/tasks.py @@ -17,7 +17,7 @@ from .base import BaseServerAPI if typing.TYPE_CHECKING: - from ayon_api.typing import TaskDict + from ayon_api.typing import TaskDict, AdvancedFilterDict class TasksAPI(BaseServerAPI): @@ -38,6 +38,7 @@ def get_tasks( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False ) -> Generator[TaskDict, None, None]: @@ -62,6 +63,7 @@ def get_tasks( filtering. active (Optional[bool]): Filter active/inactive tasks. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -75,11 +77,11 @@ def get_tasks( if not project_name: return - filters = { + graphql_filters = { "projectName": project_name } if not prepare_list_filters( - filters, + graphql_filters, ("taskIds", task_ids), ("taskNames", task_names), ("taskTypes", task_types), @@ -91,6 +93,10 @@ def get_tasks( ): return + filters = self._prepare_advanced_filters(filters) + if filters: + graphql_filters["filter"] = filters + if not fields: fields = self.get_default_fields_for_type("task") else: @@ -101,7 +107,7 @@ def get_tasks( fields.add("active") query = tasks_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) for parsed_data in query.continuous_query(self): @@ -193,6 +199,7 @@ def get_tasks_by_folder_paths( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes: bool = False ) -> dict[str, list[TaskDict]]: @@ -215,6 +222,7 @@ def get_tasks_by_folder_paths( filtering. active (Optional[bool]): Filter active/inactive tasks. Both are returned if is set to None. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for folder. All possible folder fields are returned if 'None' is passed. @@ -230,12 +238,13 @@ def get_tasks_by_folder_paths( if not project_name or not folder_paths: return {} - filters = { + graphql_filters = { "projectName": project_name, "folderPaths": list(folder_paths), } + if not prepare_list_filters( - filters, + graphql_filters, ("taskNames", task_names), ("taskTypes", task_types), ("taskAssigneesAny", assignees), @@ -245,6 +254,10 @@ def get_tasks_by_folder_paths( ): return {} + filters = self._prepare_advanced_filters(filters) + if filters: + graphql_filters["filter"] = filters + if not fields: fields = self.get_default_fields_for_type("task") else: @@ -255,7 +268,7 @@ def get_tasks_by_folder_paths( fields.add("active") query = tasks_by_folder_paths_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) output = { diff --git a/ayon_api/_api_helpers/versions.py b/ayon_api/_api_helpers/versions.py index fe8a02469..97800451c 100644 --- a/ayon_api/_api_helpers/versions.py +++ b/ayon_api/_api_helpers/versions.py @@ -15,7 +15,7 @@ from .base import BaseServerAPI, _PLACEHOLDER if typing.TYPE_CHECKING: - from ayon_api.typing import VersionDict + from ayon_api.typing import VersionDict, AdvancedFilterDict class VersionsAPI(BaseServerAPI): @@ -37,6 +37,7 @@ def get_versions( statuses: Optional[Iterable[str]] = None, tags: Optional[Iterable[str]] = None, active: Optional[bool] = True, + filters: Optional[AdvancedFilterDict] = None, fields: Optional[Iterable[str]] = None, own_attributes=_PLACEHOLDER ) -> Generator[VersionDict, None, None]: @@ -63,6 +64,7 @@ def get_versions( for filtering. active (Optional[bool]): Receive active/inactive entities. Both are returned when 'None' is passed. + filters (Optional[AdvancedFilterDict]): Advanced filtering options. fields (Optional[Iterable[str]]): Fields to be queried for version. All possible folder fields are returned if 'None' is passed. @@ -98,11 +100,12 @@ def get_versions( if not hero and not standard: return - filters = { + graphql_filters = { "projectName": project_name } + if not prepare_list_filters( - filters, + graphql_filters, ("taskIds", task_ids), ("versionIds", version_ids), ("productIds", product_ids), @@ -113,6 +116,11 @@ def get_versions( ): return + + filters = self._prepare_advanced_filters(filters) + if filters: + graphql_filters["filter"] = filters + queries = [] # Add filters based on 'hero' and 'standard' # NOTE: There is not a filter to "ignore" hero versions or to get @@ -123,14 +131,14 @@ def get_versions( # This query all versions standard + hero # - hero must be filtered out if is not enabled during loop query = versions_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): query.set_variable_value(attr, filter_value) queries.append(query) else: if hero: # Add hero query if hero is enabled hero_query = versions_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): hero_query.set_variable_value(attr, filter_value) hero_query.set_variable_value("heroOnly", True) @@ -138,7 +146,7 @@ def get_versions( if standard: standard_query = versions_graphql_query(fields) - for attr, filter_value in filters.items(): + for attr, filter_value in graphql_filters.items(): standard_query.set_variable_value(attr, filter_value) if latest: diff --git a/ayon_api/graphql_queries.py b/ayon_api/graphql_queries.py index 815cc9a77..df0a1e69e 100644 --- a/ayon_api/graphql_queries.py +++ b/ayon_api/graphql_queries.py @@ -139,6 +139,7 @@ def folders_graphql_query(fields): "folderAssigneesAll", "[String!]" ) tags_var = query.add_variable("folderTags", "[String!]") + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -157,6 +158,7 @@ def folders_graphql_query(fields): folders_field.set_filter("hasTasks", has_tasks_var) folders_field.set_filter("hasLinks", has_links_var) folders_field.set_filter("hasChildren", has_children_var) + folders_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(fields) add_links_fields(folders_field, nested_fields) @@ -188,6 +190,7 @@ def tasks_graphql_query(fields): assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]") statuses_var = query.add_variable("taskStatuses", "[String!]") tags_var = query.add_variable("taskTags", "[String!]") + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -203,6 +206,7 @@ def tasks_graphql_query(fields): tasks_field.set_filter("assignees", assignees_all_var) tasks_field.set_filter("statuses", statuses_var) tasks_field.set_filter("tags", tags_var) + tasks_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(fields) add_links_fields(tasks_field, nested_fields) @@ -233,6 +237,7 @@ def tasks_by_folder_paths_graphql_query(fields): assignees_all_var = query.add_variable("taskAssigneesAll", "[String!]") statuses_var = query.add_variable("taskStatuses", "[String!]") tags_var = query.add_variable("taskTags", "[String!]") + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -250,6 +255,7 @@ def tasks_by_folder_paths_graphql_query(fields): tasks_field.set_filter("assignees", assignees_all_var) tasks_field.set_filter("statuses", statuses_var) tasks_field.set_filter("tags", tags_var) + tasks_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(fields) add_links_fields(tasks_field, nested_fields) @@ -284,6 +290,7 @@ def products_graphql_query(fields): product_path_regex_var = query.add_variable("productPathRegex", "String!") statuses_var = query.add_variable("productStatuses", "[String!]") tags_var = query.add_variable("productTags", "[String!]") + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -298,6 +305,7 @@ def products_graphql_query(fields): products_field.set_filter("tags", tags_var) products_field.set_filter("nameEx", product_name_regex_var) products_field.set_filter("pathEx", product_path_regex_var) + products_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(set(fields)) add_links_fields(products_field, nested_fields) @@ -333,6 +341,7 @@ def versions_graphql_query(fields): ) statuses_var = query.add_variable("versionStatuses", "[String!]") tags_var = query.add_variable("versionTags", "[String!]") + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -347,6 +356,7 @@ def versions_graphql_query(fields): versions_field.set_filter("heroOrLatestOnly", hero_or_latest_only_var) versions_field.set_filter("statuses", statuses_var) versions_field.set_filter("tags", tags_var) + versions_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(set(fields)) add_links_fields(versions_field, nested_fields) @@ -383,6 +393,7 @@ def representations_graphql_query(fields): tags_var = query.add_variable( "representationTags", "[String!]" ) + filter_var = query.add_variable("filter", "String!") project_field = query.add_field("project") project_field.set_filter("name", project_name_var) @@ -394,6 +405,7 @@ def representations_graphql_query(fields): repres_field.set_filter("hasLinks", has_links_var) repres_field.set_filter("statuses", statuses_var) repres_field.set_filter("tags", tags_var) + repres_field.set_filter("filter", filter_var) nested_fields = fields_to_dict(set(fields)) add_links_fields(repres_field, nested_fields) diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 80d940086..4dc4b3449 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -2265,6 +2265,16 @@ def _prepare_fields( ) } + def _prepare_advanced_filters( + self, filters: Union[str, dict[str, Any], None] + ) -> Optional[str]: + if not filters: + return None + + if isinstance(filters, dict): + return json.dumps(filters) + return filters + def _convert_entity_data(self, entity: AnyEntityDict): if not entity or "data" not in entity: return diff --git a/ayon_api/typing.py b/ayon_api/typing.py index 008490093..0bcf2d9d4 100644 --- a/ayon_api/typing.py +++ b/ayon_api/typing.py @@ -602,3 +602,23 @@ class ActionConfigResponse(TypedDict): class EntityListAttributeDefinitionDict(TypedDict): name: str data: dict[str, Any] + + +AdvancedFilterOperator = Literal["and", "or"] +AdvancedFilterConditionOperator = Literal[ + "eq", "lt", "gt", "lte", "gte", "ne", + "isnull", "notnull", + "in", "notin", "contains", "excludes", + "any", "like" +] + + +class AdvancedFilterConditionDict(TypedDict): + key: str + value: Any + operator: AdvancedFilterConditionOperator + + +class AdvancedFilterDict(TypedDict): + conditions: list[Union[AdvancedFilterConditionDict, "AdvancedFilterDict"]] + operator: AdvancedFilterOperator