diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 9c85bdd84..8e9cd0736 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -722,6 +722,7 @@ def get_events(*args, **kwargs): Args: topics (Optional[Iterable[str]]): Name of topics. + event_ids (Optional[Iterable[str]]): Event ids. project_names (Optional[Iterable[str]]): Project on which event happened. states (Optional[Iterable[str]]): Filtering by states. @@ -746,6 +747,22 @@ def get_events(*args, **kwargs): def update_event(*args, **kwargs): + """Update event data. + + Args: + event_id (str): Event id. + sender (Optional[str]): New sender of event. + project_name (Optional[str]): New project name. + username (Optional[str]): New username. + status (Optional[str]): New event status. Enum: "pending", + "in_progress", "finished", "failed", "aborted", "restarted" + description (Optional[str]): New description. + summary (Optional[dict[str, Any]]): New summary. + payload (Optional[dict[str, Any]]): New payload. + progress (Optional[int]): New progress. Range [0-100]. + retries (Optional[int]): New retries. + + """ con = get_server_api_connection() return con.update_event(*args, **kwargs) @@ -759,7 +776,7 @@ def dispatch_event(*args, **kwargs): event_hash (Optional[str]): Event hash. project_name (Optional[str]): Project name. username (Optional[str]): Username which triggered event. - dependencies (Optional[list[str]]): List of event id dependencies. + depends_on (Optional[str]): Add dependency to another event. description (Optional[str]): Description of event. summary (Optional[dict[str, Any]]): Summary of event that can be used for simple filtering on listeners. @@ -769,6 +786,8 @@ def dispatch_event(*args, **kwargs): store (Optional[bool]): Store event in event queue for possible future processing otherwise is event send only to active listeners. + dependencies (Optional[list[str]]): Deprecated. + List of event id dependencies. Returns: RestApiResponse: Response from server. @@ -2370,6 +2389,8 @@ def get_folders(*args, **kwargs): children. Ignored when None, default behavior. statuses (Optional[Iterable[str]]): Folder statuses used for filtering. + assignees_all (Optional[Iterable[str]]): Filter by assigness + on children tasks. Task must have all of passed assignees. tags (Optional[Iterable[str]]): Folder tags used for filtering. active (Optional[bool]): Filter active/inactive folders. @@ -2992,10 +3013,12 @@ def get_versions(*args, **kwargs): version filtering. product_ids (Optional[Iterable[str]]): Product ids used for version filtering. + task_ids (Optional[Iterable[str]]): Task ids used for + version filtering. versions (Optional[Iterable[int]]): Versions we're interested in. - hero (Optional[bool]): Receive also hero versions when set to true. - standard (Optional[bool]): Receive versions which are not hero when - set to true. + hero (Optional[bool]): Skip hero versions when set to False. + standard (Optional[bool]): Skip standard (non-hero) when + set to False. latest (Optional[bool]): Return only latest version of standard versions. This can be combined only with 'standard' attribute set to True. @@ -3250,6 +3273,7 @@ def update_version(*args, **kwargs): version (Optional[int]): New version. product_id (Optional[str]): New product id. task_id (Optional[Union[str, None]]): New task id. + author (Optional[str]): New author username. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. tags (Optional[Iterable[str]]): New tags. diff --git a/ayon_api/graphql_queries.py b/ayon_api/graphql_queries.py index 4cb6bd637..25d303463 100644 --- a/ayon_api/graphql_queries.py +++ b/ayon_api/graphql_queries.py @@ -157,6 +157,9 @@ def folders_graphql_query(fields): has_links_var = query.add_variable("folderHasLinks", "HasLinksFilter") has_children_var = query.add_variable("folderHasChildren", "Boolean!") statuses_var = query.add_variable("folderStatuses", "[String!]") + folder_assignees_all_var = query.add_variable( + "folderAssigneesAll", "[String!]" + ) tags_var = query.add_variable("folderTags", "[String!]") project_field = query.add_field("project") @@ -170,6 +173,7 @@ def folders_graphql_query(fields): folders_field.set_filter("pathEx", folder_path_regex_var) folders_field.set_filter("folderTypes", folder_types_var) folders_field.set_filter("statuses", statuses_var) + folders_field.set_filter("assignees", folder_assignees_all_var) folders_field.set_filter("tags", tags_var) folders_field.set_filter("hasProducts", has_products_var) folders_field.set_filter("hasTasks", has_tasks_var) @@ -567,6 +571,7 @@ def workfiles_info_graphql_query(fields): def events_graphql_query(fields): query = GraphQlQuery("Events") topics_var = query.add_variable("eventTopics", "[String!]") + ids_var = query.add_variable("eventIds", "[String!]") projects_var = query.add_variable("projectNames", "[String!]") states_var = query.add_variable("eventStates", "[String!]") users_var = query.add_variable("eventUsers", "[String!]") @@ -576,6 +581,7 @@ def events_graphql_query(fields): older_than_var = query.add_variable("olderThanFilter", "String!") events_field = query.add_field_with_edges("events") + events_field.set_filter("ids", ids_var) events_field.set_filter("topics", topics_var) events_field.set_filter("projects", projects_var) events_field.set_filter("states", states_var) diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 6f1b06ead..abd686202 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -14,6 +14,7 @@ import copy import uuid import warnings +import itertools from contextlib import contextmanager try: @@ -111,6 +112,29 @@ ) +def _convert_list_filter_value(value): + if value is None: + return None + + if isinstance(value, PatternType): + return [value.pattern] + + if isinstance(value, (int, float, str, bool)): + return [value] + return list(set(value)) + + +def _prepare_list_filters(output, *args, **kwargs): + for key, value in itertools.chain(args, kwargs.items()): + value = _convert_list_filter_value(value) + if value is None: + continue + if not value: + return False + output[key] = value + return True + + def _get_description(response): if HTTPStatus is None: return str(response.orig_response) @@ -1383,6 +1407,7 @@ def get_event(self, event_id): def get_events( self, topics=None, + event_ids=None, project_names=None, states=None, users=None, @@ -1399,6 +1424,7 @@ def get_events( Args: topics (Optional[Iterable[str]]): Name of topics. + event_ids (Optional[Iterable[str]]): Event ids. project_names (Optional[Iterable[str]]): Project on which event happened. states (Optional[Iterable[str]]): Filtering by states. @@ -1419,42 +1445,27 @@ def get_events( """ filters = {} - if topics is not None: - topics = set(topics) - if not topics: - return - filters["eventTopics"] = list(topics) - - if project_names is not None: - project_names = set(project_names) - if not project_names: - return - filters["projectNames"] = list(project_names) - - if states is not None: - states = set(states) - if not states: - return - filters["eventStates"] = list(states) - - if users is not None: - users = set(users) - if not users: - return - filters["eventUsers"] = list(users) + if not _prepare_list_filters( + filters, + ("eventTopics", topics), + ("eventIds", event_ids), + ("projectNames", project_names), + ("eventStates", states), + ("eventUsers", users), + ): + return if include_logs is None: include_logs = False - filters["includeLogsFilter"] = include_logs - if has_children is not None: - filters["hasChildrenFilter"] = has_children - - if newer_than is not None: - filters["newerThanFilter"] = newer_than - - if older_than is not None: - filters["olderThanFilter"] = older_than + for filter_key, filter_value in ( + ("includeLogsFilter", include_logs), + ("hasChildrenFilter", has_children), + ("newerThanFilter", newer_than), + ("olderThanFilter", older_than), + ): + if filter_value is not None: + filters[filter_key] = filter_value if not fields: fields = self.get_default_fields_for_type("event") @@ -1472,6 +1483,7 @@ def update_event( event_id, sender=None, project_name=None, + username=None, status=None, description=None, summary=None, @@ -1479,11 +1491,28 @@ def update_event( progress=None, retries=None ): + """Update event data. + + Args: + event_id (str): Event id. + sender (Optional[str]): New sender of event. + project_name (Optional[str]): New project name. + username (Optional[str]): New username. + status (Optional[str]): New event status. Enum: "pending", + "in_progress", "finished", "failed", "aborted", "restarted" + description (Optional[str]): New description. + summary (Optional[dict[str, Any]]): New summary. + payload (Optional[dict[str, Any]]): New payload. + progress (Optional[int]): New progress. Range [0-100]. + retries (Optional[int]): New retries. + + """ kwargs = { key: value for key, value in ( ("sender", sender), ("project", project_name), + ("user", username), ("status", status), ("description", description), ("summary", summary), @@ -1522,12 +1551,13 @@ def dispatch_event( event_hash=None, project_name=None, username=None, - dependencies=None, + depends_on=None, description=None, summary=None, payload=None, finished=True, store=True, + dependencies=None, ): """Dispatch event to server. @@ -1536,8 +1566,8 @@ def dispatch_event( sender (Optional[str]): Sender of event. event_hash (Optional[str]): Event hash. project_name (Optional[str]): Project name. + depends_on (Optional[str]): Add dependency to another event. username (Optional[str]): Username which triggered event. - dependencies (Optional[list[str]]): List of event id dependencies. description (Optional[str]): Description of event. summary (Optional[dict[str, Any]]): Summary of event that can be used for simple filtering on listeners. @@ -1547,6 +1577,8 @@ def dispatch_event( store (Optional[bool]): Store event in event queue for possible future processing otherwise is event send only to active listeners. + dependencies (Optional[list[str]]): Deprecated. + List of event id dependencies. Returns: RestApiResponse: Response from server. @@ -1562,13 +1594,23 @@ def dispatch_event( "hash": event_hash, "project": project_name, "user": username, - "dependencies": dependencies, "description": description, "summary": summary, "payload": payload, "finished": finished, "store": store, } + if depends_on: + event_data["dependsOn"] = depends_on + + if dependencies: + warnings.warn( + ( + "Used deprecated argument 'dependencies' in" + " 'dispatch_event'. Use 'depends_on' instead." + ), + DeprecationWarning + ) response = self.post("events", **event_data) response.raise_for_status() @@ -1650,6 +1692,7 @@ def enroll_event_job( kwargs["description"] = description if events_filter is not None: kwargs["filter"] = events_filter + response = self.post("enroll", **kwargs) if response.status_code == 204: return None @@ -4069,6 +4112,13 @@ def get_folders_rest(self, project_name, include_attrib=False): list[dict[str, Any]]: List of folder entities. """ + warnings.warn( + ( + "DEPRECATION: Used deprecated 'get_folders_rest'," + " use 'get_rest_folders' instead." + ), + DeprecationWarning + ) return self.get_rest_folders(project_name, include_attrib) def get_folders( @@ -4084,6 +4134,7 @@ def get_folders( has_tasks=None, has_children=None, statuses=None, + assignees_all=None, tags=None, active=True, has_links=None, @@ -4120,6 +4171,8 @@ def get_folders( children. Ignored when None, default behavior. statuses (Optional[Iterable[str]]): Folder statuses used for filtering. + assignees_all (Optional[Iterable[str]]): Filter by assigness + on children tasks. Task must have all of passed assignees. tags (Optional[Iterable[str]]): Folder tags used for filtering. active (Optional[bool]): Filter active/inactive folders. @@ -4142,41 +4195,27 @@ def get_folders( filters = { "projectName": project_name } - if folder_ids is not None: - folder_ids = set(folder_ids) - if not folder_ids: - return - filters["folderIds"] = list(folder_ids) - - if folder_paths is not None: - folder_paths = set(folder_paths) - if not folder_paths: - return - filters["folderPaths"] = list(folder_paths) - - if folder_names is not None: - folder_names = set(folder_names) - if not folder_names: - return - filters["folderNames"] = list(folder_names) - - if folder_types is not None: - folder_types = set(folder_types) - if not folder_types: - return - filters["folderTypes"] = list(folder_types) - - if statuses is not None: - statuses = set(statuses) - if not statuses: - return - filters["folderStatuses"] = list(statuses) + if not _prepare_list_filters( + filters, + ("folderIds", folder_ids), + ("folderPaths", folder_paths), + ("folderNames", folder_names), + ("folderTypes", folder_types), + ("folderStatuses", statuses), + ("folderTags", tags), + ("folderAssigneesAll", assignees_all), + ): + return - if tags is not None: - tags = set(tags) - if not tags: - return - filters["folderTags"] = list(tags) + for filter_key, filter_value in ( + ("folderPathRegex", folder_path_regex), + ("folderHasProducts", has_products), + ("folderHasTasks", has_tasks), + ("folderHasLinks", has_links), + ("folderHasChildren", has_children), + ): + if filter_value is not None: + filters[filter_key] = filter_value if parent_ids is not None: parent_ids = set(parent_ids) @@ -4198,21 +4237,6 @@ def get_folders( filters["parentFolderIds"] = list(parent_ids) - if folder_path_regex is not None: - filters["folderPathRegex"] = folder_path_regex - - if has_products is not None: - filters["folderHasProducts"] = has_products - - if has_tasks is not None: - filters["folderHasTasks"] = has_tasks - - if has_links is not None: - filters["folderHasLinks"] = has_links.upper() - - if has_children is not None: - filters["folderHasChildren"] = has_children - if not fields: fields = self.get_default_fields_for_type("folder") else: @@ -4590,54 +4614,18 @@ def get_tasks( filters = { "projectName": project_name } - - if task_ids is not None: - task_ids = set(task_ids) - if not task_ids: - return - filters["taskIds"] = list(task_ids) - - if task_names is not None: - task_names = set(task_names) - if not task_names: - return - filters["taskNames"] = list(task_names) - - if task_types is not None: - task_types = set(task_types) - if not task_types: - return - filters["taskTypes"] = list(task_types) - - if folder_ids is not None: - folder_ids = set(folder_ids) - if not folder_ids: - return - filters["folderIds"] = list(folder_ids) - - if assignees is not None: - assignees = set(assignees) - if not assignees: - return - filters["taskAssigneesAny"] = list(assignees) - - if assignees_all is not None: - assignees_all = set(assignees_all) - if not assignees_all: - return - filters["taskAssigneesAll"] = list(assignees_all) - - if statuses is not None: - statuses = set(statuses) - if not statuses: - return - filters["taskStatuses"] = list(statuses) - - if tags is not None: - tags = set(tags) - if not tags: - return - filters["taskTags"] = list(tags) + if not _prepare_list_filters( + filters, + ("taskIds", task_ids), + ("taskNames", task_names), + ("taskTypes", task_types), + ("folderIds", folder_ids), + ("taskAssigneesAny", assignees), + ("taskAssigneesAll", assignees_all), + ("taskStatuses", statuses), + ("taskTags", tags), + ): + return if not fields: fields = self.get_default_fields_for_type("task") @@ -4795,42 +4783,16 @@ def get_tasks_by_folder_paths( "projectName": project_name, "folderPaths": list(folder_paths), } - - if task_names is not None: - task_names = set(task_names) - if not task_names: - return - filters["taskNames"] = list(task_names) - - if task_types is not None: - task_types = set(task_types) - if not task_types: - return - filters["taskTypes"] = list(task_types) - - if assignees is not None: - assignees = set(assignees) - if not assignees: - return - filters["taskAssigneesAny"] = list(assignees) - - if assignees_all is not None: - assignees_all = set(assignees_all) - if not assignees_all: - return - filters["taskAssigneesAll"] = list(assignees_all) - - if statuses is not None: - statuses = set(statuses) - if not statuses: - return - filters["taskStatuses"] = list(statuses) - - if tags is not None: - tags = set(tags) - if not tags: - return - filters["taskTags"] = list(tags) + if not _prepare_list_filters( + filters, + ("taskNames", task_names), + ("taskTypes", task_types), + ("taskAssigneesAny", assignees), + ("taskAssigneesAll", assignees_all), + ("taskStatuses", statuses), + ("taskTags", tags), + ): + return if not fields: fields = self.get_default_fields_for_type("task") @@ -5229,9 +5191,12 @@ def get_products( if own_attributes is not _PLACEHOLDER: warnings.warn( - "'own_attributes' is not supported for products. The argument" - " will be removed form function signature in future" - " (apx. version 1.0.10 or 1.1.0)." + ( + "'own_attributes' is not supported for products. The" + " argument will be removed form function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning ) # Add 'name' and 'folderId' if 'names_by_folder_ids' filter is entered @@ -5250,35 +5215,21 @@ def get_products( if filter_product_names: filters["productNames"] = list(filter_product_names) - if product_ids is not None: - product_ids = set(product_ids) - if not product_ids: - return - filters["productIds"] = list(product_ids) - - if product_types is not None: - product_types = set(product_types) - if not product_types: - return - filters["productTypes"] = list(product_types) - - if statuses is not None: - statuses = set(statuses) - if not statuses: - return - filters["productStatuses"] = list(statuses) - - if tags is not None: - tags = set(tags) - if not tags: - return - filters["productTags"] = list(tags) - - if product_name_regex: - filters["productNameRegex"] = product_name_regex + if not _prepare_list_filters( + filters, + ("productIds", product_ids), + ("productTypes", product_types), + ("productStatuses", statuses), + ("productTags", tags), + ): + return - if product_path_regex: - filters["productPathRegex"] = product_path_regex + for filter_key, filter_value in ( + ("productNameRegex", product_name_regex), + ("productPathRegex", product_path_regex), + ): + if filter_value: + filters[filter_key] = filter_value query = products_graphql_query(fields) for attr, filter_value in filters.items(): @@ -5590,6 +5541,7 @@ def get_versions( project_name, version_ids=None, product_ids=None, + task_ids=None, versions=None, hero=True, standard=True, @@ -5608,10 +5560,12 @@ def get_versions( version filtering. product_ids (Optional[Iterable[str]]): Product ids used for version filtering. + task_ids (Optional[Iterable[str]]): Task ids used for + version filtering. versions (Optional[Iterable[int]]): Versions we're interested in. - hero (Optional[bool]): Receive also hero versions when set to true. - standard (Optional[bool]): Receive versions which are not hero when - set to true. + hero (Optional[bool]): Skip hero versions when set to False. + standard (Optional[bool]): Skip standard (non-hero) when + set to False. latest (Optional[bool]): Return only latest version of standard versions. This can be combined only with 'standard' attribute set to True. @@ -5652,46 +5606,30 @@ def get_versions( if own_attributes is not _PLACEHOLDER: warnings.warn( - "'own_attributes' is not supported for versions. The argument" - " will be removed form function signature in future" - " (apx. version 1.0.10 or 1.1.0)." + ( + "'own_attributes' is not supported for versions. The" + " argument will be removed form function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning ) + if not hero and not standard: + return + filters = { "projectName": project_name } - if version_ids is not None: - version_ids = set(version_ids) - if not version_ids: - return - filters["versionIds"] = list(version_ids) - - if product_ids is not None: - product_ids = set(product_ids) - if not product_ids: - return - filters["productIds"] = list(product_ids) - - # TODO versions can't be used as filter at this moment! - if versions is not None: - versions = set(versions) - if not versions: - return - filters["versions"] = list(versions) - - if statuses is not None: - statuses = set(statuses) - if not statuses: - return - filters["versionStatuses"] = list(statuses) - - if tags is not None: - tags = set(tags) - if not tags: - return - filters["versionTags"] = list(tags) - - if not hero and not standard: + if not _prepare_list_filters( + filters, + ("taskIds", task_ids), + ("versionIds", version_ids), + ("productIds", product_ids), + ("taskIds", task_ids), + ("versions", versions), + ("versionStatuses", statuses), + ("versionTags", tags), + ): return queries = [] @@ -6143,6 +6081,7 @@ def update_version( version=None, product_id=None, task_id=NOT_SET, + author=None, attrib=None, data=None, tags=None, @@ -6167,6 +6106,7 @@ def update_version( version (Optional[int]): New version. product_id (Optional[str]): New product id. task_id (Optional[Union[str, None]]): New task id. + author (Optional[str]): New author username. attrib (Optional[dict[str, Any]]): New attributes. data (Optional[dict[str, Any]]): New data. tags (Optional[Iterable[str]]): New tags. @@ -6185,6 +6125,7 @@ def update_version( ("tags", tags), ("status", status), ("active", active), + ("author", author), ): if value is not None: update_data[key] = value @@ -6303,9 +6244,12 @@ def get_representations( if own_attributes is not _PLACEHOLDER: warnings.warn( - "'own_attributes' is not supported for representations. " - "The argument will be removed form function signature in " - "future (apx. version 1.0.10 or 1.1.0)." + ( + "'own_attributes' is not supported for representations. " + "The argument will be removed form function signature in " + "future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning ) if "files" in fields: @@ -7004,9 +6948,12 @@ def get_workfiles_info( if own_attributes is not _PLACEHOLDER: warnings.warn( - "'own_attributes' is not supported for workfiles. The argument" - " will be removed form function signature in future" - " (apx. version 1.0.10 or 1.1.0)." + ( + "'own_attributes' is not supported for workfiles. The" + " argument will be removed form function signature in" + " future (apx. version 1.0.10 or 1.1.0)." + ), + DeprecationWarning ) query = workfiles_info_graphql_query(fields)