From dada2f4831045370265037a8b1c01c43f4dd2f92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:06:35 +0200 Subject: [PATCH 01/22] added function which extract project name based on project id --- .../modules/kitsu/utils/update_op_with_zou.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index de74b0c677..c39d1c5e36 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -33,6 +33,20 @@ def create_op_asset(gazu_entity: dict) -> dict: } +def get_kitsu_project_name(project_id: str): + """Get project name based on project id in kitsu. + + Args: + project_id (str): Id of project in Kitsu. + + Returns: + str: Project name which has project in Kitsu. + """ + + project = gazu.project.get_project(project_id) + return project["name"] + + def set_op_project(dbcon: AvalonMongoDB, project_id: str): """Set project context. @@ -40,9 +54,8 @@ def set_op_project(dbcon: AvalonMongoDB, project_id: str): dbcon (AvalonMongoDB): Connection to DB project_id (str): Project zou ID """ - project = gazu.project.get_project(project_id) - project_name = project["name"] - dbcon.Session["AVALON_PROJECT"] = project_name + + dbcon.Session["AVALON_PROJECT"] = get_kitsu_project_name(project_id) def update_op_assets( From b4d11d4ae709fc65f41ec9c09cbf5e52a183677c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:07:10 +0200 Subject: [PATCH 02/22] use project name function to drop project collection --- openpype/modules/kitsu/utils/sync_service.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 577050c5af..677d269bca 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -7,6 +7,7 @@ from .credentials import validate_credentials from .update_op_with_zou import ( create_op_asset, set_op_project, + get_kitsu_project_name, write_project_to_op, update_op_assets, ) @@ -124,12 +125,11 @@ class Listener: def _delete_project(self, data): """Delete project.""" - project_doc = self.dbcon.find_one( - {"type": "project", "data.zou_id": data["project_id"]} - ) + + project_name = get_kitsu_project_name(data["project_id"]) # Delete project collection - self.dbcon.database[project_doc["name"]].drop() + self.dbcon.database[project_name].drop() # == Asset == From ee370cb7a315237608f3b60a6b6778d3fc900852 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:07:40 +0200 Subject: [PATCH 03/22] change project in session instead of replacing AvalonMongoDB with pymongo.Collection --- openpype/modules/kitsu/utils/sync_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 677d269bca..d93197a4bc 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -120,7 +120,7 @@ class Listener: # Write into DB if update_project: - self.dbcon = self.dbcon.database[project_name] + self.dbcon.Session["AVALON_PROJECT"] = project_name self.dbcon.bulk_write([update_project]) def _delete_project(self, data): From e2920ffdb53df5dd8d68b8800436f19edca2ff9c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:08:15 +0200 Subject: [PATCH 04/22] use query functions in sync service --- openpype/modules/kitsu/utils/sync_service.py | 29 +++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index d93197a4bc..38c1176df9 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -2,6 +2,10 @@ import os import gazu +from openpype.client import ( + get_project, + get_assets +) from openpype.pipeline import AvalonMongoDB from .credentials import validate_credentials from .update_op_with_zou import ( @@ -150,7 +154,8 @@ class Listener: def _update_asset(self, data): """Update asset into OP DB.""" set_op_project(self.dbcon, data["project_id"]) - project_doc = self.dbcon.find_one({"type": "project"}) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name) # Get gazu entity asset = gazu.asset.get_asset(data["asset_id"]) @@ -159,7 +164,7 @@ class Listener: # Query all assets of the local project zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in self.dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } zou_ids_and_asset_docs[asset["project_id"]] = project_doc @@ -199,7 +204,8 @@ class Listener: def _update_episode(self, data): """Update episode into OP DB.""" set_op_project(self.dbcon, data["project_id"]) - project_doc = self.dbcon.find_one({"type": "project"}) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name) # Get gazu entity episode = gazu.shot.get_episode(data["episode_id"]) @@ -208,7 +214,7 @@ class Listener: # Query all assets of the local project zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in self.dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } zou_ids_and_asset_docs[episode["project_id"]] = project_doc @@ -249,7 +255,8 @@ class Listener: def _update_sequence(self, data): """Update sequence into OP DB.""" set_op_project(self.dbcon, data["project_id"]) - project_doc = self.dbcon.find_one({"type": "project"}) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name) # Get gazu entity sequence = gazu.shot.get_sequence(data["sequence_id"]) @@ -258,7 +265,7 @@ class Listener: # Query all assets of the local project zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in self.dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } zou_ids_and_asset_docs[sequence["project_id"]] = project_doc @@ -299,7 +306,8 @@ class Listener: def _update_shot(self, data): """Update shot into OP DB.""" set_op_project(self.dbcon, data["project_id"]) - project_doc = self.dbcon.find_one({"type": "project"}) + project_name = self.dbcon.active_project() + project_doc = get_project(project_name) # Get gazu entity shot = gazu.shot.get_shot(data["shot_id"]) @@ -308,7 +316,7 @@ class Listener: # Query all assets of the local project zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in self.dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } zou_ids_and_asset_docs[shot["project_id"]] = project_doc @@ -359,10 +367,11 @@ class Listener: def _delete_task(self, data): """Delete task of OP DB.""" - set_op_project(self.dbcon, data["project_id"]) + set_op_project(self.dbcon, data["project_id"]) + project_name = self.dbcon.active_project() # Find asset doc - asset_docs = [doc for doc in self.dbcon.find({"type": "asset"})] + asset_docs = list(get_assets(project_name)) for doc in asset_docs: # Match task for name, task in doc["data"]["tasks"].items(): From d393d6c8956af8365afe5309780762740ff0205c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:09:30 +0200 Subject: [PATCH 05/22] a little bit more complicated way how to get asset matching zou id --- openpype/modules/kitsu/utils/sync_service.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 38c1176df9..3848eda7ae 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -343,14 +343,25 @@ class Listener: """Create new task into OP DB.""" # Get project entity set_op_project(self.dbcon, data["project_id"]) + project_name = self.dbcon.active_project() # Get gazu entity task = gazu.task.get_task(data["task_id"]) # Find asset doc - asset_doc = self.dbcon.find_one( - {"type": "asset", "data.zou.id": task["entity"]["id"]} + parent_name = task["entity"]["name"] + parent_zou_id = task["entity"]["id"] + asset_docs = get_assets( + project_name, + asset_names=[parent_name], + fields=["_id", "data.zou.id", "data.tasks"] ) + asset_doc = None + for _asset_doc in asset_docs: + doc_zou_id = _asset_doc.get("data", {}).get("zou", {}).get("id") + if doc_zou_id == parent_zou_id: + asset_doc = _asset_doc + break # Update asset tasks with new one asset_tasks = asset_doc["data"].get("tasks") From 20c2cedf75436e463cc6799ab1f3bcb579116abc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:09:47 +0200 Subject: [PATCH 06/22] use query functions in op to zou sync --- openpype/modules/kitsu/utils/update_zou_with_op.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index 81d421206f..b7bc418c98 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -6,6 +6,7 @@ from typing import List import gazu from pymongo import UpdateOne +from openpype.client import get_project from openpype.pipeline import AvalonMongoDB from openpype.api import get_project_settings from openpype.modules.kitsu.utils.credentials import validate_credentials @@ -53,9 +54,7 @@ def sync_zou_from_op_project( """ # Get project doc if not provided if not project_doc: - project_doc = dbcon.database[project_name].find_one( - {"type": "project"} - ) + project_doc = get_project(project_name) # Get all entities from zou print(f"Synchronizing {project_name}...") @@ -96,7 +95,7 @@ def sync_zou_from_op_project( dbcon.Session["AVALON_PROJECT"] = project_name asset_docs = { asset_doc["_id"]: asset_doc - for asset_doc in dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) } # Create new assets From 96863fa8aed63dee0b72f6a38e532525f5b6863d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:10:37 +0200 Subject: [PATCH 07/22] use query functions in zou to op sync --- .../modules/kitsu/utils/update_op_with_zou.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index c39d1c5e36..86dee3ce65 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -10,6 +10,12 @@ from gazu.task import ( all_tasks_for_shot, ) +from openpype.client import ( + get_project, + get_assets, + get_asset_by_id, + get_asset_by_name +) from openpype.pipeline import AvalonMongoDB from openpype.api import get_project_settings from openpype.lib import create_project @@ -85,9 +91,7 @@ def update_op_assets( if not item_doc: # Create asset op_asset = create_op_asset(item) insert_result = dbcon.insert_one(op_asset) - item_doc = dbcon.find_one( - {"type": "asset", "_id": insert_result.inserted_id} - ) + item_doc = get_asset_by_id(project_name, insert_result.inserted_id) # Update asset item_data = deepcopy(item_doc["data"]) @@ -235,7 +239,7 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne: UpdateOne: Update instance for the project """ project_name = project["name"] - project_doc = dbcon.database[project_name].find_one({"type": "project"}) + project_doc = get_project(project_name) if not project_doc: print(f"Creating project '{project_name}'") project_doc = create_project(project_name, project_name, dbcon=dbcon) @@ -332,19 +336,20 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): bulk_writes.append(write_project_to_op(project, dbcon)) # Try to find project document - dbcon.Session["AVALON_PROJECT"] = project["name"] - project_doc = dbcon.find_one({"type": "project"}) + project_name = project["name"] + dbcon.Session["AVALON_PROJECT"] = project_name + project_doc = get_project(project_name) # Query all assets of the local project zou_ids_and_asset_docs = { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in dbcon.find({"type": "asset"}) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou", {}).get("id") } zou_ids_and_asset_docs[project["id"]] = project_doc # Create entities root folders - project_module_settings = get_project_settings(project["name"])["kitsu"] + project_module_settings = get_project_settings(project_name)["kitsu"] for entity_type, root in project_module_settings["entities_root"].items(): parent_folders = root.split("/") direct_parent_doc = None @@ -384,7 +389,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): zou_ids_and_asset_docs.update( { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in dbcon.find({"type": "asset"}) + for asset_doc in get_assets(projec_name) if asset_doc["data"].get("zou") } ) From 767dc3dbaba27063289c6853c306acdda8493869 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:11:26 +0200 Subject: [PATCH 08/22] a little bit more complicated queries using zou id --- .../modules/kitsu/utils/update_op_with_zou.py | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 86dee3ce65..bf3705447c 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -179,14 +179,32 @@ def update_op_assets( ) if visual_parent_doc_id is None: # Find root folder doc - root_folder_doc = dbcon.find_one( - { - "type": "asset", - "name": entity_parent_folders[-1], - "data.root_of": substitute_item_type, - }, - ["_id"], + root_folder_docs = get_assets( + project_name, + asset_name=[entity_parent_folders[-1]], + fields=["_id", "data.root_of"] ) + # NOTE: Not sure why it's checking for entity type? + # OP3 does not support multiple assets with same names so type + # filtering is irelevant. + # This way mimics previous implementation: + # ``` + # root_folder_doc = dbcon.find_one( + # { + # "type": "asset", + # "name": entity_parent_folders[-1], + # "data.root_of": substitute_item_type, + # }, + # ["_id"], + # ) + # ``` + root_folder_doc = None + for folder_doc in root_folder_docs: + root_of = folder_doc.get("data", {}).get("root_of") + if root_of == substitute_item_type: + root_folder_doc = folder_doc + break + if root_folder_doc: visual_parent_doc_id = root_folder_doc["_id"] @@ -354,9 +372,26 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): parent_folders = root.split("/") direct_parent_doc = None for i, folder in enumerate(parent_folders, 1): - parent_doc = dbcon.find_one( - {"type": "asset", "name": folder, "data.root_of": entity_type} + parent_doc = get_asset_by_name( + project_name, folder, fields=["_id", "data.root_of"] ) + # NOTE: Not sure why it's checking for entity type? + # OP3 does not support multiple assets with same names so type + # filtering is irelevant. + # Also all of the entities could find be queried at once using + # 'get_assets'. + # This way mimics previous implementation: + # ``` + # parent_doc = dbcon.find_one( + # {"type": "asset", "name": folder, "data.root_of": entity_type} + # ) + # ``` + if ( + parent_doc + and parent_doc.get("data", {}).get("root_of") != entity_type + ): + parent_doc = None + if not parent_doc: direct_parent_doc = dbcon.insert_one( { From 2988af04ffcd1a42a4ca40fe4a2dbe80f163ffff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 16:57:49 +0200 Subject: [PATCH 09/22] added missing import --- openpype/modules/kitsu/utils/update_zou_with_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_zou_with_op.py b/openpype/modules/kitsu/utils/update_zou_with_op.py index b7bc418c98..57d7094e95 100644 --- a/openpype/modules/kitsu/utils/update_zou_with_op.py +++ b/openpype/modules/kitsu/utils/update_zou_with_op.py @@ -6,7 +6,7 @@ from typing import List import gazu from pymongo import UpdateOne -from openpype.client import get_project +from openpype.client import get_project, get_assets from openpype.pipeline import AvalonMongoDB from openpype.api import get_project_settings from openpype.modules.kitsu.utils.credentials import validate_credentials From 5e14a54d8248b7ddb3f02b778d89ca4a391f15c8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Jul 2022 17:02:54 +0200 Subject: [PATCH 10/22] fix typo --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index bf3705447c..f56a131b8e 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -424,7 +424,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): zou_ids_and_asset_docs.update( { asset_doc["data"]["zou"]["id"]: asset_doc - for asset_doc in get_assets(projec_name) + for asset_doc in get_assets(project_name) if asset_doc["data"].get("zou") } ) From e8f30eea9a8914ed09360d56bcd7b2a60d8367fc Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 12 Jul 2022 18:30:46 +0200 Subject: [PATCH 11/22] Added typing notation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Félix David --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index f56a131b8e..a68d6d31c3 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -39,7 +39,7 @@ def create_op_asset(gazu_entity: dict) -> dict: } -def get_kitsu_project_name(project_id: str): +def get_kitsu_project_name(project_id: str)->str: """Get project name based on project id in kitsu. Args: From a6c029d9fb1462054ca74817393beaebf32c7346 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 15 Jul 2022 10:13:32 +0200 Subject: [PATCH 12/22] skip validation of zou id --- openpype/modules/kitsu/utils/sync_service.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/openpype/modules/kitsu/utils/sync_service.py b/openpype/modules/kitsu/utils/sync_service.py index 3848eda7ae..441b95a7ec 100644 --- a/openpype/modules/kitsu/utils/sync_service.py +++ b/openpype/modules/kitsu/utils/sync_service.py @@ -4,7 +4,8 @@ import gazu from openpype.client import ( get_project, - get_assets + get_assets, + get_asset_by_name ) from openpype.pipeline import AvalonMongoDB from .credentials import validate_credentials @@ -350,18 +351,8 @@ class Listener: # Find asset doc parent_name = task["entity"]["name"] - parent_zou_id = task["entity"]["id"] - asset_docs = get_assets( - project_name, - asset_names=[parent_name], - fields=["_id", "data.zou.id", "data.tasks"] - ) - asset_doc = None - for _asset_doc in asset_docs: - doc_zou_id = _asset_doc.get("data", {}).get("zou", {}).get("id") - if doc_zou_id == parent_zou_id: - asset_doc = _asset_doc - break + + asset_doc = get_asset_by_name(project_name, parent_name) # Update asset tasks with new one asset_tasks = asset_doc["data"].get("tasks") From c210c93b325aaef0aac5f90fddbe5253a4702166 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 15 Jul 2022 10:15:32 +0200 Subject: [PATCH 13/22] changes from comments --- openpype/modules/kitsu/utils/update_op_with_zou.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index a68d6d31c3..4695a49159 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -39,14 +39,14 @@ def create_op_asset(gazu_entity: dict) -> dict: } -def get_kitsu_project_name(project_id: str)->str: +def get_kitsu_project_name(project_id: str) -> str: """Get project name based on project id in kitsu. Args: - project_id (str): Id of project in Kitsu. + project_id (str): UUID of project in Kitsu. Returns: - str: Project name which has project in Kitsu. + str: Name of Kitsu project. """ project = gazu.project.get_project(project_id) @@ -178,7 +178,7 @@ def update_op_assets( asset_doc_ids[parent_zou_id]["_id"] if parent_zou_id else None ) if visual_parent_doc_id is None: - # Find root folder doc + # Find root folder docs root_folder_docs = get_assets( project_name, asset_name=[entity_parent_folders[-1]], From c59a9cb05f641ccf8ec3638d69b1cf764cee9262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 19 Jul 2022 14:40:01 +0200 Subject: [PATCH 14/22] Fix: shot duplicate name using shot's hierarchy (ep, seq) --- openpype/modules/kitsu/utils/update_op_with_zou.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 4695a49159..f43bf07e25 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -181,7 +181,7 @@ def update_op_assets( # Find root folder docs root_folder_docs = get_assets( project_name, - asset_name=[entity_parent_folders[-1]], + asset_names=[entity_parent_folders[-1]], fields=["_id", "data.root_of"] ) # NOTE: Not sure why it's checking for entity type? @@ -221,6 +221,14 @@ def update_op_assets( parent_entity = parent_doc["data"]["zou"] parent_zou_id = parent_entity["parent_id"] + # Item name + if item_type == "Asset": + item_name = item_doc["name"] + elif item_type == "Shot": + # Name with parents hierarchy "({episode}_){sequence}_{shot}" + # to avoid duplicate name issue + item_name = "_".join(item_data["parents"] + [item_doc["name"]]) + # Set root folders parents item_data["parents"] = entity_parent_folders + item_data["parents"] @@ -234,7 +242,7 @@ def update_op_assets( item_doc["_id"], { "$set": { - "name": item["name"], + "name": item_name, "data": item_data, "parent": asset_doc_ids[item["project_id"]]["_id"], } From 9f41a512cb4ab27884ee2ad0465c23837f8ccad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 19 Jul 2022 14:44:25 +0200 Subject: [PATCH 15/22] black --- openpype/modules/kitsu/utils/update_op_with_zou.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index f43bf07e25..9a5dd7db61 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -14,7 +14,7 @@ from openpype.client import ( get_project, get_assets, get_asset_by_id, - get_asset_by_name + get_asset_by_name, ) from openpype.pipeline import AvalonMongoDB from openpype.api import get_project_settings @@ -182,7 +182,7 @@ def update_op_assets( root_folder_docs = get_assets( project_name, asset_names=[entity_parent_folders[-1]], - fields=["_id", "data.root_of"] + fields=["_id", "data.root_of"], ) # NOTE: Not sure why it's checking for entity type? # OP3 does not support multiple assets with same names so type @@ -224,7 +224,7 @@ def update_op_assets( # Item name if item_type == "Asset": item_name = item_doc["name"] - elif item_type == "Shot": + elif item_type == "Shot": # Name with parents hierarchy "({episode}_){sequence}_{shot}" # to avoid duplicate name issue item_name = "_".join(item_data["parents"] + [item_doc["name"]]) From 2bf3cf1b3094bb7402e7e3a8de55267fd4440edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Tue, 19 Jul 2022 14:54:36 +0200 Subject: [PATCH 16/22] Fix wrong name for sequence --- openpype/modules/kitsu/utils/update_op_with_zou.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 9a5dd7db61..b2f0caf52b 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -221,13 +221,12 @@ def update_op_assets( parent_entity = parent_doc["data"]["zou"] parent_zou_id = parent_entity["parent_id"] - # Item name - if item_type == "Asset": - item_name = item_doc["name"] - elif item_type == "Shot": + if item_type in ["Shot", "Sequence"]: # Name with parents hierarchy "({episode}_){sequence}_{shot}" # to avoid duplicate name issue item_name = "_".join(item_data["parents"] + [item_doc["name"]]) + else: + item_name = item_doc["name"] # Set root folders parents item_data["parents"] = entity_parent_folders + item_data["parents"] From 59395883170cac8ec8c11ca7b66ccd40d499cbc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 20 Jul 2022 17:30:40 +0200 Subject: [PATCH 17/22] Change: Asset is put under an AssetType folder --- .../modules/kitsu/utils/update_op_with_zou.py | 60 ++++++++++++------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 4695a49159..e0ff87adf7 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -14,7 +14,7 @@ from openpype.client import ( get_project, get_assets, get_asset_by_id, - get_asset_by_name + get_asset_by_name, ) from openpype.pipeline import AvalonMongoDB from openpype.api import get_project_settings @@ -154,17 +154,23 @@ def update_op_assets( parent_zou_id = substitute_parent_item["parent_id"] else: parent_zou_id = ( - item.get("parent_id") + # For Asset, put under asset type directory + item.get("entity_type_id") + if item_type == "Asset" + else None + # Else, fallback on usual hierarchy + or item.get("parent_id") or item.get("episode_id") or item.get("source_id") - ) # TODO check consistency + ) - # Substitute Episode and Sequence by Shot - substitute_item_type = ( - "shots" - if item_type in ["Episode", "Sequence"] - else f"{item_type.lower()}s" - ) + # Substitute item type for general classification (assets or shots) + if item_type in ["Asset", "AssetType"]: + substitute_item_type = "assets" + elif item_type in ["Episode", "Sequence"]: + substitute_item_type = "shots" + else: + substitute_item_type = f"{item_type.lower()}s" entity_parent_folders = [ f for f in project_module_settings["entities_root"] @@ -181,8 +187,8 @@ def update_op_assets( # Find root folder docs root_folder_docs = get_assets( project_name, - asset_name=[entity_parent_folders[-1]], - fields=["_id", "data.root_of"] + asset_names=[entity_parent_folders[-1]], + fields=["_id", "data.root_of"], ) # NOTE: Not sure why it's checking for entity type? # OP3 does not support multiple assets with same names so type @@ -219,7 +225,7 @@ def update_op_assets( # Get parent entity parent_entity = parent_doc["data"]["zou"] - parent_zou_id = parent_entity["parent_id"] + parent_zou_id = parent_entity.get("parent_id") # Set root folders parents item_data["parents"] = entity_parent_folders + item_data["parents"] @@ -236,7 +242,7 @@ def update_op_assets( "$set": { "name": item["name"], "data": item_data, - "parent": asset_doc_ids[item["project_id"]]["_id"], + "parent": project_doc["_id"], } }, ) @@ -327,6 +333,10 @@ def sync_all_projects(login: str, password: str): def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): """Update OP project in DB with Zou data. + `root_of` is meant to sort entities by type for a better readability in the data tree. It + puts all shot like (Shot and Episode and Sequence) and asset entities under two different root + folders or hierarchy, defined in settings. + Args: dbcon (AvalonMongoDB): MongoDB connection project (dict): Project dict got using gazu. @@ -341,12 +351,17 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): # Get all assets from zou all_assets = gazu.asset.all_assets_for_project(project) + all_asset_types = gazu.asset.all_asset_types_for_project(project) all_episodes = gazu.shot.all_episodes_for_project(project) all_seqs = gazu.shot.all_sequences_for_project(project) all_shots = gazu.shot.all_shots_for_project(project) all_entities = [ item - for item in all_assets + all_episodes + all_seqs + all_shots + for item in all_assets + + all_asset_types + + all_episodes + + all_seqs + + all_shots if naming_pattern.match(item["name"]) ] @@ -401,21 +416,20 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): "data": { "root_of": entity_type, "parents": parent_folders[:i], - "visualParent": direct_parent_doc, + "visualParent": direct_parent_doc.inserted_id + if direct_parent_doc + else None, "tasks": {}, }, } ) # Create - to_insert = [] - to_insert.extend( - [ - create_op_asset(item) - for item in all_entities - if item["id"] not in zou_ids_and_asset_docs.keys() - ] - ) + to_insert = [ + create_op_asset(item) + for item in all_entities + if item["id"] not in zou_ids_and_asset_docs.keys() + ] if to_insert: # Insert doc in DB dbcon.insert_many(to_insert) From 02cc2166c1ee165b48d697c876675302c8cb77db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 20 Jul 2022 17:37:19 +0200 Subject: [PATCH 18/22] docstring linting line length --- openpype/modules/kitsu/utils/update_op_with_zou.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index e0ff87adf7..cabf4e4d18 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -333,9 +333,9 @@ def sync_all_projects(login: str, password: str): def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): """Update OP project in DB with Zou data. - `root_of` is meant to sort entities by type for a better readability in the data tree. It - puts all shot like (Shot and Episode and Sequence) and asset entities under two different root - folders or hierarchy, defined in settings. + `root_of` is meant to sort entities by type for a better readability in + the data tree. It puts all shot like (Shot and Episode and Sequence) and + asset entities under two different root folders or hierarchy, defined in settings. Args: dbcon (AvalonMongoDB): MongoDB connection From a3144c9d75e3372e1d7a3008f54e2695c81a51d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 20 Jul 2022 17:40:16 +0200 Subject: [PATCH 19/22] docstring linting line length --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index cabf4e4d18..46e0fa38f3 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -333,7 +333,7 @@ def sync_all_projects(login: str, password: str): def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): """Update OP project in DB with Zou data. - `root_of` is meant to sort entities by type for a better readability in + `root_of` is meant to sort entities by type for a better readability in the data tree. It puts all shot like (Shot and Episode and Sequence) and asset entities under two different root folders or hierarchy, defined in settings. From af45aff844ab2ff28ccc76ec99d3e5a9803c92e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 20 Jul 2022 17:56:37 +0200 Subject: [PATCH 20/22] docstring linting line length --- openpype/modules/kitsu/utils/update_op_with_zou.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 46e0fa38f3..7262d2ee1a 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -335,7 +335,8 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): `root_of` is meant to sort entities by type for a better readability in the data tree. It puts all shot like (Shot and Episode and Sequence) and - asset entities under two different root folders or hierarchy, defined in settings. + asset entities under two different root folders or hierarchy, defined in + settings. Args: dbcon (AvalonMongoDB): MongoDB connection From 480d1968124ccf4fe2bc70b109145b016292fe25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20David?= Date: Wed, 20 Jul 2022 17:56:55 +0200 Subject: [PATCH 21/22] docstring linting line length --- openpype/modules/kitsu/utils/update_op_with_zou.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/kitsu/utils/update_op_with_zou.py b/openpype/modules/kitsu/utils/update_op_with_zou.py index 7262d2ee1a..7bfbd42f6a 100644 --- a/openpype/modules/kitsu/utils/update_op_with_zou.py +++ b/openpype/modules/kitsu/utils/update_op_with_zou.py @@ -335,7 +335,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict): `root_of` is meant to sort entities by type for a better readability in the data tree. It puts all shot like (Shot and Episode and Sequence) and - asset entities under two different root folders or hierarchy, defined in + asset entities under two different root folders or hierarchy, defined in settings. Args: From 92b611d76aabb35fefb3f34430a9679b5c9b28da Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 21 Jul 2022 09:58:17 +0200 Subject: [PATCH 22/22] Raise AttributeError instead of ImportError on missing attribute in 'openpype_interfaces' --- openpype/modules/base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index b9ccec13cc..1bd343fd07 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -49,6 +49,7 @@ class _ModuleClass(object): Object of this class can be stored to `sys.modules` and used for storing dynamically imported modules. """ + def __init__(self, name): # Call setattr on super class super(_ModuleClass, self).__setattr__("name", name) @@ -116,12 +117,13 @@ class _InterfacesClass(_ModuleClass): - this is because interfaces must be available even if are missing implementation """ + def __getattr__(self, attr_name): if attr_name not in self.__attributes__: if attr_name in ("__path__", "__file__"): return None - raise ImportError(( + raise AttributeError(( "cannot import name '{}' from 'openpype_interfaces'" ).format(attr_name))