From 6ce31c6296d444c86522cc947028792601174b1d Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 31 May 2024 13:56:49 +0200 Subject: [PATCH 1/5] implemented functions to query representation hierarchy --- ayon_api/graphql_queries.py | 52 +++++++++++++ ayon_api/server_api.py | 150 ++++++++++++++++++++++++++++++++++++ ayon_api/utils.py | 5 ++ 3 files changed, 207 insertions(+) diff --git a/ayon_api/graphql_queries.py b/ayon_api/graphql_queries.py index 94236af02..3fed51fea 100644 --- a/ayon_api/graphql_queries.py +++ b/ayon_api/graphql_queries.py @@ -486,6 +486,58 @@ def representations_parents_qraphql_query( return query +def representations_hierarchy_qraphql_query( + folder_fields, + product_fields, + version_fields, + representation_fields, +): + query = GraphQlQuery("RepresentationsParentsQuery") + + project_name_var = query.add_variable("projectName", "String!") + repre_ids_var = query.add_variable("representationIds", "[String!]") + + project_field = query.add_field("project") + project_field.set_filter("name", project_name_var) + + fields_queue = collections.deque() + + repres_field = project_field.add_field_with_edges("representations") + for key, value in fields_to_dict(representation_fields).items(): + fields_queue.append((key, value, repres_field)) + + repres_field.set_filter("ids", repre_ids_var) + version_field = None + if folder_fields or product_fields or version_fields: + version_field = repres_field.add_field("version") + if version_fields: + for key, value in fields_to_dict(version_fields).items(): + fields_queue.append((key, value, version_field)) + + product_field = None + if folder_fields or product_fields: + product_field = version_field.add_field("product") + for key, value in fields_to_dict(product_fields).items(): + fields_queue.append((key, value, product_field)) + + if folder_fields: + folder_field = product_field.add_field("folder") + for key, value in fields_to_dict(folder_fields).items(): + fields_queue.append((key, value, folder_field)) + + while fields_queue: + item = fields_queue.popleft() + key, value, parent = item + field = parent.add_field(key) + if value is FIELD_VALUE: + continue + + for k, v in value.items(): + fields_queue.append((k, v, field)) + + return query + + def workfiles_info_graphql_query(fields): query = GraphQlQuery("WorkfilesInfo") project_name_var = query.add_variable("projectName", "String!") diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index 19190bf45..da628bed1 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -65,6 +65,7 @@ versions_graphql_query, representations_graphql_query, representations_parents_qraphql_query, + representations_hierarchy_qraphql_query, workfiles_info_graphql_query, events_graphql_query, users_graphql_query, @@ -80,6 +81,7 @@ ) from .utils import ( RepresentationParents, + RepresentationHierarchy, prepare_query_string, logout_from_server, create_entity_id, @@ -6314,6 +6316,154 @@ def get_representation_parents(self, project_name, representation_id): ) return parents_by_repre_id[representation_id] + def get_representations_hierarchy( + self, + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, + ): + """Find representation with parents by representation id. + + Representation entity with parent entities up to project. + + Default fields are used when any fields are set to `None`. But it is + possible to pass in empty iterable (list, set, tuple) to skip + entity. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + dict[str, RepresentationHierarchy]: Parent entities by + representation id. + + """ + if not representation_ids: + return {} + + if project_fields is not None: + project_fields = set(project_fields) + + project = {} + if project_fields is None: + project = self.get_project(project_name) + + elif project_fields: + # Keep project as empty dictionary if does not have + # filled any fields + project = self.get_project( + project_name, fields=project_fields + ) + + repre_ids = set(representation_ids) + output = { + repre_id: RepresentationHierarchy( + project, None, None, None, None + ) + for repre_id in representation_ids + } + + if folder_fields is None: + folder_fields = self.get_default_fields_for_type("folder") + else: + folder_fields = set(folder_fields) + + if product_fields is None: + product_fields = self.get_default_fields_for_type("product") + else: + product_fields = set(product_fields) + + if version_fields is None: + version_fields = self.get_default_fields_for_type("version") + else: + version_fields = set(version_fields) + + if representation_fields is None: + representation_fields = self.get_default_fields_for_type( + "representation" + ) + else: + representation_fields = set(representation_fields) + + representation_fields.add("id") + + query = representations_hierarchy_qraphql_query( + folder_fields, + product_fields, + version_fields, + representation_fields, + ) + query.set_variable_value("projectName", project_name) + query.set_variable_value("representationIds", list(repre_ids)) + + parsed_data = query.query(self) + for repre in parsed_data["project"]["representations"]: + repre_id = repre["id"] + version = repre.pop("version", {}) + product = version.pop("product", {}) + folder = product.pop("folder", {}) + self._convert_entity_data(version) + self._convert_entity_data(product) + self._convert_entity_data(folder) + output[repre_id] = RepresentationHierarchy( + project, folder, product, version, repre + ) + + return output + + def get_representation_hierarchy( + self, + project_name, + representation_id, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + representation_fields=None, + ): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + RepresentationHierarchy: Representation hierarchy entities. + + """ + if not representation_id: + return None + + parents_by_repre_id = self.get_representations_hierarchy( + project_name, + [representation_id], + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + representation_fields=representation_fields, + ) + return parents_by_repre_id[representation_id] + def get_repre_ids_by_context_filters( self, project_name, diff --git a/ayon_api/utils.py b/ayon_api/utils.py index 601f2d65d..48fd1e4cc 100644 --- a/ayon_api/utils.py +++ b/ayon_api/utils.py @@ -33,6 +33,11 @@ ("version", "product", "folder", "project") ) +RepresentationHierarchy = collections.namedtuple( + "RepresentationHierarchy", + ("project", "folder", "product", "version", "representation") +) + def get_default_timeout(): """Default value for requests timeout. From 7282a65e41e080995c1b164ec21f1be384e56ed3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 31 May 2024 13:57:38 +0200 Subject: [PATCH 2/5] 'get_representations_parents' uses 'get_representations_hierarchy' --- ayon_api/server_api.py | 154 ++++++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 71 deletions(-) diff --git a/ayon_api/server_api.py b/ayon_api/server_api.py index da628bed1..d98e7d0cd 100644 --- a/ayon_api/server_api.py +++ b/ayon_api/server_api.py @@ -64,7 +64,6 @@ products_graphql_query, versions_graphql_query, representations_graphql_query, - representations_parents_qraphql_query, representations_hierarchy_qraphql_query, workfiles_info_graphql_query, events_graphql_query, @@ -6246,76 +6245,6 @@ def get_representation_by_name( return representation return None - def get_representations_parents(self, project_name, representation_ids): - """Find representations parents by representation id. - - Representation parent entities up to project. - - Args: - project_name (str): Project where to look for entities. - representation_ids (Iterable[str]): Representation ids. - - Returns: - dict[str, RepresentationParents]: Parent entities by - representation id. - - """ - if not representation_ids: - return {} - - project = self.get_project(project_name) - repre_ids = set(representation_ids) - output = { - repre_id: RepresentationParents(None, None, None, None) - for repre_id in representation_ids - } - - version_fields = self.get_default_fields_for_type("version") - product_fields = self.get_default_fields_for_type("product") - folder_fields = self.get_default_fields_for_type("folder") - - query = representations_parents_qraphql_query( - version_fields, product_fields, folder_fields - ) - query.set_variable_value("projectName", project_name) - query.set_variable_value("representationIds", list(repre_ids)) - - parsed_data = query.query(self) - for repre in parsed_data["project"]["representations"]: - repre_id = repre["id"] - version = repre.pop("version") - product = version.pop("product") - folder = product.pop("folder") - self._convert_entity_data(version) - self._convert_entity_data(product) - self._convert_entity_data(folder) - output[repre_id] = RepresentationParents( - version, product, folder, project - ) - - return output - - def get_representation_parents(self, project_name, representation_id): - """Find representation parents by representation id. - - Representation parent entities up to project. - - Args: - project_name (str): Project where to look for entities. - representation_id (str): Representation id. - - Returns: - RepresentationParents: Representation parent entities. - - """ - if not representation_id: - return None - - parents_by_repre_id = self.get_representations_parents( - project_name, [representation_id] - ) - return parents_by_repre_id[representation_id] - def get_representations_hierarchy( self, project_name, @@ -6464,6 +6393,89 @@ def get_representation_hierarchy( ) return parents_by_repre_id[representation_id] + def get_representations_parents( + self, + project_name, + representation_ids, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + ): + """Find representations parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + dict[str, RepresentationParents]: Parent entities by + representation id. + + """ + hierarchy_by_repre_id = self.get_representations_hierarchy( + project_name, + representation_ids, + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + representation_fields={"id"}, + ) + return { + repre_id: RepresentationParents( + hierarchy.version, + hierarchy.product, + hierarchy.folder, + hierarchy.project, + ) + for repre_id, hierarchy in hierarchy_by_repre_id.items() + } + + def get_representation_parents( + self, + project_name, + representation_id, + project_fields=None, + folder_fields=None, + product_fields=None, + version_fields=None, + ): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + + Returns: + RepresentationParents: Representation parent entities. + + """ + if not representation_id: + return None + + parents_by_repre_id = self.get_representations_parents( + project_name, + [representation_id], + project_fields=project_fields, + folder_fields=folder_fields, + product_fields=product_fields, + version_fields=version_fields, + ) + return parents_by_repre_id[representation_id] + def get_repre_ids_by_context_filters( self, project_name, From 358fe5cc8acbf7d2e278f2ffc36e9fb7fa2c7c18 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 31 May 2024 13:58:33 +0200 Subject: [PATCH 3/5] import 'fields_to_dict' from graphql --- ayon_api/graphql_queries.py | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/ayon_api/graphql_queries.py b/ayon_api/graphql_queries.py index 3fed51fea..98e27cae5 100644 --- a/ayon_api/graphql_queries.py +++ b/ayon_api/graphql_queries.py @@ -1,29 +1,7 @@ import collections from .constants import DEFAULT_LINK_FIELDS -from .graphql import FIELD_VALUE, GraphQlQuery - - -def fields_to_dict(fields): - if not fields: - return None - - output = {} - for field in fields: - hierarchy = field.split(".") - last = hierarchy.pop(-1) - value = output - for part in hierarchy: - if value is FIELD_VALUE: - break - - if part not in value: - value[part] = {} - value = value[part] - - if value is not FIELD_VALUE: - value[last] = FIELD_VALUE - return output +from .graphql import FIELD_VALUE, GraphQlQuery, fields_to_dict def add_links_fields(entity_field, nested_fields): From 1c4fd715d0f172c4a5cbaee96492f0d162fd2e09 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 31 May 2024 13:58:46 +0200 Subject: [PATCH 4/5] 'fields_to_dict' always return dictionary --- ayon_api/graphql.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ayon_api/graphql.py b/ayon_api/graphql.py index 7668476de..8c2e0f55d 100644 --- a/ayon_api/graphql.py +++ b/ayon_api/graphql.py @@ -10,10 +10,10 @@ def fields_to_dict(fields): + output = {} if not fields: - return None + return output - output = {} for field in fields: hierarchy = field.split(".") last = hierarchy.pop(-1) From 26c3923a095284db1c898cb4fe6fb01aca92a7e7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Fri, 31 May 2024 16:14:13 +0200 Subject: [PATCH 5/5] added new functions to public api --- ayon_api/__init__.py | 4 +++ ayon_api/_api.py | 67 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/ayon_api/__init__.py b/ayon_api/__init__.py index 5531f06391..4540a5317 100644 --- a/ayon_api/__init__.py +++ b/ayon_api/__init__.py @@ -174,6 +174,8 @@ get_representations, get_representation_by_id, get_representation_by_name, + get_representations_hierarchy, + get_representation_hierarchy, get_representations_parents, get_representation_parents, get_repre_ids_by_context_filters, @@ -390,6 +392,8 @@ "get_representations", "get_representation_by_id", "get_representation_by_name", + "get_representations_hierarchy", + "get_representation_hierarchy", "get_representations_parents", "get_representation_parents", "get_repre_ids_by_context_filters", diff --git a/ayon_api/_api.py b/ayon_api/_api.py index 5decea8cf..79386326b 100644 --- a/ayon_api/_api.py +++ b/ayon_api/_api.py @@ -3232,14 +3232,69 @@ def get_representation_by_name(*args, **kwargs): return con.get_representation_by_name(*args, **kwargs) +def get_representations_hierarchy(*args, **kwargs): + """Find representation with parents by representation id. + + Representation entity with parent entities up to project. + + Default fields are used when any fields are set to `None`. But it is + possible to pass in empty iterable (list, set, tuple) to skip + entity. + + Args: + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + dict[str, RepresentationHierarchy]: Parent entities by + representation id. + + """ + con = get_server_api_connection() + return con.get_representations_hierarchy(*args, **kwargs) + + +def get_representation_hierarchy(*args, **kwargs): + """Find representation parents by representation id. + + Representation parent entities up to project. + + Args: + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. + representation_fields (Optional[Iterable[str]]): Representation + fields. + + Returns: + RepresentationHierarchy: Representation hierarchy entities. + + """ + con = get_server_api_connection() + return con.get_representation_hierarchy(*args, **kwargs) + + def get_representations_parents(*args, **kwargs): """Find representations parents by representation id. Representation parent entities up to project. Args: - project_name (str): Project where to look for entities. - representation_ids (Iterable[str]): Representation ids. + project_name (str): Project where to look for entities. + representation_ids (Iterable[str]): Representation ids. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. Returns: dict[str, RepresentationParents]: Parent entities by @@ -3256,8 +3311,12 @@ def get_representation_parents(*args, **kwargs): Representation parent entities up to project. Args: - project_name (str): Project where to look for entities. - representation_id (str): Representation id. + project_name (str): Project where to look for entities. + representation_id (str): Representation id. + project_fields (Optional[Iterable[str]]): Project fields. + folder_fields (Optional[Iterable[str]]): Folder fields. + product_fields (Optional[Iterable[str]]): Product fields. + version_fields (Optional[Iterable[str]]): Version fields. Returns: RepresentationParents: Representation parent entities.