From a9413b1ecf8694be012ce28dca5c7f738efe63f6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 5 Oct 2022 13:50:27 +0200 Subject: [PATCH 01/10] OP-4181 - changed legacy way to update database Operations used instead. --- openpype/client/operations.py | 43 +++++++ .../plugins/publish/integrate_hero_version.py | 115 +++++++++--------- 2 files changed, 98 insertions(+), 60 deletions(-) diff --git a/openpype/client/operations.py b/openpype/client/operations.py index 48e86457266..1f2727599c2 100644 --- a/openpype/client/operations.py +++ b/openpype/client/operations.py @@ -23,6 +23,7 @@ CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0" CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0" CURRENT_VERSION_SCHEMA = "openpype:version-3.0" +CURRENT_HERO_VERSION_SCHEMA = "openpype:hero_version-1.0" CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0" CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0" CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0" @@ -162,6 +163,34 @@ def new_version_doc(version, subset_id, data=None, entity_id=None): } +def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): + """Create skeleton data of hero version document. + + Args: + version_id (ObjectId): Is considered as unique identifier of version + under subset. + parent_id (Union[str, ObjectId]): Id of parent subset. + data (Dict[str, Any]): Version document data. + entity_id (Union[str, ObjectId]): Predefined id of document. New id is + created if not passed. + + Returns: + Dict[str, Any]: Skeleton of version document. + """ + + if data is None: + data = {} + + return { + "_id": _create_or_convert_to_mongo_id(entity_id), + "schema": CURRENT_HERO_VERSION_SCHEMA, + "type": "hero_version", + "version_id": version_id, + "parent": parent_id, + "data": data + } + + def new_representation_doc( name, version_id, context, data=None, entity_id=None ): @@ -293,6 +322,20 @@ def prepare_version_update_data(old_doc, new_doc, replace=True): return _prepare_update_data(old_doc, new_doc, replace) +def prepare_hero_version_update_data(old_doc, new_doc, replace=True): + """Compare two hero version documents and prepare update data. + + Based on compared values will create update data for 'UpdateOperation'. + + Empty output means that documents are identical. + + Returns: + Dict[str, Any]: Changes between old and new document. + """ + + return _prepare_update_data(old_doc, new_doc, replace) + + def prepare_representation_update_data(old_doc, new_doc, replace=True): """Compare two representation documents and prepare update data. diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index c0760a54714..26327ccc979 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -4,8 +4,6 @@ import errno import shutil -from bson.objectid import ObjectId -from pymongo import InsertOne, ReplaceOne import pyblish.api from openpype.client import ( @@ -14,10 +12,16 @@ get_archived_representations, get_representations, ) +from openpype.client.operations import ( + OperationsSession, + _create_or_convert_to_mongo_id, + new_hero_version_doc, + prepare_hero_version_update_data, + prepare_representation_update_data, +) from openpype.lib import create_hard_link from openpype.pipeline import ( - schema, - legacy_io, + schema ) from openpype.pipeline.publish import get_publish_template_name @@ -187,35 +191,29 @@ def integrate_instance( repre["name"].lower(): repre for repre in old_repres } - if old_version: - new_version_id = old_version["_id"] - else: - new_version_id = ObjectId() - - new_hero_version = { - "_id": new_version_id, - "version_id": src_version_entity["_id"], - "parent": src_version_entity["parent"], - "type": "hero_version", - "schema": "openpype:hero_version-1.0" - } - schema.validate(new_hero_version) + op_session = OperationsSession() - # Don't make changes in database until everything is O.K. - bulk_writes = [] + new_hero_version = new_hero_version_doc( + src_version_entity["_id"], + src_version_entity["parent"] + ) if old_version: self.log.debug("Replacing old hero version.") - bulk_writes.append( - ReplaceOne( - {"_id": new_hero_version["_id"]}, - new_hero_version - ) + new_hero_version["_id"] = old_version["_id"] + update_data = prepare_hero_version_update_data( + old_version, new_hero_version + ) + op_session.update_entity( + project_name, + new_hero_version["type"], + old_version["_id"], + update_data ) else: self.log.debug("Creating first hero version.") - bulk_writes.append( - InsertOne(new_hero_version) + op_session.create_entity( + project_name, new_hero_version["type"], new_hero_version ) # Separate old representations into `to replace` and `to delete` @@ -235,7 +233,7 @@ def integrate_instance( archived_repres = list(get_archived_representations( project_name, # Check what is type of archived representation - version_ids=[new_version_id] + version_ids=[new_hero_version["_id"]] )) archived_repres_by_name = {} for repre in archived_repres: @@ -382,12 +380,15 @@ def integrate_instance( # Replace current representation if repre_name_low in old_repres_to_replace: old_repre = old_repres_to_replace.pop(repre_name_low) + repre["_id"] = old_repre["_id"] - bulk_writes.append( - ReplaceOne( - {"_id": old_repre["_id"]}, - repre - ) + update_data = prepare_representation_update_data( + old_repre, repre) + op_session.update_entity( + project_name, + "representation", + old_repre["_id"], + update_data ) # Unarchive representation @@ -395,21 +396,21 @@ def integrate_instance( archived_repre = archived_repres_by_name.pop( repre_name_low ) - old_id = archived_repre["old_id"] - repre["_id"] = old_id - bulk_writes.append( - ReplaceOne( - {"old_id": old_id}, - repre - ) + repre["_id"] = archived_repre["old_id"] + update_data = prepare_representation_update_data( + archived_repre, repre) + op_session.update_entity( + project_name, + "representation", + archived_repre["_id"], + update_data ) # Create representation else: - repre["_id"] = ObjectId() - bulk_writes.append( - InsertOne(repre) - ) + repre["_id"] = _create_or_convert_to_mongo_id(None) + op_session.create_entity(project_name, "representation", + repre) self.path_checks = [] @@ -430,28 +431,22 @@ def integrate_instance( archived_repre = archived_repres_by_name.pop( repre_name_low ) - repre["old_id"] = repre["_id"] - repre["_id"] = archived_repre["_id"] - repre["type"] = archived_repre["type"] - bulk_writes.append( - ReplaceOne( - {"_id": archived_repre["_id"]}, - repre - ) - ) + changes["old_id"] = repre["_id"] + changes["_id"] = archived_repre["_id"] + changes["type"] = archived_repre["type"] + op_session.update_entity(project_name, + archived_repre["type"], + archived_repre["_id"], + changes) else: repre["old_id"] = repre["_id"] - repre["_id"] = ObjectId() + repre["_id"] = _create_or_convert_to_mongo_id(None) repre["type"] = "archived_representation" - bulk_writes.append( - InsertOne(repre) - ) + op_session.create_entity(project_name, "representation", + repre) - if bulk_writes: - legacy_io.database[project_name].bulk_write( - bulk_writes - ) + op_session.commit() # Remove backuped previous hero if ( From 5c401fb17ff893420137bfbd872997fea954d857 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 5 Oct 2022 14:13:52 +0200 Subject: [PATCH 02/10] OP-4181 - Hound --- openpype/plugins/publish/integrate_hero_version.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 26327ccc979..adc629352e2 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -432,9 +432,9 @@ def integrate_instance( repre_name_low ) - changes["old_id"] = repre["_id"] - changes["_id"] = archived_repre["_id"] - changes["type"] = archived_repre["type"] + changes = {"old_id": repre["_id"], + "_id": archived_repre["_id"], + "type": archived_repre["type"]} op_session.update_entity(project_name, archived_repre["type"], archived_repre["_id"], From 640971a49bf2398a5216b0083d018cf794cc4bcf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 13:40:37 +0200 Subject: [PATCH 03/10] OP-4181 - modified signature of new_hero_version_doc --- openpype/client/operations.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/client/operations.py b/openpype/client/operations.py index 1f2727599c2..fd639c34a7c 100644 --- a/openpype/client/operations.py +++ b/openpype/client/operations.py @@ -163,13 +163,13 @@ def new_version_doc(version, subset_id, data=None, entity_id=None): } -def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): +def new_hero_version_doc(version_id, subset_id, data=None, entity_id=None): """Create skeleton data of hero version document. Args: version_id (ObjectId): Is considered as unique identifier of version under subset. - parent_id (Union[str, ObjectId]): Id of parent subset. + subset_id (Union[str, ObjectId]): Id of parent subset. data (Dict[str, Any]): Version document data. entity_id (Union[str, ObjectId]): Predefined id of document. New id is created if not passed. @@ -186,7 +186,7 @@ def new_hero_version_doc(version_id, parent_id, data=None, entity_id=None): "schema": CURRENT_HERO_VERSION_SCHEMA, "type": "hero_version", "version_id": version_id, - "parent": parent_id, + "parent": subset_id, "data": data } From a7150bd6f1c9494734f03265eecbe86ff284d882 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:28:30 +0200 Subject: [PATCH 04/10] OP-4181 - clean up after review comments --- igniter/GPUCache/data_0 | Bin 0 -> 8192 bytes igniter/GPUCache/data_1 | Bin 0 -> 270336 bytes igniter/GPUCache/data_2 | Bin 0 -> 8192 bytes igniter/GPUCache/data_3 | Bin 0 -> 8192 bytes igniter/GPUCache/index | Bin 0 -> 262512 bytes openpype/hooks/pre_python2_prelaunch.py | 35 + openpype/hosts/photoshop/tests/expr.py | 51 + openpype/lib/token | 1 + .../event_handlers_user/action_edl_create.py | 275 ++++ openpype/pipeline/temp_anatomy.py | 1330 +++++++++++++++++ .../plugins/publish/integrate_hero_version.py | 9 +- .../_process_referenced_pipeline_result.json | 92 ++ tests/unit/test_unzip.py | 11 + vendor/configs/OpenColorIO-Configs | 1 + vendor/instance.json | 1133 ++++++++++++++ vendor/response.json | 1 + vendor/temp.json | 46 + 17 files changed, 2982 insertions(+), 3 deletions(-) create mode 100644 igniter/GPUCache/data_0 create mode 100644 igniter/GPUCache/data_1 create mode 100644 igniter/GPUCache/data_2 create mode 100644 igniter/GPUCache/data_3 create mode 100644 igniter/GPUCache/index create mode 100644 openpype/hooks/pre_python2_prelaunch.py create mode 100644 openpype/hosts/photoshop/tests/expr.py create mode 100644 openpype/lib/token create mode 100644 openpype/modules/ftrack/event_handlers_user/action_edl_create.py create mode 100644 openpype/pipeline/temp_anatomy.py create mode 100644 tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json create mode 100644 tests/unit/test_unzip.py create mode 160000 vendor/configs/OpenColorIO-Configs create mode 100644 vendor/instance.json create mode 100644 vendor/response.json create mode 100644 vendor/temp.json diff --git a/igniter/GPUCache/data_0 b/igniter/GPUCache/data_0 new file mode 100644 index 0000000000000000000000000000000000000000..d76fb77e93ac8a536b5dbade616d63abd00626c5 GIT binary patch literal 8192 zcmeIuK?wjL5Jka{7-jo+5O1auw}mk8@B+*}b0s6M>Kg$91PBlyK!5-N0t5&UAV7cs W0RjXF5FkK+009C72oNCfo4^Gh&;oe? literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_1 b/igniter/GPUCache/data_1 new file mode 100644 index 0000000000000000000000000000000000000000..212f73166781160e472f8e76c3b9998b3775ecb7 GIT binary patch literal 270336 zcmeI%u?@m75CFhW@CfV>3QP3t%>amw0a8XffrJVo)0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N c0t5&UAV7cs0RjXF5FkK+009C72>hqO2eI!7!2kdN literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_2 b/igniter/GPUCache/data_2 new file mode 100644 index 0000000000000000000000000000000000000000..c7e2eb9adcfb2d3313ec85f5c28cedda950a3f9b GIT binary patch literal 8192 zcmeIu!3h8`2n0b1_TQ7_m#U&=2(t%Qz}%M=ae7_Oi2wlt1PBlyK!5-N0t5&UAV7cs V0RjXF5FkK+009C72oTsN@Bv`}0$Tt8 literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/data_3 b/igniter/GPUCache/data_3 new file mode 100644 index 0000000000000000000000000000000000000000..5eec97358cf550862fd343fc9a73c159d4c0ab10 GIT binary patch literal 8192 zcmeIuK@9*P5CpLeAOQbv2)|PW$RO!FMnHFsm9+HS=9>r*AV7cs0RjXF5FkK+009C7 W2oNAZfB*pk1PBlyK!5;&-vkZ-dID$w literal 0 HcmV?d00001 diff --git a/igniter/GPUCache/index b/igniter/GPUCache/index new file mode 100644 index 0000000000000000000000000000000000000000..b2998cfef1a6457e5cfe9dc37e029bdbe0a7f778 GIT binary patch literal 262512 zcmeIuu?>JQ00XcT9zZ-&b?3`o&{Gf_S0S;K0~nnt$>{4|&t%Cr+dIlg%Diho_EzWC z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjYm G5qJP2>jZZI literal 0 HcmV?d00001 diff --git a/openpype/hooks/pre_python2_prelaunch.py b/openpype/hooks/pre_python2_prelaunch.py new file mode 100644 index 00000000000..84272d2e5d7 --- /dev/null +++ b/openpype/hooks/pre_python2_prelaunch.py @@ -0,0 +1,35 @@ +import os +from openpype.lib import PreLaunchHook + + +class PrePython2Vendor(PreLaunchHook): + """Prepend python 2 dependencies for py2 hosts.""" + order = 10 + + def execute(self): + if not self.application.use_python_2: + return + + # Prepare vendor dir path + self.log.info("adding global python 2 vendor") + pype_root = os.getenv("OPENPYPE_REPOS_ROOT") + python_2_vendor = os.path.join( + pype_root, + "openpype", + "vendor", + "python", + "python_2" + ) + + # Add Python 2 modules + python_paths = [ + python_2_vendor + ] + + # Load PYTHONPATH from current launch context + python_path = self.launch_context.env.get("PYTHONPATH") + if python_path: + python_paths.append(python_path) + + # Set new PYTHONPATH to launch context environments + self.launch_context.env["PYTHONPATH"] = os.pathsep.join(python_paths) diff --git a/openpype/hosts/photoshop/tests/expr.py b/openpype/hosts/photoshop/tests/expr.py new file mode 100644 index 00000000000..ff796f417c3 --- /dev/null +++ b/openpype/hosts/photoshop/tests/expr.py @@ -0,0 +1,51 @@ +import json + +data = [ + { + "schema": "openpype:container-2.0", + "id": "pyblish.avalon.container", + "name": "imageArtNeew", + "namespace": "Jungle_imageArtNeew_001", + "loader": "ReferenceLoader", + "representation": "61c1eb91e1a4d1e5a23582f6", + "members": [ + "131" + ] + }, + { + "id": "pyblish.avalon.instance", + "family": "image", + "asset": "Jungle", + "subset": "imageMainBg", + "active": True, + "variant": "Main", + "uuid": "199", + "long_name": "BG" + }, + { + "id": "pyblish.avalon.instance", + "family": "image", + "asset": "Jungle", + "subset": "imageMain", + "active": True, + "variant": "Main", + "uuid": "192", + "long_name": "imageMain" + }, + { + "id": "pyblish.avalon.instance", + "family": "workfile", + "subset": "workfile", + "active": True, + "creator_identifier": "workfile", + "asset": "Jungle", + "task": "art", + "variant": "", + "instance_id": "3ed19342-cd8e-4bb6-8cda-d6e74d9a7efe", + "creator_attributes": {}, + "publish_attributes": {} + } +] + +with open("C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\openpype\\hosts\\photoshop\\tests\\mock_get_layers_metadata.json", 'w') as fp: + fp.write(json.dumps(data, indent=4)) \ No newline at end of file diff --git a/openpype/lib/token b/openpype/lib/token new file mode 100644 index 00000000000..193a2aac957 --- /dev/null +++ b/openpype/lib/token @@ -0,0 +1 @@ +5d58370a7702b2efee5120704246baf4abb865323fc9db9a04827bfb478569d6 \ No newline at end of file diff --git a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py new file mode 100644 index 00000000000..7ac139ae630 --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py @@ -0,0 +1,275 @@ +import os +import subprocess +import tempfile +import shutil +import json +import sys + +import opentimelineio as otio +import ftrack_api +import requests + +from openpype_modules.ftrack.lib import BaseAction + + +def download_file(url, path): + with open(path, "wb") as f: + print("\nDownloading %s" % path) + response = requests.get(url, stream=True) + total_length = response.headers.get('content-length') + + if total_length is None: + f.write(response.content) + else: + dl = 0 + total_length = int(total_length) + for data in response.iter_content(chunk_size=4096): + dl += len(data) + f.write(data) + done = int(50 * dl / total_length) + sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done))) + sys.stdout.flush() + + +class ExportEditorialAction(BaseAction): + '''Export Editorial action''' + + label = "Export Editorial" + variant = None + identifier = "export-editorial" + description = None + component_name_order = ["exr", "mov", "ftrackreview-mp4_src"] + + def export_editorial(self, entity, output_path): + session = ftrack_api.Session() + unmanaged_location = session.query( + "Location where name is \"ftrack.unmanaged\"" + ).one() + temp_path = tempfile.mkdtemp() + + files = {} + for obj in entity["review_session_objects"]: + data = {} + parent_name = obj["asset_version"]["asset"]["parent"]["name"] + component_query = "Component where version_id is \"{}\"" + component_query += " and name is \"{}\"" + for name in self.component_name_order: + try: + component = session.query( + component_query.format( + obj["asset_version"]["id"], name + ) + ).one() + path = unmanaged_location.get_filesystem_path(component) + data["path"] = path.replace("\\", "/") + break + except ftrack_api.exception.NoResultFoundError: + pass + + # Download online review if not local path found. + if "path" not in data: + component = session.query( + component_query.format( + obj["asset_version"]["id"], "ftrackreview-mp4" + ) + ).one() + location = component["component_locations"][0] + component_url = location["location"].get_url(component) + asset_name = obj["asset_version"]["asset"]["name"] + version = obj["asset_version"]["version"] + filename = "{}_{}_v{:03d}.mp4".format( + parent_name, asset_name, version + ) + filepath = os.path.join( + output_path, "downloads", filename + ).replace("\\", "/") + + if not os.path.exists(os.path.dirname(filepath)): + os.makedirs(os.path.dirname(filepath)) + + download_file(component_url, filepath) + data["path"] = filepath + + # Get frame duration and framerate. + query = "Component where version_id is \"{}\"" + query += " and name is \"ftrackreview-mp4\"" + component = session.query( + query.format(obj["asset_version"]["id"]) + ).one() + metadata = json.loads(component["metadata"]["ftr_meta"]) + data["framerate"] = metadata["frameRate"] + data["frames"] = metadata["frameOut"] - metadata["frameIn"] + + # Find audio if it exists. + query = "Asset where parent.id is \"{}\"" + query += " and type.name is \"Audio\"" + asset = session.query( + query.format(obj["asset_version"]["asset"]["parent"]["id"]) + ) + if asset: + asset_version = asset[0]["versions"][-1] + query = "Component where version_id is \"{}\"" + query += " and name is \"{}\"" + comp = session.query( + query.format(asset_version["id"], "wav") + ).one() + src = unmanaged_location.get_filesystem_path(comp) + dst = os.path.join(temp_path, parent_name + ".wav") + shutil.copy(src, dst) + + # Collect data. + files[parent_name] = data + + clips = [] + for name, data in files.items(): + self.log.info("Processing {} with {}".format(name, data)) + f = data["path"] + range = otio.opentime.TimeRange( + start_time=otio.opentime.RationalTime(0, data["framerate"]), + duration=otio.opentime.RationalTime( + data["frames"], data["framerate"] + ) + ) + + media_reference = otio.schema.ExternalReference( + available_range=range, + target_url=f"file://{f}" + ) + + clip = otio.schema.Clip( + name=name, + media_reference=media_reference, + source_range=range + ) + clips.append(clip) + + # path = os.path.join(temp_path, name + ".wav").replace("\\", "/") + # if not os.path.exists(path): + # args = ["ffmpeg", "-y", "-i", f, path] + # self.log.info(subprocess.list2cmdline(args)) + # subprocess.call(args) + + timeline = otio.schema.timeline_from_clips(clips) + otio.adapters.write_to_file( + timeline, os.path.join(output_path, entity["name"] + ".xml") + ) + + data = "" + for f in os.listdir(temp_path): + f = f.replace("\\", "/") + data += f"file '{f}'\n" + + path = os.path.join(temp_path, "temp.txt") + with open(path, "w") as f: + f.write(data) + + args = [ + "ffmpeg", "-y", "-f", "concat", "-safe", "0", + "-i", os.path.basename(path), + os.path.join(output_path, entity["name"] + ".wav") + ] + self.log.info(subprocess.list2cmdline(args)) + subprocess.call(args, cwd=temp_path) + + shutil.rmtree(temp_path) + + def discover(self, session, entities, event): + '''Return true if we can handle the selected entities. + *session* is a `ftrack_api.Session` instance + *entities* is a list of tuples each containing the entity type and the + entity id. + If the entity is a hierarchical you will always get the entity + type TypedContext, once retrieved through a get operation you + will have the "real" entity type ie. example Shot, Sequence + or Asset Build. + *event* the unmodified original event + ''' + if len(entities) == 1: + if entities[0].entity_type == "ReviewSession": + return True + + return False + + def launch(self, session, entities, event): + '''Callback method for the custom action. + return either a bool ( True if successful or False if the action + failed ) or a dictionary with they keys `message` and `success`, the + message should be a string and will be displayed as feedback to the + user, success should be a bool, True if successful or False if the + action failed. + *session* is a `ftrack_api.Session` instance + *entities* is a list of tuples each containing the entity type and the + entity id. + If the entity is a hierarchical you will always get the entity + type TypedContext, once retrieved through a get operation you + will have the "real" entity type ie. example Shot, Sequence + or Asset Build. + *event* the unmodified original event + ''' + if 'values' in event['data']: + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + job = session.create( + 'Job', + { + 'user': user, + 'status': 'running', + 'data': json.dumps({ + 'description': 'Export Editorial.' + }) + } + ) + session.commit() + + try: + output_path = event["data"]["values"]["output_path"] + + if not os.path.exists(output_path): + os.makedirs(output_path) + + self.export_editorial(entities[0], output_path) + + job['status'] = 'done' + session.commit() + except Exception: + session.rollback() + job["status"] = "failed" + session.commit() + self.log.error( + "Exporting editorial failed ({})", exc_info=True + ) + + return { + 'success': True, + 'message': 'Action completed successfully' + } + + items = [ + { + 'label': 'Output folder:', + 'type': 'text', + 'value': '', + 'name': 'output_path' + } + + ] + return { + 'success': True, + 'message': "", + 'items': items + } + + +def register(session): + '''Register action. Called when used as an event plugin.''' + + ExportEditorialAction(session).register() + + +if __name__ == "__main__": + session = ftrack_api.Session() + action = ExportEditorialAction(session) + id = "bfe0477c-d5a8-49d8-88b9-6d44d2e48fd9" + review_session = session.get("ReviewSession", id) + path = r"c:/projects" + action.export_editorial(review_session, path) \ No newline at end of file diff --git a/openpype/pipeline/temp_anatomy.py b/openpype/pipeline/temp_anatomy.py new file mode 100644 index 00000000000..27a9370928a --- /dev/null +++ b/openpype/pipeline/temp_anatomy.py @@ -0,0 +1,1330 @@ +import os +import re +import copy +import platform +import collections +import numbers + +import six +import time + +from openpype.settings.lib import ( + get_anatomy_settings, + get_project_settings, + get_default_project_settings, + get_local_settings +) + +from openpype.client import get_project +from openpype.lib.path_templates import ( + TemplateUnsolved, + TemplateResult, + TemplatesDict, + FormatObject, +) +from openpype.lib.log import Logger +from openpype.lib import get_local_site_id + +log = Logger.get_logger(__name__) + + +class ProjectNotSet(Exception): + """Exception raised when is created Anatomy without project name.""" + + +class RootCombinationError(Exception): + """This exception is raised when templates has combined root types.""" + + def __init__(self, roots): + joined_roots = ", ".join( + ["\"{}\"".format(_root) for _root in roots] + ) + # TODO better error message + msg = ( + "Combination of root with and" + " without root name in AnatomyTemplates. {}" + ).format(joined_roots) + + super(RootCombinationError, self).__init__(msg) + + +class BaseAnatomy(object): + """Anatomy module helps to keep project settings. + + Wraps key project specifications, AnatomyTemplates and Roots. + """ + + def __init__(self, project_doc, local_settings): + project_name = project_doc["name"] + self.project_name = project_name + + self._data = self._prepare_anatomy_data( + project_doc, local_settings + ) + self._templates_obj = AnatomyTemplates(self) + self._roots_obj = Roots(self) + + root_key_regex = re.compile(r"{(root?[^}]+)}") + root_name_regex = re.compile(r"root\[([^]]+)\]") + + # Anatomy used as dictionary + # - implemented only getters returning copy + def __getitem__(self, key): + return copy.deepcopy(self._data[key]) + + def get(self, key, default=None): + return copy.deepcopy(self._data).get(key, default) + + def keys(self): + return copy.deepcopy(self._data).keys() + + def values(self): + return copy.deepcopy(self._data).values() + + def items(self): + return copy.deepcopy(self._data).items() + + @staticmethod + def _prepare_anatomy_data(anatomy_data): + """Prepare anatomy data for further processing. + + Method added to replace `{task}` with `{task[name]}` in templates. + """ + templates_data = anatomy_data.get("templates") + if templates_data: + # Replace `{task}` with `{task[name]}` in templates + value_queue = collections.deque() + value_queue.append(templates_data) + while value_queue: + item = value_queue.popleft() + if not isinstance(item, dict): + continue + + for key in tuple(item.keys()): + value = item[key] + if isinstance(value, dict): + value_queue.append(value) + + elif isinstance(value, six.string_types): + item[key] = value.replace("{task}", "{task[name]}") + return anatomy_data + + def reset(self): + """Reset values of cached data in templates and roots objects.""" + self._data = self._prepare_anatomy_data( + get_anatomy_settings(self.project_name, self._site_name) + ) + self.templates_obj.reset() + self.roots_obj.reset() + + @property + def templates(self): + """Wrap property `templates` of Anatomy's AnatomyTemplates instance.""" + return self._templates_obj.templates + + @property + def templates_obj(self): + """Return `AnatomyTemplates` object of current Anatomy instance.""" + return self._templates_obj + + def format(self, *args, **kwargs): + """Wrap `format` method of Anatomy's `templates_obj`.""" + return self._templates_obj.format(*args, **kwargs) + + def format_all(self, *args, **kwargs): + """Wrap `format_all` method of Anatomy's `templates_obj`.""" + return self._templates_obj.format_all(*args, **kwargs) + + @property + def roots(self): + """Wrap `roots` property of Anatomy's `roots_obj`.""" + return self._roots_obj.roots + + @property + def roots_obj(self): + """Return `Roots` object of current Anatomy instance.""" + return self._roots_obj + + def root_environments(self): + """Return OPENPYPE_ROOT_* environments for current project in dict.""" + return self._roots_obj.root_environments() + + def root_environmets_fill_data(self, template=None): + """Environment variable values in dictionary for rootless path. + + Args: + template (str): Template for environment variable key fill. + By default is set to `"${}"`. + """ + return self.roots_obj.root_environmets_fill_data(template) + + def find_root_template_from_path(self, *args, **kwargs): + """Wrapper for Roots `find_root_template_from_path`.""" + return self.roots_obj.find_root_template_from_path(*args, **kwargs) + + def path_remapper(self, *args, **kwargs): + """Wrapper for Roots `path_remapper`.""" + return self.roots_obj.path_remapper(*args, **kwargs) + + def all_root_paths(self): + """Wrapper for Roots `all_root_paths`.""" + return self.roots_obj.all_root_paths() + + def set_root_environments(self): + """Set OPENPYPE_ROOT_* environments for current project.""" + self._roots_obj.set_root_environments() + + def root_names(self): + """Return root names for current project.""" + return self.root_names_from_templates(self.templates) + + def _root_keys_from_templates(self, data): + """Extract root key from templates in data. + + Args: + data (dict): Data that may contain templates as string. + + Return: + set: Set of all root names from templates as strings. + + Output example: `{"root[work]", "root[publish]"}` + """ + + output = set() + if isinstance(data, dict): + for value in data.values(): + for root in self._root_keys_from_templates(value): + output.add(root) + + elif isinstance(data, str): + for group in re.findall(self.root_key_regex, data): + output.add(group) + + return output + + def root_value_for_template(self, template): + """Returns value of root key from template.""" + root_templates = [] + for group in re.findall(self.root_key_regex, template): + root_templates.append("{" + group + "}") + + if not root_templates: + return None + + return root_templates[0].format(**{"root": self.roots}) + + def root_names_from_templates(self, templates): + """Extract root names form anatomy templates. + + Returns None if values in templates contain only "{root}". + Empty list is returned if there is no "root" in templates. + Else returns all root names from templates in list. + + RootCombinationError is raised when templates contain both root types, + basic "{root}" and with root name specification "{root[work]}". + + Args: + templates (dict): Anatomy templates where roots are not filled. + + Return: + list/None: List of all root names from templates as strings when + multiroot setup is used, otherwise None is returned. + """ + roots = list(self._root_keys_from_templates(templates)) + # Return empty list if no roots found in templates + if not roots: + return roots + + # Raise exception when root keys have roots with and without root name. + # Invalid output example: ["root", "root[project]", "root[render]"] + if len(roots) > 1 and "root" in roots: + raise RootCombinationError(roots) + + # Return None if "root" without root name in templates + if len(roots) == 1 and roots[0] == "root": + return None + + names = set() + for root in roots: + for group in re.findall(self.root_name_regex, root): + names.add(group) + return list(names) + + def fill_root(self, template_path): + """Fill template path where is only "root" key unfilled. + + Args: + template_path (str): Path with "root" key in. + Example path: "{root}/projects/MyProject/Shot01/Lighting/..." + + Return: + str: formatted path + """ + # NOTE does not care if there are different keys than "root" + return template_path.format(**{"root": self.roots}) + + @classmethod + def fill_root_with_path(cls, rootless_path, root_path): + """Fill path without filled "root" key with passed path. + + This is helper to fill root with different directory path than anatomy + has defined no matter if is single or multiroot. + + Output path is same as input path if `rootless_path` does not contain + unfilled root key. + + Args: + rootless_path (str): Path without filled "root" key. Example: + "{root[work]}/MyProject/..." + root_path (str): What should replace root key in `rootless_path`. + + Returns: + str: Path with filled root. + """ + output = str(rootless_path) + for group in re.findall(cls.root_key_regex, rootless_path): + replacement = "{" + group + "}" + output = output.replace(replacement, root_path) + + return output + + def replace_root_with_env_key(self, filepath, template=None): + """Replace root of path with environment key. + + # Example: + ## Project with roots: + ``` + { + "nas": { + "windows": P:/projects", + ... + } + ... + } + ``` + + ## Entered filepath + "P:/projects/project/asset/task/animation_v001.ma" + + ## Entered template + "<{}>" + + ## Output + "/project/asset/task/animation_v001.ma" + + Args: + filepath (str): Full file path where root should be replaced. + template (str): Optional template for environment key. Must + have one index format key. + Default value if not entered: "${}" + + Returns: + str: Path where root is replaced with environment root key. + + Raise: + ValueError: When project's roots were not found in entered path. + """ + success, rootless_path = self.find_root_template_from_path(filepath) + if not success: + raise ValueError( + "{}: Project's roots were not found in path: {}".format( + self.project_name, filepath + ) + ) + + data = self.root_environmets_fill_data(template) + return rootless_path.format(**data) + + +class Anatomy(BaseAnatomy): + _project_cache = {} + + def __init__(self, project_name=None, site_name=None): + if not project_name: + project_name = os.environ.get("AVALON_PROJECT") + + if not project_name: + raise ProjectNotSet(( + "Implementation bug: Project name is not set. Anatomy requires" + " to load data for specific project." + )) + + self._site_name = site_name + project_info = self.get_project_data_and_cache(project_name, site_name) + + super(Anatomy, self).__init__( + project_info["project_doc"], + project_info["local_settings"] + ) + + @classmethod + def get_project_data_and_cache(cls, project_name, site_name): + project_info = cls._project_cache.get(project_name) + if project_info is not None: + if time.time() - project_info["start"] > 10: + cls._project_cache.pop(project_name) + project_info = None + + if project_info is None: + if site_name is None: + if project_name: + project_settings = get_project_settings(project_name) + else: + project_settings = get_default_project_settings() + site_name = ( + project_settings["global"] + ["sync_server"] + ["config"] + ["active_site"] + ) + if site_name == "local": + site_name = get_local_site_id() + + project_info = { + "project_doc": get_project(project_name), + "local_settings": get_local_settings(site_name), + "site_name": site_name, + "start": time.time() + } + cls._project_cache[project_name] = project_info + + return project_info + + def reset(self): + """Reset values of cached data in templates and roots objects.""" + self._data = self._prepare_anatomy_data( + get_anatomy_settings(self.project_name, self._site_name) + ) + self.templates_obj.reset() + self.roots_obj.reset() + + +class AnatomyTemplateUnsolved(TemplateUnsolved): + """Exception for unsolved template when strict is set to True.""" + + msg = "Anatomy template \"{0}\" is unsolved.{1}{2}" + + +class AnatomyTemplateResult(TemplateResult): + rootless = None + + def __new__(cls, result, rootless_path): + new_obj = super(AnatomyTemplateResult, cls).__new__( + cls, + str(result), + result.template, + result.solved, + result.used_values, + result.missing_keys, + result.invalid_types + ) + new_obj.rootless = rootless_path + return new_obj + + def validate(self): + if not self.solved: + raise AnatomyTemplateUnsolved( + self.template, + self.missing_keys, + self.invalid_types + ) + + def copy(self): + tmp = TemplateResult( + str(self), + self.template, + self.solved, + self.used_values, + self.missing_keys, + self.invalid_types + ) + return self.__class__(tmp, self.rootless) + + def normalized(self): + """Convert to normalized path.""" + + tmp = TemplateResult( + os.path.normpath(self), + self.template, + self.solved, + self.used_values, + self.missing_keys, + self.invalid_types + ) + return self.__class__(tmp, self.rootless) + + +class AnatomyTemplates(TemplatesDict): + inner_key_pattern = re.compile(r"(\{@.*?[^{}0]*\})") + inner_key_name_pattern = re.compile(r"\{@(.*?[^{}0]*)\}") + + def __init__(self, anatomy): + super(AnatomyTemplates, self).__init__() + self.anatomy = anatomy + self.loaded_project = None + + def __getitem__(self, key): + return self.templates[key] + + def get(self, key, default=None): + return self.templates.get(key, default) + + def reset(self): + self._raw_templates = None + self._templates = None + self._objected_templates = None + + @property + def project_name(self): + return self.anatomy.project_name + + @property + def roots(self): + return self.anatomy.roots + + @property + def templates(self): + self._validate_discovery() + return self._templates + + @property + def objected_templates(self): + self._validate_discovery() + return self._objected_templates + + def _validate_discovery(self): + if self.project_name != self.loaded_project: + self.reset() + + if self._templates is None: + self._discover() + self.loaded_project = self.project_name + + def _format_value(self, value, data): + if isinstance(value, RootItem): + return self._solve_dict(value, data) + + result = super(AnatomyTemplates, self)._format_value(value, data) + if isinstance(result, TemplateResult): + rootless_path = self._rootless_path(result, data) + result = AnatomyTemplateResult(result, rootless_path) + return result + + def set_templates(self, templates): + if not templates: + self.reset() + return + + self._raw_templates = copy.deepcopy(templates) + templates = copy.deepcopy(templates) + v_queue = collections.deque() + v_queue.append(templates) + while v_queue: + item = v_queue.popleft() + if not isinstance(item, dict): + continue + + for key in tuple(item.keys()): + value = item[key] + if isinstance(value, dict): + v_queue.append(value) + + elif ( + isinstance(value, six.string_types) + and "{task}" in value + ): + item[key] = value.replace("{task}", "{task[name]}") + + solved_templates = self.solve_template_inner_links(templates) + self._templates = solved_templates + self._objected_templates = self.create_ojected_templates( + solved_templates + ) + + def default_templates(self): + """Return default templates data with solved inner keys.""" + return self.solve_template_inner_links( + self.anatomy["templates"] + ) + + def _discover(self): + """ Loads anatomy templates from yaml. + Default templates are loaded if project is not set or project does + not have set it's own. + TODO: create templates if not exist. + + Returns: + TemplatesResultDict: Contain templates data for current project of + default templates. + """ + + if self.project_name is None: + # QUESTION create project specific if not found? + raise AssertionError(( + "Project \"{0}\" does not have his own templates." + " Trying to use default." + ).format(self.project_name)) + + self.set_templates(self.anatomy["templates"]) + + @classmethod + def replace_inner_keys(cls, matches, value, key_values, key): + """Replacement of inner keys in template values.""" + for match in matches: + anatomy_sub_keys = ( + cls.inner_key_name_pattern.findall(match) + ) + if key in anatomy_sub_keys: + raise ValueError(( + "Unsolvable recursion in inner keys, " + "key: \"{}\" is in his own value." + " Can't determine source, please check Anatomy templates." + ).format(key)) + + for anatomy_sub_key in anatomy_sub_keys: + replace_value = key_values.get(anatomy_sub_key) + if replace_value is None: + raise KeyError(( + "Anatomy templates can't be filled." + " Anatomy key `{0}` has" + " invalid inner key `{1}`." + ).format(key, anatomy_sub_key)) + + if not ( + isinstance(replace_value, numbers.Number) + or isinstance(replace_value, six.string_types) + ): + raise ValueError(( + "Anatomy templates can't be filled." + " Anatomy key `{0}` has" + " invalid inner key `{1}`" + " with value `{2}`." + ).format(key, anatomy_sub_key, str(replace_value))) + + value = value.replace(match, str(replace_value)) + + return value + + @classmethod + def prepare_inner_keys(cls, key_values): + """Check values of inner keys. + + Check if inner key exist in template group and has valid value. + It is also required to avoid infinite loop with unsolvable recursion + when first inner key's value refers to second inner key's value where + first is used. + """ + keys_to_solve = set(key_values.keys()) + while True: + found = False + for key in tuple(keys_to_solve): + value = key_values[key] + + if isinstance(value, six.string_types): + matches = cls.inner_key_pattern.findall(value) + if not matches: + keys_to_solve.remove(key) + continue + + found = True + key_values[key] = cls.replace_inner_keys( + matches, value, key_values, key + ) + continue + + elif not isinstance(value, dict): + keys_to_solve.remove(key) + continue + + subdict_found = False + for _key, _value in tuple(value.items()): + matches = cls.inner_key_pattern.findall(_value) + if not matches: + continue + + subdict_found = True + found = True + key_values[key][_key] = cls.replace_inner_keys( + matches, _value, key_values, + "{}.{}".format(key, _key) + ) + + if not subdict_found: + keys_to_solve.remove(key) + + if not found: + break + + return key_values + + @classmethod + def solve_template_inner_links(cls, templates): + """Solve templates inner keys identified by "{@*}". + + Process is split into 2 parts. + First is collecting all global keys (keys in top hierarchy where value + is not dictionary). All global keys are set for all group keys (keys + in top hierarchy where value is dictionary). Value of a key is not + overridden in group if already contain value for the key. + + In second part all keys with "at" symbol in value are replaced with + value of the key afterward "at" symbol from the group. + + Args: + templates (dict): Raw templates data. + + Example: + templates:: + key_1: "value_1", + key_2: "{@key_1}/{filling_key}" + + group_1: + key_3: "value_3/{@key_2}" + + group_2: + key_2": "value_2" + key_4": "value_4/{@key_2}" + + output:: + key_1: "value_1" + key_2: "value_1/{filling_key}" + + group_1: { + key_1: "value_1" + key_2: "value_1/{filling_key}" + key_3: "value_3/value_1/{filling_key}" + + group_2: { + key_1: "value_1" + key_2: "value_2" + key_4: "value_3/value_2" + """ + default_key_values = templates.pop("defaults", {}) + for key, value in tuple(templates.items()): + if isinstance(value, dict): + continue + default_key_values[key] = templates.pop(key) + + # Pop "others" key before before expected keys are processed + other_templates = templates.pop("others") or {} + + keys_by_subkey = {} + for sub_key, sub_value in templates.items(): + key_values = {} + key_values.update(default_key_values) + key_values.update(sub_value) + keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) + + for sub_key, sub_value in other_templates.items(): + if sub_key in keys_by_subkey: + log.warning(( + "Key \"{}\" is duplicated in others. Skipping." + ).format(sub_key)) + continue + + key_values = {} + key_values.update(default_key_values) + key_values.update(sub_value) + keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) + + default_keys_by_subkeys = cls.prepare_inner_keys(default_key_values) + + for key, value in default_keys_by_subkeys.items(): + keys_by_subkey[key] = value + + return keys_by_subkey + + def _dict_to_subkeys_list(self, subdict, pre_keys=None): + if pre_keys is None: + pre_keys = [] + output = [] + for key in subdict: + value = subdict[key] + result = list(pre_keys) + result.append(key) + if isinstance(value, dict): + for item in self._dict_to_subkeys_list(value, result): + output.append(item) + else: + output.append(result) + return output + + def _keys_to_dicts(self, key_list, value): + if not key_list: + return None + if len(key_list) == 1: + return {key_list[0]: value} + return {key_list[0]: self._keys_to_dicts(key_list[1:], value)} + + def _rootless_path(self, result, final_data): + used_values = result.used_values + missing_keys = result.missing_keys + template = result.template + invalid_types = result.invalid_types + if ( + "root" not in used_values + or "root" in missing_keys + or "{root" not in template + ): + return + + for invalid_type in invalid_types: + if "root" in invalid_type: + return + + root_keys = self._dict_to_subkeys_list({"root": used_values["root"]}) + if not root_keys: + return + + output = str(result) + for used_root_keys in root_keys: + if not used_root_keys: + continue + + used_value = used_values + root_key = None + for key in used_root_keys: + used_value = used_value[key] + if root_key is None: + root_key = key + else: + root_key += "[{}]".format(key) + + root_key = "{" + root_key + "}" + output = output.replace(str(used_value), root_key) + + return output + + def format(self, data, strict=True): + copy_data = copy.deepcopy(data) + roots = self.roots + if roots: + copy_data["root"] = roots + result = super(AnatomyTemplates, self).format(copy_data) + result.strict = strict + return result + + def format_all(self, in_data, only_keys=True): + """ Solves templates based on entered data. + + Args: + data (dict): Containing keys to be filled into template. + + Returns: + TemplatesResultDict: Output `TemplateResult` have `strict` + attribute set to False so accessing unfilled keys in templates + won't raise any exceptions. + """ + return self.format(in_data, strict=False) + + +class RootItem(FormatObject): + """Represents one item or roots. + + Holds raw data of root item specification. Raw data contain value + for each platform, but current platform value is used when object + is used for formatting of template. + + Args: + root_raw_data (dict): Dictionary containing root values by platform + names. ["windows", "linux" and "darwin"] + name (str, optional): Root name which is representing. Used with + multi root setup otherwise None value is expected. + parent_keys (list, optional): All dictionary parent keys. Values of + `parent_keys` are used for get full key which RootItem is + representing. Used for replacing root value in path with + formattable key. e.g. parent_keys == ["work"] -> {root[work]} + parent (object, optional): It is expected to be `Roots` object. + Value of `parent` won't affect code logic much. + """ + + def __init__( + self, root_raw_data, name=None, parent_keys=None, parent=None + ): + lowered_platform_keys = {} + for key, value in root_raw_data.items(): + lowered_platform_keys[key.lower()] = value + self.raw_data = lowered_platform_keys + self.cleaned_data = self._clean_roots(lowered_platform_keys) + self.name = name + self.parent_keys = parent_keys or [] + self.parent = parent + + self.available_platforms = list(lowered_platform_keys.keys()) + self.value = lowered_platform_keys.get(platform.system().lower()) + self.clean_value = self.clean_root(self.value) + + def __format__(self, *args, **kwargs): + return self.value.__format__(*args, **kwargs) + + def __str__(self): + return str(self.value) + + def __repr__(self): + return self.__str__() + + def __getitem__(self, key): + if isinstance(key, numbers.Number): + return self.value[key] + + additional_info = "" + if self.parent and self.parent.project_name: + additional_info += " for project \"{}\"".format( + self.parent.project_name + ) + + raise AssertionError( + "Root key \"{}\" is missing{}.".format( + key, additional_info + ) + ) + + def full_key(self): + """Full key value for dictionary formatting in template. + + Returns: + str: Return full replacement key for formatting. This helps when + multiple roots are set. In that case e.g. `"root[work]"` is + returned. + """ + if not self.name: + return "root" + + joined_parent_keys = "".join( + ["[{}]".format(key) for key in self.parent_keys] + ) + return "root{}".format(joined_parent_keys) + + def clean_path(self, path): + """Just replace backslashes with forward slashes.""" + return str(path).replace("\\", "/") + + def clean_root(self, root): + """Makes sure root value does not end with slash.""" + if root: + root = self.clean_path(root) + while root.endswith("/"): + root = root[:-1] + return root + + def _clean_roots(self, raw_data): + """Clean all values of raw root item values.""" + cleaned = {} + for key, value in raw_data.items(): + cleaned[key] = self.clean_root(value) + return cleaned + + def path_remapper(self, path, dst_platform=None, src_platform=None): + """Remap path for specific platform. + + Args: + path (str): Source path which need to be remapped. + dst_platform (str, optional): Specify destination platform + for which remapping should happen. + src_platform (str, optional): Specify source platform. This is + recommended to not use and keep unset until you really want + to use specific platform. + roots (dict/RootItem/None, optional): It is possible to remap + path with different roots then instance where method was + called has. + + Returns: + str/None: When path does not contain known root then + None is returned else returns remapped path with "{root}" + or "{root[]}". + """ + cleaned_path = self.clean_path(path) + if dst_platform: + dst_root_clean = self.cleaned_data.get(dst_platform) + if not dst_root_clean: + key_part = "" + full_key = self.full_key() + if full_key != "root": + key_part += "\"{}\" ".format(full_key) + + log.warning( + "Root {}miss platform \"{}\" definition.".format( + key_part, dst_platform + ) + ) + return None + + if cleaned_path.startswith(dst_root_clean): + return cleaned_path + + if src_platform: + src_root_clean = self.cleaned_data.get(src_platform) + if src_root_clean is None: + log.warning( + "Root \"{}\" miss platform \"{}\" definition.".format( + self.full_key(), src_platform + ) + ) + return None + + if not cleaned_path.startswith(src_root_clean): + return None + + subpath = cleaned_path[len(src_root_clean):] + if dst_platform: + # `dst_root_clean` is used from upper condition + return dst_root_clean + subpath + return self.clean_value + subpath + + result, template = self.find_root_template_from_path(path) + if not result: + return None + + def parent_dict(keys, value): + if not keys: + return value + + key = keys.pop(0) + return {key: parent_dict(keys, value)} + + if dst_platform: + format_value = parent_dict(list(self.parent_keys), dst_root_clean) + else: + format_value = parent_dict(list(self.parent_keys), self.value) + + return template.format(**{"root": format_value}) + + def find_root_template_from_path(self, path): + """Replaces known root value with formattable key in path. + + All platform values are checked for this replacement. + + Args: + path (str): Path where root value should be found. + + Returns: + tuple: Tuple contain 2 values: `success` (bool) and `path` (str). + When success it True then path should contain replaced root + value with formattable key. + + Example: + When input path is:: + "C:/windows/path/root/projects/my_project/file.ext" + + And raw data of item looks like:: + { + "windows": "C:/windows/path/root", + "linux": "/mount/root" + } + + Output will be:: + (True, "{root}/projects/my_project/file.ext") + + If any of raw data value wouldn't match path's root output is:: + (False, "C:/windows/path/root/projects/my_project/file.ext") + """ + result = False + output = str(path) + + root_paths = list(self.cleaned_data.values()) + mod_path = self.clean_path(path) + for root_path in root_paths: + # Skip empty paths + if not root_path: + continue + + if mod_path.startswith(root_path): + result = True + replacement = "{" + self.full_key() + "}" + output = replacement + mod_path[len(root_path):] + break + + return (result, output) + + +class Roots: + """Object which should be used for formatting "root" key in templates. + + Args: + anatomy Anatomy: Anatomy object created for a specific project. + """ + + env_prefix = "OPENPYPE_PROJECT_ROOT" + roots_filename = "roots.json" + + def __init__(self, anatomy): + self.anatomy = anatomy + self.loaded_project = None + self._roots = None + + def __format__(self, *args, **kwargs): + return self.roots.__format__(*args, **kwargs) + + def __getitem__(self, key): + return self.roots[key] + + def reset(self): + """Reset current roots value.""" + self._roots = None + + def path_remapper( + self, path, dst_platform=None, src_platform=None, roots=None + ): + """Remap path for specific platform. + + Args: + path (str): Source path which need to be remapped. + dst_platform (str, optional): Specify destination platform + for which remapping should happen. + src_platform (str, optional): Specify source platform. This is + recommended to not use and keep unset until you really want + to use specific platform. + roots (dict/RootItem/None, optional): It is possible to remap + path with different roots then instance where method was + called has. + + Returns: + str/None: When path does not contain known root then + None is returned else returns remapped path with "{root}" + or "{root[]}". + """ + if roots is None: + roots = self.roots + + if roots is None: + raise ValueError("Roots are not set. Can't find path.") + + if "{root" in path: + path = path.format(**{"root": roots}) + # If `dst_platform` is not specified then return else continue. + if not dst_platform: + return path + + if isinstance(roots, RootItem): + return roots.path_remapper(path, dst_platform, src_platform) + + for _root in roots.values(): + result = self.path_remapper( + path, dst_platform, src_platform, _root + ) + if result is not None: + return result + + def find_root_template_from_path(self, path, roots=None): + """Find root value in entered path and replace it with formatting key. + + Args: + path (str): Source path where root will be searched. + roots (Roots/dict, optional): It is possible to use different + roots than instance where method was triggered has. + + Returns: + tuple: Output contains tuple with bool representing success as + first value and path with or without replaced root with + formatting key as second value. + + Raises: + ValueError: When roots are not entered and can't be loaded. + """ + if roots is None: + log.debug( + "Looking for matching root in path \"{}\".".format(path) + ) + roots = self.roots + + if roots is None: + raise ValueError("Roots are not set. Can't find path.") + + if isinstance(roots, RootItem): + return roots.find_root_template_from_path(path) + + for root_name, _root in roots.items(): + success, result = self.find_root_template_from_path(path, _root) + if success: + log.info("Found match in root \"{}\".".format(root_name)) + return success, result + + log.warning("No matching root was found in current setting.") + return (False, path) + + def set_root_environments(self): + """Set root environments for current project.""" + for key, value in self.root_environments().items(): + os.environ[key] = value + + def root_environments(self): + """Use root keys to create unique keys for environment variables. + + Concatenates prefix "OPENPYPE_ROOT" with root keys to create unique + keys. + + Returns: + dict: Result is `{(str): (str)}` dicitonary where key represents + unique key concatenated by keys and value is root value of + current platform root. + + Example: + With raw root values:: + "work": { + "windows": "P:/projects/work", + "linux": "/mnt/share/projects/work", + "darwin": "/darwin/path/work" + }, + "publish": { + "windows": "P:/projects/publish", + "linux": "/mnt/share/projects/publish", + "darwin": "/darwin/path/publish" + } + + Result on windows platform:: + { + "OPENPYPE_ROOT_WORK": "P:/projects/work", + "OPENPYPE_ROOT_PUBLISH": "P:/projects/publish" + } + + Short example when multiroot is not used:: + { + "OPENPYPE_ROOT": "P:/projects" + } + """ + return self._root_environments() + + def all_root_paths(self, roots=None): + """Return all paths for all roots of all platforms.""" + if roots is None: + roots = self.roots + + output = [] + if isinstance(roots, RootItem): + for value in roots.raw_data.values(): + output.append(value) + return output + + for _roots in roots.values(): + output.extend(self.all_root_paths(_roots)) + return output + + def _root_environments(self, keys=None, roots=None): + if not keys: + keys = [] + if roots is None: + roots = self.roots + + if isinstance(roots, RootItem): + key_items = [self.env_prefix] + for _key in keys: + key_items.append(_key.upper()) + + key = "_".join(key_items) + # Make sure key and value does not contain unicode + # - can happen in Python 2 hosts + return {str(key): str(roots.value)} + + output = {} + for _key, _value in roots.items(): + _keys = list(keys) + _keys.append(_key) + output.update(self._root_environments(_keys, _value)) + return output + + def root_environmets_fill_data(self, template=None): + """Environment variable values in dictionary for rootless path. + + Args: + template (str): Template for environment variable key fill. + By default is set to `"${}"`. + """ + if template is None: + template = "${}" + return self._root_environmets_fill_data(template) + + def _root_environmets_fill_data(self, template, keys=None, roots=None): + if keys is None and roots is None: + return { + "root": self._root_environmets_fill_data( + template, [], self.roots + ) + } + + if isinstance(roots, RootItem): + key_items = [Roots.env_prefix] + for _key in keys: + key_items.append(_key.upper()) + key = "_".join(key_items) + return template.format(key) + + output = {} + for key, value in roots.items(): + _keys = list(keys) + _keys.append(key) + output[key] = self._root_environmets_fill_data( + template, _keys, value + ) + return output + + @property + def project_name(self): + """Return project name which will be used for loading root values.""" + return self.anatomy.project_name + + @property + def roots(self): + """Property for filling "root" key in templates. + + This property returns roots for current project or default root values. + Warning: + Default roots value may cause issues when project use different + roots settings. That may happen when project use multiroot + templates but default roots miss their keys. + """ + if self.project_name != self.loaded_project: + self._roots = None + + if self._roots is None: + self._roots = self._discover() + self.loaded_project = self.project_name + return self._roots + + def _discover(self): + """ Loads current project's roots or default. + + Default roots are loaded if project override's does not contain roots. + + Returns: + `RootItem` or `dict` with multiple `RootItem`s when multiroot + setting is used. + """ + + return self._parse_dict(self.anatomy["roots"], parent=self) + + @staticmethod + def _parse_dict(data, key=None, parent_keys=None, parent=None): + """Parse roots raw data into RootItem or dictionary with RootItems. + + Converting raw roots data to `RootItem` helps to handle platform keys. + This method is recursive to be able handle multiroot setup and + is static to be able to load default roots without creating new object. + + Args: + data (dict): Should contain raw roots data to be parsed. + key (str, optional): Current root key. Set by recursion. + parent_keys (list): Parent dictionary keys. Set by recursion. + parent (Roots, optional): Parent object set in `RootItem` + helps to keep RootItem instance updated with `Roots` object. + + Returns: + `RootItem` or `dict` with multiple `RootItem`s when multiroot + setting is used. + """ + if not parent_keys: + parent_keys = [] + is_last = False + for value in data.values(): + if isinstance(value, six.string_types): + is_last = True + break + + if is_last: + return RootItem(data, key, parent_keys, parent=parent) + + output = {} + for _key, value in data.items(): + _parent_keys = list(parent_keys) + _parent_keys.append(_key) + output[_key] = Roots._parse_dict(value, _key, _parent_keys, parent) + return output diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index adc629352e2..661975993b5 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,7 +14,6 @@ ) from openpype.client.operations import ( OperationsSession, - _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -193,9 +192,13 @@ def integrate_instance( op_session = OperationsSession() + entity_id = None + if old_version: + entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"] + src_version_entity["parent"], + entity_id=entity_id ) if old_version: @@ -408,7 +411,7 @@ def integrate_instance( # Create representation else: - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre.pop("_id", None) op_session.create_entity(project_name, "representation", repre) diff --git a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json new file mode 100644 index 00000000000..fb798524bc8 --- /dev/null +++ b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json @@ -0,0 +1,92 @@ +[ + { + "_id": { + "$oid": "623c9d53db3f5046eb1ad5f4" + }, + "schema": "openpype:version-3.0", + "type": "version", + "parent": { + "$oid": "5f3e439a30a9464d6c181cbc" + }, + "name": 94, + "data": { + "families": [ + "workfile" + ], + "time": "20220324T173254Z", + "author": "petrk", + "source": "C:/projects_local/petr_test/assets/locations/Jungle/work/art/petr_test_Jungle_art_v009.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": 25.0, + "intent": "-", + "inputLinks": [ + { + "type": "reference", + "id": { + "$oid": "618eb14f0a55a9c1591e913c" + }, + "linkedBy": "publish" + } + ] + }, + "outputs_recursive": [ + { + "_id": { + "$oid": "618eb14f0a55a9c1591e913c" + }, + "schema": "openpype:version-3.0", + "type": "version", + "parent": { + "$oid": "618e42a72ff49bd543bc1768" + }, + "name": 8, + "data": { + "families": [ + "image" + ], + "time": "20211112T192359Z", + "author": "petrk", + "source": "C:/projects_local/petr_test/assets/locations/Town/work/art/petr_test_Town_art_v005.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": 25.0, + "intent": "-", + "inputLinks": [ + { + "type": "reference", + "id": { + "$oid": "5f3cd2d530a94638544837c3" + }, + "linkedBy": "publish" + } + ] + }, + "depth": 0 + }, + { + "_id": { + "$oid": "5f3cd2d530a94638544837c3" + }, + "schema": "pype:version-3.0", + "type": "version", + "parent": { + "$oid": "5f3a714030a9464bfc7d2382" + }, + "name": 7, + "data": { + "families": [ + "image" + ], + "time": "20200819T092032Z", + "author": "petrk", + "source": "/c/projects/petr_test/assets/characters/Hero/work/art/Hero_v019.psd", + "comment": "", + "machine": "LAPTOP-UB778LHG", + "fps": null + }, + "depth": 1 + } + ] + } +] \ No newline at end of file diff --git a/tests/unit/test_unzip.py b/tests/unit/test_unzip.py new file mode 100644 index 00000000000..586fc49b6fa --- /dev/null +++ b/tests/unit/test_unzip.py @@ -0,0 +1,11 @@ + +from openpype.hosts.harmony.api.lib import _ZipFile +from pathlib import Path + +def test_zip(): + source = "c:/Users/petrk/Downloads/fbb_fbb100_sh0020_workfileAnimation_v010.zip" + dest = "c:/projects/temp/unzipped_with_python_111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\\2222222222222222222222222222222222222222222222222222222222222222222222222222222222" + + dest = Path(dest) + with _ZipFile(source, "r") as zip_ref: + zip_ref.extractall(dest.as_posix()) \ No newline at end of file diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs new file mode 160000 index 00000000000..0bb079c08be --- /dev/null +++ b/vendor/configs/OpenColorIO-Configs @@ -0,0 +1 @@ +Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 diff --git a/vendor/instance.json b/vendor/instance.json new file mode 100644 index 00000000000..b1d623e85d7 --- /dev/null +++ b/vendor/instance.json @@ -0,0 +1,1133 @@ +{ + 'family': 'render', + 'name': 'renderLightingDefault', + 'label': 'renderLightingDefault - local', + 'version': 1, + 'time': '', + 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', + 'subset': 'renderLightingDefault', + 'asset': 'shot01', + 'attachTo': False, + 'setMembers': '', + 'publish': True, + 'resolutionWidth': 1920.0, + 'resolutionHeight': 1080.0, + 'pixelAspect': 1, + 'frameStart': 0, + 'frameEnd': 0, + 'frameStep': 1, + 'handleStart': 0, + 'handleEnd': 0, + 'ignoreFrameHandleCheck': False, + 'renderer': 'aerender', + 'review': True, + 'priority': 50, + 'families': [ + 'render', + 'review', + 'ftrack', + 'slack' + ], + 'multipartExr': False, + 'convertToScanline': False, + 'tileRendering': False, + 'tilesX': 0, + 'tilesY': 0, + 'toBeRenderedOn': 'deadline', + 'deadlineSubmissionJob': None, + 'anatomyData': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-' + }, + 'outputDir': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001', + 'comp_name': '℗ renderLightingDefault', + 'comp_id': 1, + 'fps': 25, + 'projectEntity': { + '_id': ObjectId( + '5f2a6d2311e06a9818a1958b' + ), + 'name': 'petr_test', + 'created_d': datetime.datetime(2020, + 9, + 17, + 15, + 27, + 27, + 927000), + 'data': { + 'ftrackId': 'e5eda2bc-d682-11ea-afc1-92591a5b5e3e', + 'entityType': 'Project', + 'applications': [ + 'maya_2019', + 'photoshop_2021', + 'photoshop_2022', + 'harmony_17', + 'aftereffects_2022', + 'harmony_20', + 'nukestudio_12.2', + 'nukex_12.2', + 'hiero_12.2', + 'blender_2.93' + ], + 'library_project': True, + 'clipIn': 1, + 'resolutionWidth': 1920.0, + 'handleEnd': 0, + 'frameEnd': 1001, + 'resolutionHeight': 1080.0, + 'frameStart': 1001.0, + 'pixelAspect': 1.0, + 'fps': 25.0, + 'handleStart': 0, + 'clipOut': 1, + 'tools_env': [], + 'code': 'petr_test', + 'active': True + }, + 'type': 'project', + 'config': { + 'apps': [ + { + 'name': 'aftereffects/2022' + }, + { + 'name': 'maya/2019' + }, + { + 'name': 'hiero/12-2' + }, + { + 'name': 'photoshop/2021' + }, + { + 'name': 'nuke/12-2' + }, + { + 'name': 'photoshop/2022' + } + ], + 'tasks': { + 'Layout': { + 'short_name': 'lay' + }, + 'Setdress': { + 'short_name': 'dress' + }, + 'Previz': { + 'short_name': '' + }, + 'Generic': { + 'short_name': 'gener' + }, + 'Animation': { + 'short_name': 'anim' + }, + 'Modeling': { + 'short_name': 'mdl' + }, + 'Lookdev': { + 'short_name': 'look' + }, + 'FX': { + 'short_name': 'fx' + }, + 'Lighting': { + 'short_name': 'lgt' + }, + 'Compositing': { + 'short_name': 'comp' + }, + 'Tracking': { + 'short_name': '' + }, + 'Rigging': { + 'short_name': 'rig' + }, + 'Paint': { + 'short_name': 'paint' + }, + 'schedulle': { + 'short_name': '' + }, + 'Art': { + 'short_name': 'art' + }, + 'Texture': { + 'short_name': 'tex' + }, + 'Edit': { + 'short_name': 'edit' + } + }, + 'imageio': { + 'hiero': { + 'workfile': { + 'ocioConfigName': 'nuke-default', + 'ocioconfigpath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'workingSpace': 'linear', + 'sixteenBitLut': 'sRGB', + 'eightBitLut': 'sRGB', + 'floatLut': 'linear', + 'logLut': 'Cineon', + 'viewerLut': 'sRGB', + 'thumbnailLut': 'sRGB' + }, + 'regexInputs': { + 'inputs': [ + { + 'regex': '[^-a-zA-Z0-9](plateRef).*(?=mp4)', + 'colorspace': 'sRGB' + } + ] + } + }, + 'nuke': { + 'viewer': { + 'viewerProcess': 'sRGB' + }, + 'baking': { + 'viewerProcess': 'rec709' + }, + 'workfile': { + 'colorManagement': 'Nuke', + 'OCIO_config': 'nuke-default', + 'customOCIOConfigPath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'workingSpaceLUT': 'linear', + 'monitorLut': 'sRGB', + 'int8Lut': 'sRGB', + 'int16Lut': 'sRGB', + 'logLut': 'Cineon', + 'floatLut': 'linear' + }, + 'nodes': { + 'requiredNodes': [ + { + 'plugins': [ + 'CreateWriteRender' + ], + 'nukeNodeClass': 'Write', + 'knobs': [ + { + 'name': 'file_type', + 'value': 'exr' + }, + { + 'name': 'datatype', + 'value': '16 bit half' + }, + { + 'name': 'compression', + 'value': 'Zip (1 scanline)' + }, + { + 'name': 'autocrop', + 'value': 'True' + }, + { + 'name': 'tile_color', + 'value': '0xff0000ff' + }, + { + 'name': 'channels', + 'value': 'rgb' + }, + { + 'name': 'colorspace', + 'value': 'linear' + }, + { + 'name': 'create_directories', + 'value': 'True' + } + ] + }, + { + 'plugins': [ + 'CreateWritePrerender' + ], + 'nukeNodeClass': 'Write', + 'knobs': [ + { + 'name': 'file_type', + 'value': 'exr' + }, + { + 'name': 'datatype', + 'value': '16 bit half' + }, + { + 'name': 'compression', + 'value': 'Zip (1 scanline)' + }, + { + 'name': 'autocrop', + 'value': 'False' + }, + { + 'name': 'tile_color', + 'value': '0xadab1dff' + }, + { + 'name': 'channels', + 'value': 'rgb' + }, + { + 'name': 'colorspace', + 'value': 'linear' + }, + { + 'name': 'create_directories', + 'value': 'True' + } + ] + } + ], + 'customNodes': [] + }, + 'regexInputs': { + 'inputs': [ + { + 'regex': '[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]', + 'colorspace': 'linear' + } + ] + } + }, + 'maya': { + 'colorManagementPreference': { + 'configFilePath': { + 'windows': [], + 'darwin': [], + 'linux': [] + }, + 'renderSpace': 'scene-linear Rec 709/sRGB', + 'viewTransform': 'sRGB gamma' + } + } + }, + 'roots': { + 'work': { + 'windows': 'C:/projects', + 'darwin': '/Volumes/path', + 'linux': '/mnt/share/projects' + } + }, + 'templates': { + 'defaults': { + 'version_padding': 3, + 'version': 'v{version:0>{@version_padding}}', + 'frame_padding': 4, + 'frame': '{frame:0>{@frame_padding}}' + }, + 'work': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task}', + 'file': '{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'render': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', + 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'publish': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', + 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}', + 'path': '{@folder}/{@file}', + 'thumbnail': '{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}' + }, + 'hero': { + 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/hero', + 'file': '{project[code]}_{asset}_{subset}_hero<_{output}><.{frame}>.{ext}', + 'path': '{@folder}/{@file}' + }, + 'delivery': {}, + 'others': {} + } + }, + 'parent': None, + 'schema': 'avalon-core:project-2.0' + }, + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'frameStartHandle': 0, + 'frameEndHandle': 0, + 'byFrameStep': 1, + 'author': 'petrk', + 'expectedFiles': [ + 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001\\shot01_renderLightingDefault_v001.mov' + ], + 'slack_channel_message_profiles': [ + { + 'channels': [ + 'test_integration' + ], + 'upload_thumbnail': True, + 'message': 'Test message' + } + ], + 'slack_token': 'xoxb-1494100953104-2176825439264-jGqvQzfq9uZJPmyX5Q4o4TnP', + 'representations': [ + { + 'frameStart': 0, + 'frameEnd': 0, + 'name': 'mov', + 'ext': 'mov', + 'files': ' renderLightingDefault.mov', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'review' + ], + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' + ] + }, + { + 'name': 'thumbnail', + 'ext': 'jpg', + 'files': 'thumbnail.jpg', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'thumbnail' + ], + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' + ] + }, + { + 'frameStart': 0, + 'frameEnd': 0, + 'name': 'h264_mp4', + 'ext': 'mp4', + 'files': ' renderLightingDefault_h264burnin.mp4', + 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', + 'tags': [ + 'review', + 'burnin', + 'ftrackreview' + ], + 'resolutionWidth': 1920, + 'resolutionHeight': 1080, + 'outputName': 'h264', + 'outputDef': { + 'ext': 'mp4', + 'tags': [ + 'burnin', + 'ftrackreview' + ], + 'burnins': [], + 'ffmpeg_args': { + 'video_filters': [], + 'audio_filters': [], + 'input': [ + '-apply_trc gamma22' + ], + 'output': [ + '-pix_fmt yuv420p', + '-crf 18', + '-intra' + ] + }, + 'filter': { + 'families': [ + 'render', + 'review', + 'ftrack' + ] + }, + 'overscan_crop': '', + 'overscan_color': [ + 0, + 0, + 0, + 255 + ], + 'width': 0, + 'height': 0, + 'bg_color': [ + 0, + 0, + 0, + 0 + ], + 'letter_box': { + 'enabled': False, + 'ratio': 0.0, + 'state': 'letterbox', + 'fill_color': [ + 0, + 0, + 0, + 255 + ], + 'line_thickness': 0, + 'line_color': [ + 255, + 0, + 0, + 255 + ] + }, + 'filename_suffix': 'h264' + }, + 'frameStartFtrack': 0, + 'frameEndFtrack': 0, + 'ffmpeg_cmd': 'C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\vendor\\bin\\ffmpeg\\windows\\bin\\ffmpeg -apply_trc gamma22 -i "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault.mov" -pix_fmt yuv420p -crf 18 -intra -y "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264.mp4"', + 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'publishedFiles': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + } + ], + 'assetEntity': { + '_id': ObjectId( + '5fabee9730a94666449245b7' + ), + 'name': 'shot01', + 'data': { + 'ftrackId': '0c5f548c-2425-11eb-b203-628b111fac3c', + 'entityType': 'Shot', + 'clipIn': 1, + 'resolutionWidth': 1920.0, + 'handleEnd': 0.0, + 'frameEnd': 1001, + 'resolutionHeight': 1080.0, + 'frameStart': 1001.0, + 'pixelAspect': 1.0, + 'fps': 25.0, + 'handleStart': 0.0, + 'clipOut': 1, + 'tools_env': [], + 'avalon_mongo_id': '5fabee9730a94666449245b7', + 'parents': [ + 'sequences', + 'seq01' + ], + 'hierarchy': 'sequences\\seq01', + 'tasks': { + 'lighting': { + 'type': 'Lighting' + }, + 'animation': { + 'type': 'Animation' + }, + 'compositing': { + 'type': 'Compositing' + } + }, + 'visualParent': ObjectId( + '5fabee9730a94666449245b6' + ) + }, + 'type': 'asset', + 'parent': ObjectId( + '5f2a6d2311e06a9818a1958b' + ), + 'schema': 'pype:asset-3.0' + }, + 'subsetEntity': { + '_id': ObjectId( + '61d723a271e6fce378bd428c' + ), + 'schema': 'openpype:subset-3.0', + 'type': 'subset', + 'name': 'renderLightingDefault', + 'data': { + 'families': [ + 'render', + 'review', + 'ftrack', + 'slack' + ] + }, + 'parent': ObjectId( + '5fabee9730a94666449245b7' + ) + }, + 'versionEntity': { + '_id': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'schema': 'openpype:version-3.0', + 'type': 'version', + 'parent': ObjectId( + '61d723a271e6fce378bd428c' + ), + 'name': 1, + 'data': { + 'families': [ + 'render', + 'render', + 'review', + 'ftrack', + 'slack' + ], + 'time': '20220106T181423Z', + 'author': 'petrk', + 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', + 'comment': '', + 'machine': 'LAPTOP-UB778LHG', + 'fps': 25.0, + 'intent': '-', + 'frameStart': 0, + 'frameEnd': 0, + 'handleEnd': 0, + 'handleStart': 0, + 'inputLinks': [ + OrderedDict( + [ + ( + 'type', + 'generative' + ), + ( + 'id', + ObjectId( + '600ab849c411725a626b8c35' + )), + ( + 'linkedBy', + 'publish' + ) + ] + ) + ] + } + }, + 'transfers': [ + [ + 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264burnin.mp4', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + ], + 'destination_list': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ], + 'published_representations': { + ObjectId( + '61d723a371e6fce378bd428e' + ): { + 'representation': { + '_id': ObjectId( + '61d723a371e6fce378bd428e' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'mov', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'ext': 'mov', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'mov', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a371e6fce378bd4291' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.mov', + 'size': 1654788, + 'hash': 'petr_test_shot01_renderLightingDefault_v001,mov|1641489300,6230524|1654788', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 15, + 264448) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'representation': 'mov', + 'ext': 'mov' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' + ] + }, + ObjectId( + '61d723a371e6fce378bd4292' + ): { + 'representation': { + '_id': ObjectId( + '61d723a371e6fce378bd4292' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'thumbnail', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'ext': 'jpg', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'jpg', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a371e6fce378bd4295' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.jpg', + 'size': 871, + 'hash': 'petr_test_shot01_renderLightingDefault_v001,jpg|1641489301,1720147|871', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 15, + 825446) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'representation': 'jpg', + 'ext': 'jpg' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' + ] + }, + ObjectId( + '61d723a471e6fce378bd4296' + ): { + 'representation': { + '_id': ObjectId( + '61d723a471e6fce378bd4296' + ), + 'schema': 'openpype:representation-2.0', + 'type': 'representation', + 'parent': ObjectId( + '61d723a371e6fce378bd428d' + ), + 'name': 'h264_mp4', + 'data': { + 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' + }, + 'dependencies': [], + 'context': { + 'root': { + 'work': 'C:/projects' + }, + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'hierarchy': 'sequences/seq01', + 'asset': 'shot01', + 'family': 'render', + 'subset': 'renderLightingDefault', + 'version': 1, + 'output': 'h264', + 'ext': 'mp4', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'representation': 'mp4', + 'username': 'petrk' + }, + 'files': [ + { + '_id': ObjectId( + '61d723a471e6fce378bd4299' + ), + 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'size': 10227, + 'hash': 'petr_test_shot01_renderLightingDefault_v001_h264,mp4|1641489313,659368|10227', + 'sites': [ + { + 'name': 'studio', + 'created_dt': datetime.datetime(2022, + 1, + 6, + 18, + 15, + 16, + 53445) + } + ] + } + ] + }, + 'anatomy_data': { + 'project': { + 'name': 'petr_test', + 'code': 'petr_test' + }, + 'asset': 'shot01', + 'parent': 'seq01', + 'hierarchy': 'sequences/seq01', + 'task': { + 'name': 'lighting', + 'type': 'Lighting', + 'short': 'lgt' + }, + 'username': 'petrk', + 'app': 'aftereffects', + 'd': '6', + 'dd': '06', + 'ddd': 'Thu', + 'dddd': 'Thursday', + 'm': '1', + 'mm': '01', + 'mmm': 'Jan', + 'mmmm': 'January', + 'yy': '22', + 'yyyy': '2022', + 'H': '18', + 'HH': '18', + 'h': '6', + 'hh': '06', + 'ht': 'PM', + 'M': '14', + 'MM': '14', + 'S': '23', + 'SS': '23', + 'version': 1, + 'subset': 'renderLightingDefault', + 'family': 'render', + 'intent': '-', + 'resolution_width': 1920, + 'resolution_height': 1080, + 'fps': 25, + 'output': 'h264', + 'representation': 'mp4', + 'ext': 'mp4' + }, + 'published_files': [ + 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' + ] + } + }, + 'ftrackComponentsList': [ + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': True, + 'component_data': { + 'name': 'thumbnail' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'ftrackreview-mp4', + 'metadata': { + 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' + } + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'thumbnail_src' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'ftrackreview-mp4_src', + 'metadata': { + 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' + } + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', + 'component_location': , + 'component': + }, + { + 'assettype_data': { + 'short': 'render' + }, + 'asset_data': { + 'name': 'renderLightingDefault' + }, + 'assetversion_data': { + 'version': 1 + }, + 'component_overwrite': False, + 'thumbnail': False, + 'component_data': { + 'name': 'mov' + }, + 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', + 'component_location': , + 'component': + } + ], + 'ftrackIntegratedAssetVersions': [ + + ] +} \ No newline at end of file diff --git a/vendor/response.json b/vendor/response.json new file mode 100644 index 00000000000..26a4fae2fdc --- /dev/null +++ b/vendor/response.json @@ -0,0 +1 @@ +{status: 200, headers: {'date': 'Tue, 11 Jan 2022 11:08:57 GMT', 'server': 'Apache', 'x-powered-by': 'HHVM/4.128.0', 'access-control-allow-origin': '*', 'referrer-policy': 'no-referrer', 'x-slack-backend': 'r', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'access-control-allow-headers': 'slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags', 'access-control-expose-headers': 'x-slack-req-id, retry-after', 'x-oauth-scopes': 'chat:write,chat:write.public,files:write,chat:write.customize', 'x-accepted-oauth-scopes': 'chat:write', 'expires': 'Mon, 26 Jul 1997 05:00:00 GMT', 'cache-control': 'private, no-cache, no-store, must-revalidate', 'pragma': 'no-cache', 'x-xss-protection': '0', 'x-content-type-options': 'nosniff', 'x-slack-req-id': '9d1d11399a44c8751f89bb4dcd2b91fb', 'vary': 'Accept-Encoding', 'content-type': 'application/json; charset=utf-8', 'x-envoy-upstream-service-time': '52', 'x-backend': 'main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-qno3', 'x-slack-shared-secret-outcome': 'no-match', 'via': 'envoy-www-iad-omsy, envoy-edge-iad-bgfx', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close', 'transfer-encoding': 'chunked'}, body: {"ok":true,"channel":"C024DUFM8MB","ts":"1641899337.001100","message":{"type":"message","subtype":"bot_message","text":"RenderCompositingDefault published for Jungle\n\nHere should be link to review C:\\projects\\petr_test\\assets\\locations\\Jungle\\publish\\render\\renderCompositingDefault\\v253\\petr_test_Jungle_renderCompositingDefault_v253_h264.mp4\n\n Attachment links: \n\n","ts":"1641899337.001100","username":"OpenPypeNotifier","icons":{"image_48":"https:\/\/s3-us-west-2.amazonaws.com\/slack-files2\/bot_icons\/2022-01-07\/2934353684385_48.png"},"bot_id":"B024H0P0CAE"}} \ No newline at end of file diff --git a/vendor/temp.json b/vendor/temp.json new file mode 100644 index 00000000000..089174d26c3 --- /dev/null +++ b/vendor/temp.json @@ -0,0 +1,46 @@ +{ + project(name: "demo_Big_Episodic") { + representations( + first: 0, + after: 0, + localSite: "local", + remoteSite: "local" + ) { + edges { + node { + id + name + # Sorry: totalSize is not implemented, but it will be + # totalSize + fileCount + # overal sync state + localState{ + status + size + timestamp + } + remoteState{ + status + size + timestamp + } + # crawl to the top to get parent info + version { + version + subset { + family + name + folder { + name + } + } + } + } + } + pageInfo { + hasNextPage + endCursor + } + } + } +} \ No newline at end of file From b47e480d7c5384862afbbfae3e7e0b779036e1da Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:31:02 +0200 Subject: [PATCH 05/10] Revert "OP-4181 - clean up after review comments" This reverts commit a7150bd6f1c9494734f03265eecbe86ff284d882. --- igniter/GPUCache/data_0 | Bin 8192 -> 0 bytes igniter/GPUCache/data_1 | Bin 270336 -> 0 bytes igniter/GPUCache/data_2 | Bin 8192 -> 0 bytes igniter/GPUCache/data_3 | Bin 8192 -> 0 bytes igniter/GPUCache/index | Bin 262512 -> 0 bytes openpype/hooks/pre_python2_prelaunch.py | 35 - openpype/hosts/photoshop/tests/expr.py | 51 - openpype/lib/token | 1 - .../event_handlers_user/action_edl_create.py | 275 ---- openpype/pipeline/temp_anatomy.py | 1330 ----------------- .../plugins/publish/integrate_hero_version.py | 9 +- .../_process_referenced_pipeline_result.json | 92 -- tests/unit/test_unzip.py | 11 - vendor/configs/OpenColorIO-Configs | 1 - vendor/instance.json | 1133 -------------- vendor/response.json | 1 - vendor/temp.json | 46 - 17 files changed, 3 insertions(+), 2982 deletions(-) delete mode 100644 igniter/GPUCache/data_0 delete mode 100644 igniter/GPUCache/data_1 delete mode 100644 igniter/GPUCache/data_2 delete mode 100644 igniter/GPUCache/data_3 delete mode 100644 igniter/GPUCache/index delete mode 100644 openpype/hooks/pre_python2_prelaunch.py delete mode 100644 openpype/hosts/photoshop/tests/expr.py delete mode 100644 openpype/lib/token delete mode 100644 openpype/modules/ftrack/event_handlers_user/action_edl_create.py delete mode 100644 openpype/pipeline/temp_anatomy.py delete mode 100644 tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json delete mode 100644 tests/unit/test_unzip.py delete mode 160000 vendor/configs/OpenColorIO-Configs delete mode 100644 vendor/instance.json delete mode 100644 vendor/response.json delete mode 100644 vendor/temp.json diff --git a/igniter/GPUCache/data_0 b/igniter/GPUCache/data_0 deleted file mode 100644 index d76fb77e93ac8a536b5dbade616d63abd00626c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIuK?wjL5Jka{7-jo+5O1auw}mk8@B+*}b0s6M>Kg$91PBlyK!5-N0t5&UAV7cs W0RjXF5FkK+009C72oNCfo4^Gh&;oe? diff --git a/igniter/GPUCache/data_1 b/igniter/GPUCache/data_1 deleted file mode 100644 index 212f73166781160e472f8e76c3b9998b3775ecb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 270336 zcmeI%u?@m75CFhW@CfV>3QP3t%>amw0a8XffrJVo)0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N c0t5&UAV7cs0RjXF5FkK+009C72>hqO2eI!7!2kdN diff --git a/igniter/GPUCache/data_2 b/igniter/GPUCache/data_2 deleted file mode 100644 index c7e2eb9adcfb2d3313ec85f5c28cedda950a3f9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIu!3h8`2n0b1_TQ7_m#U&=2(t%Qz}%M=ae7_Oi2wlt1PBlyK!5-N0t5&UAV7cs V0RjXF5FkK+009C72oTsN@Bv`}0$Tt8 diff --git a/igniter/GPUCache/data_3 b/igniter/GPUCache/data_3 deleted file mode 100644 index 5eec97358cf550862fd343fc9a73c159d4c0ab10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 zcmeIuK@9*P5CpLeAOQbv2)|PW$RO!FMnHFsm9+HS=9>r*AV7cs0RjXF5FkK+009C7 W2oNAZfB*pk1PBlyK!5;&-vkZ-dID$w diff --git a/igniter/GPUCache/index b/igniter/GPUCache/index deleted file mode 100644 index b2998cfef1a6457e5cfe9dc37e029bdbe0a7f778..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262512 zcmeIuu?>JQ00XcT9zZ-&b?3`o&{Gf_S0S;K0~nnt$>{4|&t%Cr+dIlg%Diho_EzWC z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjYm G5qJP2>jZZI diff --git a/openpype/hooks/pre_python2_prelaunch.py b/openpype/hooks/pre_python2_prelaunch.py deleted file mode 100644 index 84272d2e5d7..00000000000 --- a/openpype/hooks/pre_python2_prelaunch.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -from openpype.lib import PreLaunchHook - - -class PrePython2Vendor(PreLaunchHook): - """Prepend python 2 dependencies for py2 hosts.""" - order = 10 - - def execute(self): - if not self.application.use_python_2: - return - - # Prepare vendor dir path - self.log.info("adding global python 2 vendor") - pype_root = os.getenv("OPENPYPE_REPOS_ROOT") - python_2_vendor = os.path.join( - pype_root, - "openpype", - "vendor", - "python", - "python_2" - ) - - # Add Python 2 modules - python_paths = [ - python_2_vendor - ] - - # Load PYTHONPATH from current launch context - python_path = self.launch_context.env.get("PYTHONPATH") - if python_path: - python_paths.append(python_path) - - # Set new PYTHONPATH to launch context environments - self.launch_context.env["PYTHONPATH"] = os.pathsep.join(python_paths) diff --git a/openpype/hosts/photoshop/tests/expr.py b/openpype/hosts/photoshop/tests/expr.py deleted file mode 100644 index ff796f417c3..00000000000 --- a/openpype/hosts/photoshop/tests/expr.py +++ /dev/null @@ -1,51 +0,0 @@ -import json - -data = [ - { - "schema": "openpype:container-2.0", - "id": "pyblish.avalon.container", - "name": "imageArtNeew", - "namespace": "Jungle_imageArtNeew_001", - "loader": "ReferenceLoader", - "representation": "61c1eb91e1a4d1e5a23582f6", - "members": [ - "131" - ] - }, - { - "id": "pyblish.avalon.instance", - "family": "image", - "asset": "Jungle", - "subset": "imageMainBg", - "active": True, - "variant": "Main", - "uuid": "199", - "long_name": "BG" - }, - { - "id": "pyblish.avalon.instance", - "family": "image", - "asset": "Jungle", - "subset": "imageMain", - "active": True, - "variant": "Main", - "uuid": "192", - "long_name": "imageMain" - }, - { - "id": "pyblish.avalon.instance", - "family": "workfile", - "subset": "workfile", - "active": True, - "creator_identifier": "workfile", - "asset": "Jungle", - "task": "art", - "variant": "", - "instance_id": "3ed19342-cd8e-4bb6-8cda-d6e74d9a7efe", - "creator_attributes": {}, - "publish_attributes": {} - } -] - -with open("C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\openpype\\hosts\\photoshop\\tests\\mock_get_layers_metadata.json", 'w') as fp: - fp.write(json.dumps(data, indent=4)) \ No newline at end of file diff --git a/openpype/lib/token b/openpype/lib/token deleted file mode 100644 index 193a2aac957..00000000000 --- a/openpype/lib/token +++ /dev/null @@ -1 +0,0 @@ -5d58370a7702b2efee5120704246baf4abb865323fc9db9a04827bfb478569d6 \ No newline at end of file diff --git a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py b/openpype/modules/ftrack/event_handlers_user/action_edl_create.py deleted file mode 100644 index 7ac139ae630..00000000000 --- a/openpype/modules/ftrack/event_handlers_user/action_edl_create.py +++ /dev/null @@ -1,275 +0,0 @@ -import os -import subprocess -import tempfile -import shutil -import json -import sys - -import opentimelineio as otio -import ftrack_api -import requests - -from openpype_modules.ftrack.lib import BaseAction - - -def download_file(url, path): - with open(path, "wb") as f: - print("\nDownloading %s" % path) - response = requests.get(url, stream=True) - total_length = response.headers.get('content-length') - - if total_length is None: - f.write(response.content) - else: - dl = 0 - total_length = int(total_length) - for data in response.iter_content(chunk_size=4096): - dl += len(data) - f.write(data) - done = int(50 * dl / total_length) - sys.stdout.write("\r[%s%s]" % ('=' * done, ' ' * (50-done))) - sys.stdout.flush() - - -class ExportEditorialAction(BaseAction): - '''Export Editorial action''' - - label = "Export Editorial" - variant = None - identifier = "export-editorial" - description = None - component_name_order = ["exr", "mov", "ftrackreview-mp4_src"] - - def export_editorial(self, entity, output_path): - session = ftrack_api.Session() - unmanaged_location = session.query( - "Location where name is \"ftrack.unmanaged\"" - ).one() - temp_path = tempfile.mkdtemp() - - files = {} - for obj in entity["review_session_objects"]: - data = {} - parent_name = obj["asset_version"]["asset"]["parent"]["name"] - component_query = "Component where version_id is \"{}\"" - component_query += " and name is \"{}\"" - for name in self.component_name_order: - try: - component = session.query( - component_query.format( - obj["asset_version"]["id"], name - ) - ).one() - path = unmanaged_location.get_filesystem_path(component) - data["path"] = path.replace("\\", "/") - break - except ftrack_api.exception.NoResultFoundError: - pass - - # Download online review if not local path found. - if "path" not in data: - component = session.query( - component_query.format( - obj["asset_version"]["id"], "ftrackreview-mp4" - ) - ).one() - location = component["component_locations"][0] - component_url = location["location"].get_url(component) - asset_name = obj["asset_version"]["asset"]["name"] - version = obj["asset_version"]["version"] - filename = "{}_{}_v{:03d}.mp4".format( - parent_name, asset_name, version - ) - filepath = os.path.join( - output_path, "downloads", filename - ).replace("\\", "/") - - if not os.path.exists(os.path.dirname(filepath)): - os.makedirs(os.path.dirname(filepath)) - - download_file(component_url, filepath) - data["path"] = filepath - - # Get frame duration and framerate. - query = "Component where version_id is \"{}\"" - query += " and name is \"ftrackreview-mp4\"" - component = session.query( - query.format(obj["asset_version"]["id"]) - ).one() - metadata = json.loads(component["metadata"]["ftr_meta"]) - data["framerate"] = metadata["frameRate"] - data["frames"] = metadata["frameOut"] - metadata["frameIn"] - - # Find audio if it exists. - query = "Asset where parent.id is \"{}\"" - query += " and type.name is \"Audio\"" - asset = session.query( - query.format(obj["asset_version"]["asset"]["parent"]["id"]) - ) - if asset: - asset_version = asset[0]["versions"][-1] - query = "Component where version_id is \"{}\"" - query += " and name is \"{}\"" - comp = session.query( - query.format(asset_version["id"], "wav") - ).one() - src = unmanaged_location.get_filesystem_path(comp) - dst = os.path.join(temp_path, parent_name + ".wav") - shutil.copy(src, dst) - - # Collect data. - files[parent_name] = data - - clips = [] - for name, data in files.items(): - self.log.info("Processing {} with {}".format(name, data)) - f = data["path"] - range = otio.opentime.TimeRange( - start_time=otio.opentime.RationalTime(0, data["framerate"]), - duration=otio.opentime.RationalTime( - data["frames"], data["framerate"] - ) - ) - - media_reference = otio.schema.ExternalReference( - available_range=range, - target_url=f"file://{f}" - ) - - clip = otio.schema.Clip( - name=name, - media_reference=media_reference, - source_range=range - ) - clips.append(clip) - - # path = os.path.join(temp_path, name + ".wav").replace("\\", "/") - # if not os.path.exists(path): - # args = ["ffmpeg", "-y", "-i", f, path] - # self.log.info(subprocess.list2cmdline(args)) - # subprocess.call(args) - - timeline = otio.schema.timeline_from_clips(clips) - otio.adapters.write_to_file( - timeline, os.path.join(output_path, entity["name"] + ".xml") - ) - - data = "" - for f in os.listdir(temp_path): - f = f.replace("\\", "/") - data += f"file '{f}'\n" - - path = os.path.join(temp_path, "temp.txt") - with open(path, "w") as f: - f.write(data) - - args = [ - "ffmpeg", "-y", "-f", "concat", "-safe", "0", - "-i", os.path.basename(path), - os.path.join(output_path, entity["name"] + ".wav") - ] - self.log.info(subprocess.list2cmdline(args)) - subprocess.call(args, cwd=temp_path) - - shutil.rmtree(temp_path) - - def discover(self, session, entities, event): - '''Return true if we can handle the selected entities. - *session* is a `ftrack_api.Session` instance - *entities* is a list of tuples each containing the entity type and the - entity id. - If the entity is a hierarchical you will always get the entity - type TypedContext, once retrieved through a get operation you - will have the "real" entity type ie. example Shot, Sequence - or Asset Build. - *event* the unmodified original event - ''' - if len(entities) == 1: - if entities[0].entity_type == "ReviewSession": - return True - - return False - - def launch(self, session, entities, event): - '''Callback method for the custom action. - return either a bool ( True if successful or False if the action - failed ) or a dictionary with they keys `message` and `success`, the - message should be a string and will be displayed as feedback to the - user, success should be a bool, True if successful or False if the - action failed. - *session* is a `ftrack_api.Session` instance - *entities* is a list of tuples each containing the entity type and the - entity id. - If the entity is a hierarchical you will always get the entity - type TypedContext, once retrieved through a get operation you - will have the "real" entity type ie. example Shot, Sequence - or Asset Build. - *event* the unmodified original event - ''' - if 'values' in event['data']: - userId = event['source']['user']['id'] - user = session.query('User where id is ' + userId).one() - job = session.create( - 'Job', - { - 'user': user, - 'status': 'running', - 'data': json.dumps({ - 'description': 'Export Editorial.' - }) - } - ) - session.commit() - - try: - output_path = event["data"]["values"]["output_path"] - - if not os.path.exists(output_path): - os.makedirs(output_path) - - self.export_editorial(entities[0], output_path) - - job['status'] = 'done' - session.commit() - except Exception: - session.rollback() - job["status"] = "failed" - session.commit() - self.log.error( - "Exporting editorial failed ({})", exc_info=True - ) - - return { - 'success': True, - 'message': 'Action completed successfully' - } - - items = [ - { - 'label': 'Output folder:', - 'type': 'text', - 'value': '', - 'name': 'output_path' - } - - ] - return { - 'success': True, - 'message': "", - 'items': items - } - - -def register(session): - '''Register action. Called when used as an event plugin.''' - - ExportEditorialAction(session).register() - - -if __name__ == "__main__": - session = ftrack_api.Session() - action = ExportEditorialAction(session) - id = "bfe0477c-d5a8-49d8-88b9-6d44d2e48fd9" - review_session = session.get("ReviewSession", id) - path = r"c:/projects" - action.export_editorial(review_session, path) \ No newline at end of file diff --git a/openpype/pipeline/temp_anatomy.py b/openpype/pipeline/temp_anatomy.py deleted file mode 100644 index 27a9370928a..00000000000 --- a/openpype/pipeline/temp_anatomy.py +++ /dev/null @@ -1,1330 +0,0 @@ -import os -import re -import copy -import platform -import collections -import numbers - -import six -import time - -from openpype.settings.lib import ( - get_anatomy_settings, - get_project_settings, - get_default_project_settings, - get_local_settings -) - -from openpype.client import get_project -from openpype.lib.path_templates import ( - TemplateUnsolved, - TemplateResult, - TemplatesDict, - FormatObject, -) -from openpype.lib.log import Logger -from openpype.lib import get_local_site_id - -log = Logger.get_logger(__name__) - - -class ProjectNotSet(Exception): - """Exception raised when is created Anatomy without project name.""" - - -class RootCombinationError(Exception): - """This exception is raised when templates has combined root types.""" - - def __init__(self, roots): - joined_roots = ", ".join( - ["\"{}\"".format(_root) for _root in roots] - ) - # TODO better error message - msg = ( - "Combination of root with and" - " without root name in AnatomyTemplates. {}" - ).format(joined_roots) - - super(RootCombinationError, self).__init__(msg) - - -class BaseAnatomy(object): - """Anatomy module helps to keep project settings. - - Wraps key project specifications, AnatomyTemplates and Roots. - """ - - def __init__(self, project_doc, local_settings): - project_name = project_doc["name"] - self.project_name = project_name - - self._data = self._prepare_anatomy_data( - project_doc, local_settings - ) - self._templates_obj = AnatomyTemplates(self) - self._roots_obj = Roots(self) - - root_key_regex = re.compile(r"{(root?[^}]+)}") - root_name_regex = re.compile(r"root\[([^]]+)\]") - - # Anatomy used as dictionary - # - implemented only getters returning copy - def __getitem__(self, key): - return copy.deepcopy(self._data[key]) - - def get(self, key, default=None): - return copy.deepcopy(self._data).get(key, default) - - def keys(self): - return copy.deepcopy(self._data).keys() - - def values(self): - return copy.deepcopy(self._data).values() - - def items(self): - return copy.deepcopy(self._data).items() - - @staticmethod - def _prepare_anatomy_data(anatomy_data): - """Prepare anatomy data for further processing. - - Method added to replace `{task}` with `{task[name]}` in templates. - """ - templates_data = anatomy_data.get("templates") - if templates_data: - # Replace `{task}` with `{task[name]}` in templates - value_queue = collections.deque() - value_queue.append(templates_data) - while value_queue: - item = value_queue.popleft() - if not isinstance(item, dict): - continue - - for key in tuple(item.keys()): - value = item[key] - if isinstance(value, dict): - value_queue.append(value) - - elif isinstance(value, six.string_types): - item[key] = value.replace("{task}", "{task[name]}") - return anatomy_data - - def reset(self): - """Reset values of cached data in templates and roots objects.""" - self._data = self._prepare_anatomy_data( - get_anatomy_settings(self.project_name, self._site_name) - ) - self.templates_obj.reset() - self.roots_obj.reset() - - @property - def templates(self): - """Wrap property `templates` of Anatomy's AnatomyTemplates instance.""" - return self._templates_obj.templates - - @property - def templates_obj(self): - """Return `AnatomyTemplates` object of current Anatomy instance.""" - return self._templates_obj - - def format(self, *args, **kwargs): - """Wrap `format` method of Anatomy's `templates_obj`.""" - return self._templates_obj.format(*args, **kwargs) - - def format_all(self, *args, **kwargs): - """Wrap `format_all` method of Anatomy's `templates_obj`.""" - return self._templates_obj.format_all(*args, **kwargs) - - @property - def roots(self): - """Wrap `roots` property of Anatomy's `roots_obj`.""" - return self._roots_obj.roots - - @property - def roots_obj(self): - """Return `Roots` object of current Anatomy instance.""" - return self._roots_obj - - def root_environments(self): - """Return OPENPYPE_ROOT_* environments for current project in dict.""" - return self._roots_obj.root_environments() - - def root_environmets_fill_data(self, template=None): - """Environment variable values in dictionary for rootless path. - - Args: - template (str): Template for environment variable key fill. - By default is set to `"${}"`. - """ - return self.roots_obj.root_environmets_fill_data(template) - - def find_root_template_from_path(self, *args, **kwargs): - """Wrapper for Roots `find_root_template_from_path`.""" - return self.roots_obj.find_root_template_from_path(*args, **kwargs) - - def path_remapper(self, *args, **kwargs): - """Wrapper for Roots `path_remapper`.""" - return self.roots_obj.path_remapper(*args, **kwargs) - - def all_root_paths(self): - """Wrapper for Roots `all_root_paths`.""" - return self.roots_obj.all_root_paths() - - def set_root_environments(self): - """Set OPENPYPE_ROOT_* environments for current project.""" - self._roots_obj.set_root_environments() - - def root_names(self): - """Return root names for current project.""" - return self.root_names_from_templates(self.templates) - - def _root_keys_from_templates(self, data): - """Extract root key from templates in data. - - Args: - data (dict): Data that may contain templates as string. - - Return: - set: Set of all root names from templates as strings. - - Output example: `{"root[work]", "root[publish]"}` - """ - - output = set() - if isinstance(data, dict): - for value in data.values(): - for root in self._root_keys_from_templates(value): - output.add(root) - - elif isinstance(data, str): - for group in re.findall(self.root_key_regex, data): - output.add(group) - - return output - - def root_value_for_template(self, template): - """Returns value of root key from template.""" - root_templates = [] - for group in re.findall(self.root_key_regex, template): - root_templates.append("{" + group + "}") - - if not root_templates: - return None - - return root_templates[0].format(**{"root": self.roots}) - - def root_names_from_templates(self, templates): - """Extract root names form anatomy templates. - - Returns None if values in templates contain only "{root}". - Empty list is returned if there is no "root" in templates. - Else returns all root names from templates in list. - - RootCombinationError is raised when templates contain both root types, - basic "{root}" and with root name specification "{root[work]}". - - Args: - templates (dict): Anatomy templates where roots are not filled. - - Return: - list/None: List of all root names from templates as strings when - multiroot setup is used, otherwise None is returned. - """ - roots = list(self._root_keys_from_templates(templates)) - # Return empty list if no roots found in templates - if not roots: - return roots - - # Raise exception when root keys have roots with and without root name. - # Invalid output example: ["root", "root[project]", "root[render]"] - if len(roots) > 1 and "root" in roots: - raise RootCombinationError(roots) - - # Return None if "root" without root name in templates - if len(roots) == 1 and roots[0] == "root": - return None - - names = set() - for root in roots: - for group in re.findall(self.root_name_regex, root): - names.add(group) - return list(names) - - def fill_root(self, template_path): - """Fill template path where is only "root" key unfilled. - - Args: - template_path (str): Path with "root" key in. - Example path: "{root}/projects/MyProject/Shot01/Lighting/..." - - Return: - str: formatted path - """ - # NOTE does not care if there are different keys than "root" - return template_path.format(**{"root": self.roots}) - - @classmethod - def fill_root_with_path(cls, rootless_path, root_path): - """Fill path without filled "root" key with passed path. - - This is helper to fill root with different directory path than anatomy - has defined no matter if is single or multiroot. - - Output path is same as input path if `rootless_path` does not contain - unfilled root key. - - Args: - rootless_path (str): Path without filled "root" key. Example: - "{root[work]}/MyProject/..." - root_path (str): What should replace root key in `rootless_path`. - - Returns: - str: Path with filled root. - """ - output = str(rootless_path) - for group in re.findall(cls.root_key_regex, rootless_path): - replacement = "{" + group + "}" - output = output.replace(replacement, root_path) - - return output - - def replace_root_with_env_key(self, filepath, template=None): - """Replace root of path with environment key. - - # Example: - ## Project with roots: - ``` - { - "nas": { - "windows": P:/projects", - ... - } - ... - } - ``` - - ## Entered filepath - "P:/projects/project/asset/task/animation_v001.ma" - - ## Entered template - "<{}>" - - ## Output - "/project/asset/task/animation_v001.ma" - - Args: - filepath (str): Full file path where root should be replaced. - template (str): Optional template for environment key. Must - have one index format key. - Default value if not entered: "${}" - - Returns: - str: Path where root is replaced with environment root key. - - Raise: - ValueError: When project's roots were not found in entered path. - """ - success, rootless_path = self.find_root_template_from_path(filepath) - if not success: - raise ValueError( - "{}: Project's roots were not found in path: {}".format( - self.project_name, filepath - ) - ) - - data = self.root_environmets_fill_data(template) - return rootless_path.format(**data) - - -class Anatomy(BaseAnatomy): - _project_cache = {} - - def __init__(self, project_name=None, site_name=None): - if not project_name: - project_name = os.environ.get("AVALON_PROJECT") - - if not project_name: - raise ProjectNotSet(( - "Implementation bug: Project name is not set. Anatomy requires" - " to load data for specific project." - )) - - self._site_name = site_name - project_info = self.get_project_data_and_cache(project_name, site_name) - - super(Anatomy, self).__init__( - project_info["project_doc"], - project_info["local_settings"] - ) - - @classmethod - def get_project_data_and_cache(cls, project_name, site_name): - project_info = cls._project_cache.get(project_name) - if project_info is not None: - if time.time() - project_info["start"] > 10: - cls._project_cache.pop(project_name) - project_info = None - - if project_info is None: - if site_name is None: - if project_name: - project_settings = get_project_settings(project_name) - else: - project_settings = get_default_project_settings() - site_name = ( - project_settings["global"] - ["sync_server"] - ["config"] - ["active_site"] - ) - if site_name == "local": - site_name = get_local_site_id() - - project_info = { - "project_doc": get_project(project_name), - "local_settings": get_local_settings(site_name), - "site_name": site_name, - "start": time.time() - } - cls._project_cache[project_name] = project_info - - return project_info - - def reset(self): - """Reset values of cached data in templates and roots objects.""" - self._data = self._prepare_anatomy_data( - get_anatomy_settings(self.project_name, self._site_name) - ) - self.templates_obj.reset() - self.roots_obj.reset() - - -class AnatomyTemplateUnsolved(TemplateUnsolved): - """Exception for unsolved template when strict is set to True.""" - - msg = "Anatomy template \"{0}\" is unsolved.{1}{2}" - - -class AnatomyTemplateResult(TemplateResult): - rootless = None - - def __new__(cls, result, rootless_path): - new_obj = super(AnatomyTemplateResult, cls).__new__( - cls, - str(result), - result.template, - result.solved, - result.used_values, - result.missing_keys, - result.invalid_types - ) - new_obj.rootless = rootless_path - return new_obj - - def validate(self): - if not self.solved: - raise AnatomyTemplateUnsolved( - self.template, - self.missing_keys, - self.invalid_types - ) - - def copy(self): - tmp = TemplateResult( - str(self), - self.template, - self.solved, - self.used_values, - self.missing_keys, - self.invalid_types - ) - return self.__class__(tmp, self.rootless) - - def normalized(self): - """Convert to normalized path.""" - - tmp = TemplateResult( - os.path.normpath(self), - self.template, - self.solved, - self.used_values, - self.missing_keys, - self.invalid_types - ) - return self.__class__(tmp, self.rootless) - - -class AnatomyTemplates(TemplatesDict): - inner_key_pattern = re.compile(r"(\{@.*?[^{}0]*\})") - inner_key_name_pattern = re.compile(r"\{@(.*?[^{}0]*)\}") - - def __init__(self, anatomy): - super(AnatomyTemplates, self).__init__() - self.anatomy = anatomy - self.loaded_project = None - - def __getitem__(self, key): - return self.templates[key] - - def get(self, key, default=None): - return self.templates.get(key, default) - - def reset(self): - self._raw_templates = None - self._templates = None - self._objected_templates = None - - @property - def project_name(self): - return self.anatomy.project_name - - @property - def roots(self): - return self.anatomy.roots - - @property - def templates(self): - self._validate_discovery() - return self._templates - - @property - def objected_templates(self): - self._validate_discovery() - return self._objected_templates - - def _validate_discovery(self): - if self.project_name != self.loaded_project: - self.reset() - - if self._templates is None: - self._discover() - self.loaded_project = self.project_name - - def _format_value(self, value, data): - if isinstance(value, RootItem): - return self._solve_dict(value, data) - - result = super(AnatomyTemplates, self)._format_value(value, data) - if isinstance(result, TemplateResult): - rootless_path = self._rootless_path(result, data) - result = AnatomyTemplateResult(result, rootless_path) - return result - - def set_templates(self, templates): - if not templates: - self.reset() - return - - self._raw_templates = copy.deepcopy(templates) - templates = copy.deepcopy(templates) - v_queue = collections.deque() - v_queue.append(templates) - while v_queue: - item = v_queue.popleft() - if not isinstance(item, dict): - continue - - for key in tuple(item.keys()): - value = item[key] - if isinstance(value, dict): - v_queue.append(value) - - elif ( - isinstance(value, six.string_types) - and "{task}" in value - ): - item[key] = value.replace("{task}", "{task[name]}") - - solved_templates = self.solve_template_inner_links(templates) - self._templates = solved_templates - self._objected_templates = self.create_ojected_templates( - solved_templates - ) - - def default_templates(self): - """Return default templates data with solved inner keys.""" - return self.solve_template_inner_links( - self.anatomy["templates"] - ) - - def _discover(self): - """ Loads anatomy templates from yaml. - Default templates are loaded if project is not set or project does - not have set it's own. - TODO: create templates if not exist. - - Returns: - TemplatesResultDict: Contain templates data for current project of - default templates. - """ - - if self.project_name is None: - # QUESTION create project specific if not found? - raise AssertionError(( - "Project \"{0}\" does not have his own templates." - " Trying to use default." - ).format(self.project_name)) - - self.set_templates(self.anatomy["templates"]) - - @classmethod - def replace_inner_keys(cls, matches, value, key_values, key): - """Replacement of inner keys in template values.""" - for match in matches: - anatomy_sub_keys = ( - cls.inner_key_name_pattern.findall(match) - ) - if key in anatomy_sub_keys: - raise ValueError(( - "Unsolvable recursion in inner keys, " - "key: \"{}\" is in his own value." - " Can't determine source, please check Anatomy templates." - ).format(key)) - - for anatomy_sub_key in anatomy_sub_keys: - replace_value = key_values.get(anatomy_sub_key) - if replace_value is None: - raise KeyError(( - "Anatomy templates can't be filled." - " Anatomy key `{0}` has" - " invalid inner key `{1}`." - ).format(key, anatomy_sub_key)) - - if not ( - isinstance(replace_value, numbers.Number) - or isinstance(replace_value, six.string_types) - ): - raise ValueError(( - "Anatomy templates can't be filled." - " Anatomy key `{0}` has" - " invalid inner key `{1}`" - " with value `{2}`." - ).format(key, anatomy_sub_key, str(replace_value))) - - value = value.replace(match, str(replace_value)) - - return value - - @classmethod - def prepare_inner_keys(cls, key_values): - """Check values of inner keys. - - Check if inner key exist in template group and has valid value. - It is also required to avoid infinite loop with unsolvable recursion - when first inner key's value refers to second inner key's value where - first is used. - """ - keys_to_solve = set(key_values.keys()) - while True: - found = False - for key in tuple(keys_to_solve): - value = key_values[key] - - if isinstance(value, six.string_types): - matches = cls.inner_key_pattern.findall(value) - if not matches: - keys_to_solve.remove(key) - continue - - found = True - key_values[key] = cls.replace_inner_keys( - matches, value, key_values, key - ) - continue - - elif not isinstance(value, dict): - keys_to_solve.remove(key) - continue - - subdict_found = False - for _key, _value in tuple(value.items()): - matches = cls.inner_key_pattern.findall(_value) - if not matches: - continue - - subdict_found = True - found = True - key_values[key][_key] = cls.replace_inner_keys( - matches, _value, key_values, - "{}.{}".format(key, _key) - ) - - if not subdict_found: - keys_to_solve.remove(key) - - if not found: - break - - return key_values - - @classmethod - def solve_template_inner_links(cls, templates): - """Solve templates inner keys identified by "{@*}". - - Process is split into 2 parts. - First is collecting all global keys (keys in top hierarchy where value - is not dictionary). All global keys are set for all group keys (keys - in top hierarchy where value is dictionary). Value of a key is not - overridden in group if already contain value for the key. - - In second part all keys with "at" symbol in value are replaced with - value of the key afterward "at" symbol from the group. - - Args: - templates (dict): Raw templates data. - - Example: - templates:: - key_1: "value_1", - key_2: "{@key_1}/{filling_key}" - - group_1: - key_3: "value_3/{@key_2}" - - group_2: - key_2": "value_2" - key_4": "value_4/{@key_2}" - - output:: - key_1: "value_1" - key_2: "value_1/{filling_key}" - - group_1: { - key_1: "value_1" - key_2: "value_1/{filling_key}" - key_3: "value_3/value_1/{filling_key}" - - group_2: { - key_1: "value_1" - key_2: "value_2" - key_4: "value_3/value_2" - """ - default_key_values = templates.pop("defaults", {}) - for key, value in tuple(templates.items()): - if isinstance(value, dict): - continue - default_key_values[key] = templates.pop(key) - - # Pop "others" key before before expected keys are processed - other_templates = templates.pop("others") or {} - - keys_by_subkey = {} - for sub_key, sub_value in templates.items(): - key_values = {} - key_values.update(default_key_values) - key_values.update(sub_value) - keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) - - for sub_key, sub_value in other_templates.items(): - if sub_key in keys_by_subkey: - log.warning(( - "Key \"{}\" is duplicated in others. Skipping." - ).format(sub_key)) - continue - - key_values = {} - key_values.update(default_key_values) - key_values.update(sub_value) - keys_by_subkey[sub_key] = cls.prepare_inner_keys(key_values) - - default_keys_by_subkeys = cls.prepare_inner_keys(default_key_values) - - for key, value in default_keys_by_subkeys.items(): - keys_by_subkey[key] = value - - return keys_by_subkey - - def _dict_to_subkeys_list(self, subdict, pre_keys=None): - if pre_keys is None: - pre_keys = [] - output = [] - for key in subdict: - value = subdict[key] - result = list(pre_keys) - result.append(key) - if isinstance(value, dict): - for item in self._dict_to_subkeys_list(value, result): - output.append(item) - else: - output.append(result) - return output - - def _keys_to_dicts(self, key_list, value): - if not key_list: - return None - if len(key_list) == 1: - return {key_list[0]: value} - return {key_list[0]: self._keys_to_dicts(key_list[1:], value)} - - def _rootless_path(self, result, final_data): - used_values = result.used_values - missing_keys = result.missing_keys - template = result.template - invalid_types = result.invalid_types - if ( - "root" not in used_values - or "root" in missing_keys - or "{root" not in template - ): - return - - for invalid_type in invalid_types: - if "root" in invalid_type: - return - - root_keys = self._dict_to_subkeys_list({"root": used_values["root"]}) - if not root_keys: - return - - output = str(result) - for used_root_keys in root_keys: - if not used_root_keys: - continue - - used_value = used_values - root_key = None - for key in used_root_keys: - used_value = used_value[key] - if root_key is None: - root_key = key - else: - root_key += "[{}]".format(key) - - root_key = "{" + root_key + "}" - output = output.replace(str(used_value), root_key) - - return output - - def format(self, data, strict=True): - copy_data = copy.deepcopy(data) - roots = self.roots - if roots: - copy_data["root"] = roots - result = super(AnatomyTemplates, self).format(copy_data) - result.strict = strict - return result - - def format_all(self, in_data, only_keys=True): - """ Solves templates based on entered data. - - Args: - data (dict): Containing keys to be filled into template. - - Returns: - TemplatesResultDict: Output `TemplateResult` have `strict` - attribute set to False so accessing unfilled keys in templates - won't raise any exceptions. - """ - return self.format(in_data, strict=False) - - -class RootItem(FormatObject): - """Represents one item or roots. - - Holds raw data of root item specification. Raw data contain value - for each platform, but current platform value is used when object - is used for formatting of template. - - Args: - root_raw_data (dict): Dictionary containing root values by platform - names. ["windows", "linux" and "darwin"] - name (str, optional): Root name which is representing. Used with - multi root setup otherwise None value is expected. - parent_keys (list, optional): All dictionary parent keys. Values of - `parent_keys` are used for get full key which RootItem is - representing. Used for replacing root value in path with - formattable key. e.g. parent_keys == ["work"] -> {root[work]} - parent (object, optional): It is expected to be `Roots` object. - Value of `parent` won't affect code logic much. - """ - - def __init__( - self, root_raw_data, name=None, parent_keys=None, parent=None - ): - lowered_platform_keys = {} - for key, value in root_raw_data.items(): - lowered_platform_keys[key.lower()] = value - self.raw_data = lowered_platform_keys - self.cleaned_data = self._clean_roots(lowered_platform_keys) - self.name = name - self.parent_keys = parent_keys or [] - self.parent = parent - - self.available_platforms = list(lowered_platform_keys.keys()) - self.value = lowered_platform_keys.get(platform.system().lower()) - self.clean_value = self.clean_root(self.value) - - def __format__(self, *args, **kwargs): - return self.value.__format__(*args, **kwargs) - - def __str__(self): - return str(self.value) - - def __repr__(self): - return self.__str__() - - def __getitem__(self, key): - if isinstance(key, numbers.Number): - return self.value[key] - - additional_info = "" - if self.parent and self.parent.project_name: - additional_info += " for project \"{}\"".format( - self.parent.project_name - ) - - raise AssertionError( - "Root key \"{}\" is missing{}.".format( - key, additional_info - ) - ) - - def full_key(self): - """Full key value for dictionary formatting in template. - - Returns: - str: Return full replacement key for formatting. This helps when - multiple roots are set. In that case e.g. `"root[work]"` is - returned. - """ - if not self.name: - return "root" - - joined_parent_keys = "".join( - ["[{}]".format(key) for key in self.parent_keys] - ) - return "root{}".format(joined_parent_keys) - - def clean_path(self, path): - """Just replace backslashes with forward slashes.""" - return str(path).replace("\\", "/") - - def clean_root(self, root): - """Makes sure root value does not end with slash.""" - if root: - root = self.clean_path(root) - while root.endswith("/"): - root = root[:-1] - return root - - def _clean_roots(self, raw_data): - """Clean all values of raw root item values.""" - cleaned = {} - for key, value in raw_data.items(): - cleaned[key] = self.clean_root(value) - return cleaned - - def path_remapper(self, path, dst_platform=None, src_platform=None): - """Remap path for specific platform. - - Args: - path (str): Source path which need to be remapped. - dst_platform (str, optional): Specify destination platform - for which remapping should happen. - src_platform (str, optional): Specify source platform. This is - recommended to not use and keep unset until you really want - to use specific platform. - roots (dict/RootItem/None, optional): It is possible to remap - path with different roots then instance where method was - called has. - - Returns: - str/None: When path does not contain known root then - None is returned else returns remapped path with "{root}" - or "{root[]}". - """ - cleaned_path = self.clean_path(path) - if dst_platform: - dst_root_clean = self.cleaned_data.get(dst_platform) - if not dst_root_clean: - key_part = "" - full_key = self.full_key() - if full_key != "root": - key_part += "\"{}\" ".format(full_key) - - log.warning( - "Root {}miss platform \"{}\" definition.".format( - key_part, dst_platform - ) - ) - return None - - if cleaned_path.startswith(dst_root_clean): - return cleaned_path - - if src_platform: - src_root_clean = self.cleaned_data.get(src_platform) - if src_root_clean is None: - log.warning( - "Root \"{}\" miss platform \"{}\" definition.".format( - self.full_key(), src_platform - ) - ) - return None - - if not cleaned_path.startswith(src_root_clean): - return None - - subpath = cleaned_path[len(src_root_clean):] - if dst_platform: - # `dst_root_clean` is used from upper condition - return dst_root_clean + subpath - return self.clean_value + subpath - - result, template = self.find_root_template_from_path(path) - if not result: - return None - - def parent_dict(keys, value): - if not keys: - return value - - key = keys.pop(0) - return {key: parent_dict(keys, value)} - - if dst_platform: - format_value = parent_dict(list(self.parent_keys), dst_root_clean) - else: - format_value = parent_dict(list(self.parent_keys), self.value) - - return template.format(**{"root": format_value}) - - def find_root_template_from_path(self, path): - """Replaces known root value with formattable key in path. - - All platform values are checked for this replacement. - - Args: - path (str): Path where root value should be found. - - Returns: - tuple: Tuple contain 2 values: `success` (bool) and `path` (str). - When success it True then path should contain replaced root - value with formattable key. - - Example: - When input path is:: - "C:/windows/path/root/projects/my_project/file.ext" - - And raw data of item looks like:: - { - "windows": "C:/windows/path/root", - "linux": "/mount/root" - } - - Output will be:: - (True, "{root}/projects/my_project/file.ext") - - If any of raw data value wouldn't match path's root output is:: - (False, "C:/windows/path/root/projects/my_project/file.ext") - """ - result = False - output = str(path) - - root_paths = list(self.cleaned_data.values()) - mod_path = self.clean_path(path) - for root_path in root_paths: - # Skip empty paths - if not root_path: - continue - - if mod_path.startswith(root_path): - result = True - replacement = "{" + self.full_key() + "}" - output = replacement + mod_path[len(root_path):] - break - - return (result, output) - - -class Roots: - """Object which should be used for formatting "root" key in templates. - - Args: - anatomy Anatomy: Anatomy object created for a specific project. - """ - - env_prefix = "OPENPYPE_PROJECT_ROOT" - roots_filename = "roots.json" - - def __init__(self, anatomy): - self.anatomy = anatomy - self.loaded_project = None - self._roots = None - - def __format__(self, *args, **kwargs): - return self.roots.__format__(*args, **kwargs) - - def __getitem__(self, key): - return self.roots[key] - - def reset(self): - """Reset current roots value.""" - self._roots = None - - def path_remapper( - self, path, dst_platform=None, src_platform=None, roots=None - ): - """Remap path for specific platform. - - Args: - path (str): Source path which need to be remapped. - dst_platform (str, optional): Specify destination platform - for which remapping should happen. - src_platform (str, optional): Specify source platform. This is - recommended to not use and keep unset until you really want - to use specific platform. - roots (dict/RootItem/None, optional): It is possible to remap - path with different roots then instance where method was - called has. - - Returns: - str/None: When path does not contain known root then - None is returned else returns remapped path with "{root}" - or "{root[]}". - """ - if roots is None: - roots = self.roots - - if roots is None: - raise ValueError("Roots are not set. Can't find path.") - - if "{root" in path: - path = path.format(**{"root": roots}) - # If `dst_platform` is not specified then return else continue. - if not dst_platform: - return path - - if isinstance(roots, RootItem): - return roots.path_remapper(path, dst_platform, src_platform) - - for _root in roots.values(): - result = self.path_remapper( - path, dst_platform, src_platform, _root - ) - if result is not None: - return result - - def find_root_template_from_path(self, path, roots=None): - """Find root value in entered path and replace it with formatting key. - - Args: - path (str): Source path where root will be searched. - roots (Roots/dict, optional): It is possible to use different - roots than instance where method was triggered has. - - Returns: - tuple: Output contains tuple with bool representing success as - first value and path with or without replaced root with - formatting key as second value. - - Raises: - ValueError: When roots are not entered and can't be loaded. - """ - if roots is None: - log.debug( - "Looking for matching root in path \"{}\".".format(path) - ) - roots = self.roots - - if roots is None: - raise ValueError("Roots are not set. Can't find path.") - - if isinstance(roots, RootItem): - return roots.find_root_template_from_path(path) - - for root_name, _root in roots.items(): - success, result = self.find_root_template_from_path(path, _root) - if success: - log.info("Found match in root \"{}\".".format(root_name)) - return success, result - - log.warning("No matching root was found in current setting.") - return (False, path) - - def set_root_environments(self): - """Set root environments for current project.""" - for key, value in self.root_environments().items(): - os.environ[key] = value - - def root_environments(self): - """Use root keys to create unique keys for environment variables. - - Concatenates prefix "OPENPYPE_ROOT" with root keys to create unique - keys. - - Returns: - dict: Result is `{(str): (str)}` dicitonary where key represents - unique key concatenated by keys and value is root value of - current platform root. - - Example: - With raw root values:: - "work": { - "windows": "P:/projects/work", - "linux": "/mnt/share/projects/work", - "darwin": "/darwin/path/work" - }, - "publish": { - "windows": "P:/projects/publish", - "linux": "/mnt/share/projects/publish", - "darwin": "/darwin/path/publish" - } - - Result on windows platform:: - { - "OPENPYPE_ROOT_WORK": "P:/projects/work", - "OPENPYPE_ROOT_PUBLISH": "P:/projects/publish" - } - - Short example when multiroot is not used:: - { - "OPENPYPE_ROOT": "P:/projects" - } - """ - return self._root_environments() - - def all_root_paths(self, roots=None): - """Return all paths for all roots of all platforms.""" - if roots is None: - roots = self.roots - - output = [] - if isinstance(roots, RootItem): - for value in roots.raw_data.values(): - output.append(value) - return output - - for _roots in roots.values(): - output.extend(self.all_root_paths(_roots)) - return output - - def _root_environments(self, keys=None, roots=None): - if not keys: - keys = [] - if roots is None: - roots = self.roots - - if isinstance(roots, RootItem): - key_items = [self.env_prefix] - for _key in keys: - key_items.append(_key.upper()) - - key = "_".join(key_items) - # Make sure key and value does not contain unicode - # - can happen in Python 2 hosts - return {str(key): str(roots.value)} - - output = {} - for _key, _value in roots.items(): - _keys = list(keys) - _keys.append(_key) - output.update(self._root_environments(_keys, _value)) - return output - - def root_environmets_fill_data(self, template=None): - """Environment variable values in dictionary for rootless path. - - Args: - template (str): Template for environment variable key fill. - By default is set to `"${}"`. - """ - if template is None: - template = "${}" - return self._root_environmets_fill_data(template) - - def _root_environmets_fill_data(self, template, keys=None, roots=None): - if keys is None and roots is None: - return { - "root": self._root_environmets_fill_data( - template, [], self.roots - ) - } - - if isinstance(roots, RootItem): - key_items = [Roots.env_prefix] - for _key in keys: - key_items.append(_key.upper()) - key = "_".join(key_items) - return template.format(key) - - output = {} - for key, value in roots.items(): - _keys = list(keys) - _keys.append(key) - output[key] = self._root_environmets_fill_data( - template, _keys, value - ) - return output - - @property - def project_name(self): - """Return project name which will be used for loading root values.""" - return self.anatomy.project_name - - @property - def roots(self): - """Property for filling "root" key in templates. - - This property returns roots for current project or default root values. - Warning: - Default roots value may cause issues when project use different - roots settings. That may happen when project use multiroot - templates but default roots miss their keys. - """ - if self.project_name != self.loaded_project: - self._roots = None - - if self._roots is None: - self._roots = self._discover() - self.loaded_project = self.project_name - return self._roots - - def _discover(self): - """ Loads current project's roots or default. - - Default roots are loaded if project override's does not contain roots. - - Returns: - `RootItem` or `dict` with multiple `RootItem`s when multiroot - setting is used. - """ - - return self._parse_dict(self.anatomy["roots"], parent=self) - - @staticmethod - def _parse_dict(data, key=None, parent_keys=None, parent=None): - """Parse roots raw data into RootItem or dictionary with RootItems. - - Converting raw roots data to `RootItem` helps to handle platform keys. - This method is recursive to be able handle multiroot setup and - is static to be able to load default roots without creating new object. - - Args: - data (dict): Should contain raw roots data to be parsed. - key (str, optional): Current root key. Set by recursion. - parent_keys (list): Parent dictionary keys. Set by recursion. - parent (Roots, optional): Parent object set in `RootItem` - helps to keep RootItem instance updated with `Roots` object. - - Returns: - `RootItem` or `dict` with multiple `RootItem`s when multiroot - setting is used. - """ - if not parent_keys: - parent_keys = [] - is_last = False - for value in data.values(): - if isinstance(value, six.string_types): - is_last = True - break - - if is_last: - return RootItem(data, key, parent_keys, parent=parent) - - output = {} - for _key, value in data.items(): - _parent_keys = list(parent_keys) - _parent_keys.append(_key) - output[_key] = Roots._parse_dict(value, _key, _parent_keys, parent) - return output diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 661975993b5..adc629352e2 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,6 +14,7 @@ ) from openpype.client.operations import ( OperationsSession, + _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -192,13 +193,9 @@ def integrate_instance( op_session = OperationsSession() - entity_id = None - if old_version: - entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"], - entity_id=entity_id + src_version_entity["parent"] ) if old_version: @@ -411,7 +408,7 @@ def integrate_instance( # Create representation else: - repre.pop("_id", None) + repre["_id"] = _create_or_convert_to_mongo_id(None) op_session.create_entity(project_name, "representation", repre) diff --git a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json b/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json deleted file mode 100644 index fb798524bc8..00000000000 --- a/tests/unit/openpype/lib/resources/_process_referenced_pipeline_result.json +++ /dev/null @@ -1,92 +0,0 @@ -[ - { - "_id": { - "$oid": "623c9d53db3f5046eb1ad5f4" - }, - "schema": "openpype:version-3.0", - "type": "version", - "parent": { - "$oid": "5f3e439a30a9464d6c181cbc" - }, - "name": 94, - "data": { - "families": [ - "workfile" - ], - "time": "20220324T173254Z", - "author": "petrk", - "source": "C:/projects_local/petr_test/assets/locations/Jungle/work/art/petr_test_Jungle_art_v009.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": 25.0, - "intent": "-", - "inputLinks": [ - { - "type": "reference", - "id": { - "$oid": "618eb14f0a55a9c1591e913c" - }, - "linkedBy": "publish" - } - ] - }, - "outputs_recursive": [ - { - "_id": { - "$oid": "618eb14f0a55a9c1591e913c" - }, - "schema": "openpype:version-3.0", - "type": "version", - "parent": { - "$oid": "618e42a72ff49bd543bc1768" - }, - "name": 8, - "data": { - "families": [ - "image" - ], - "time": "20211112T192359Z", - "author": "petrk", - "source": "C:/projects_local/petr_test/assets/locations/Town/work/art/petr_test_Town_art_v005.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": 25.0, - "intent": "-", - "inputLinks": [ - { - "type": "reference", - "id": { - "$oid": "5f3cd2d530a94638544837c3" - }, - "linkedBy": "publish" - } - ] - }, - "depth": 0 - }, - { - "_id": { - "$oid": "5f3cd2d530a94638544837c3" - }, - "schema": "pype:version-3.0", - "type": "version", - "parent": { - "$oid": "5f3a714030a9464bfc7d2382" - }, - "name": 7, - "data": { - "families": [ - "image" - ], - "time": "20200819T092032Z", - "author": "petrk", - "source": "/c/projects/petr_test/assets/characters/Hero/work/art/Hero_v019.psd", - "comment": "", - "machine": "LAPTOP-UB778LHG", - "fps": null - }, - "depth": 1 - } - ] - } -] \ No newline at end of file diff --git a/tests/unit/test_unzip.py b/tests/unit/test_unzip.py deleted file mode 100644 index 586fc49b6fa..00000000000 --- a/tests/unit/test_unzip.py +++ /dev/null @@ -1,11 +0,0 @@ - -from openpype.hosts.harmony.api.lib import _ZipFile -from pathlib import Path - -def test_zip(): - source = "c:/Users/petrk/Downloads/fbb_fbb100_sh0020_workfileAnimation_v010.zip" - dest = "c:/projects/temp/unzipped_with_python_111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\\2222222222222222222222222222222222222222222222222222222222222222222222222222222222" - - dest = Path(dest) - with _ZipFile(source, "r") as zip_ref: - zip_ref.extractall(dest.as_posix()) \ No newline at end of file diff --git a/vendor/configs/OpenColorIO-Configs b/vendor/configs/OpenColorIO-Configs deleted file mode 160000 index 0bb079c08be..00000000000 --- a/vendor/configs/OpenColorIO-Configs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0bb079c08be410030669cbf5f19ff869b88af953 diff --git a/vendor/instance.json b/vendor/instance.json deleted file mode 100644 index b1d623e85d7..00000000000 --- a/vendor/instance.json +++ /dev/null @@ -1,1133 +0,0 @@ -{ - 'family': 'render', - 'name': 'renderLightingDefault', - 'label': 'renderLightingDefault - local', - 'version': 1, - 'time': '', - 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', - 'subset': 'renderLightingDefault', - 'asset': 'shot01', - 'attachTo': False, - 'setMembers': '', - 'publish': True, - 'resolutionWidth': 1920.0, - 'resolutionHeight': 1080.0, - 'pixelAspect': 1, - 'frameStart': 0, - 'frameEnd': 0, - 'frameStep': 1, - 'handleStart': 0, - 'handleEnd': 0, - 'ignoreFrameHandleCheck': False, - 'renderer': 'aerender', - 'review': True, - 'priority': 50, - 'families': [ - 'render', - 'review', - 'ftrack', - 'slack' - ], - 'multipartExr': False, - 'convertToScanline': False, - 'tileRendering': False, - 'tilesX': 0, - 'tilesY': 0, - 'toBeRenderedOn': 'deadline', - 'deadlineSubmissionJob': None, - 'anatomyData': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-' - }, - 'outputDir': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001', - 'comp_name': '℗ renderLightingDefault', - 'comp_id': 1, - 'fps': 25, - 'projectEntity': { - '_id': ObjectId( - '5f2a6d2311e06a9818a1958b' - ), - 'name': 'petr_test', - 'created_d': datetime.datetime(2020, - 9, - 17, - 15, - 27, - 27, - 927000), - 'data': { - 'ftrackId': 'e5eda2bc-d682-11ea-afc1-92591a5b5e3e', - 'entityType': 'Project', - 'applications': [ - 'maya_2019', - 'photoshop_2021', - 'photoshop_2022', - 'harmony_17', - 'aftereffects_2022', - 'harmony_20', - 'nukestudio_12.2', - 'nukex_12.2', - 'hiero_12.2', - 'blender_2.93' - ], - 'library_project': True, - 'clipIn': 1, - 'resolutionWidth': 1920.0, - 'handleEnd': 0, - 'frameEnd': 1001, - 'resolutionHeight': 1080.0, - 'frameStart': 1001.0, - 'pixelAspect': 1.0, - 'fps': 25.0, - 'handleStart': 0, - 'clipOut': 1, - 'tools_env': [], - 'code': 'petr_test', - 'active': True - }, - 'type': 'project', - 'config': { - 'apps': [ - { - 'name': 'aftereffects/2022' - }, - { - 'name': 'maya/2019' - }, - { - 'name': 'hiero/12-2' - }, - { - 'name': 'photoshop/2021' - }, - { - 'name': 'nuke/12-2' - }, - { - 'name': 'photoshop/2022' - } - ], - 'tasks': { - 'Layout': { - 'short_name': 'lay' - }, - 'Setdress': { - 'short_name': 'dress' - }, - 'Previz': { - 'short_name': '' - }, - 'Generic': { - 'short_name': 'gener' - }, - 'Animation': { - 'short_name': 'anim' - }, - 'Modeling': { - 'short_name': 'mdl' - }, - 'Lookdev': { - 'short_name': 'look' - }, - 'FX': { - 'short_name': 'fx' - }, - 'Lighting': { - 'short_name': 'lgt' - }, - 'Compositing': { - 'short_name': 'comp' - }, - 'Tracking': { - 'short_name': '' - }, - 'Rigging': { - 'short_name': 'rig' - }, - 'Paint': { - 'short_name': 'paint' - }, - 'schedulle': { - 'short_name': '' - }, - 'Art': { - 'short_name': 'art' - }, - 'Texture': { - 'short_name': 'tex' - }, - 'Edit': { - 'short_name': 'edit' - } - }, - 'imageio': { - 'hiero': { - 'workfile': { - 'ocioConfigName': 'nuke-default', - 'ocioconfigpath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'workingSpace': 'linear', - 'sixteenBitLut': 'sRGB', - 'eightBitLut': 'sRGB', - 'floatLut': 'linear', - 'logLut': 'Cineon', - 'viewerLut': 'sRGB', - 'thumbnailLut': 'sRGB' - }, - 'regexInputs': { - 'inputs': [ - { - 'regex': '[^-a-zA-Z0-9](plateRef).*(?=mp4)', - 'colorspace': 'sRGB' - } - ] - } - }, - 'nuke': { - 'viewer': { - 'viewerProcess': 'sRGB' - }, - 'baking': { - 'viewerProcess': 'rec709' - }, - 'workfile': { - 'colorManagement': 'Nuke', - 'OCIO_config': 'nuke-default', - 'customOCIOConfigPath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'workingSpaceLUT': 'linear', - 'monitorLut': 'sRGB', - 'int8Lut': 'sRGB', - 'int16Lut': 'sRGB', - 'logLut': 'Cineon', - 'floatLut': 'linear' - }, - 'nodes': { - 'requiredNodes': [ - { - 'plugins': [ - 'CreateWriteRender' - ], - 'nukeNodeClass': 'Write', - 'knobs': [ - { - 'name': 'file_type', - 'value': 'exr' - }, - { - 'name': 'datatype', - 'value': '16 bit half' - }, - { - 'name': 'compression', - 'value': 'Zip (1 scanline)' - }, - { - 'name': 'autocrop', - 'value': 'True' - }, - { - 'name': 'tile_color', - 'value': '0xff0000ff' - }, - { - 'name': 'channels', - 'value': 'rgb' - }, - { - 'name': 'colorspace', - 'value': 'linear' - }, - { - 'name': 'create_directories', - 'value': 'True' - } - ] - }, - { - 'plugins': [ - 'CreateWritePrerender' - ], - 'nukeNodeClass': 'Write', - 'knobs': [ - { - 'name': 'file_type', - 'value': 'exr' - }, - { - 'name': 'datatype', - 'value': '16 bit half' - }, - { - 'name': 'compression', - 'value': 'Zip (1 scanline)' - }, - { - 'name': 'autocrop', - 'value': 'False' - }, - { - 'name': 'tile_color', - 'value': '0xadab1dff' - }, - { - 'name': 'channels', - 'value': 'rgb' - }, - { - 'name': 'colorspace', - 'value': 'linear' - }, - { - 'name': 'create_directories', - 'value': 'True' - } - ] - } - ], - 'customNodes': [] - }, - 'regexInputs': { - 'inputs': [ - { - 'regex': '[^-a-zA-Z0-9]beauty[^-a-zA-Z0-9]', - 'colorspace': 'linear' - } - ] - } - }, - 'maya': { - 'colorManagementPreference': { - 'configFilePath': { - 'windows': [], - 'darwin': [], - 'linux': [] - }, - 'renderSpace': 'scene-linear Rec 709/sRGB', - 'viewTransform': 'sRGB gamma' - } - } - }, - 'roots': { - 'work': { - 'windows': 'C:/projects', - 'darwin': '/Volumes/path', - 'linux': '/mnt/share/projects' - } - }, - 'templates': { - 'defaults': { - 'version_padding': 3, - 'version': 'v{version:0>{@version_padding}}', - 'frame_padding': 4, - 'frame': '{frame:0>{@frame_padding}}' - }, - 'work': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/work/{task}', - 'file': '{project[code]}_{asset}_{task}_{@version}<_{comment}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'render': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', - 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'publish': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}', - 'file': '{project[code]}_{asset}_{subset}_{@version}<_{output}><.{@frame}><_{udim}>.{ext}', - 'path': '{@folder}/{@file}', - 'thumbnail': '{thumbnail_root}/{project[name]}/{_id}_{thumbnail_type}.{ext}' - }, - 'hero': { - 'folder': '{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/hero', - 'file': '{project[code]}_{asset}_{subset}_hero<_{output}><.{frame}>.{ext}', - 'path': '{@folder}/{@file}' - }, - 'delivery': {}, - 'others': {} - } - }, - 'parent': None, - 'schema': 'avalon-core:project-2.0' - }, - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'frameStartHandle': 0, - 'frameEndHandle': 0, - 'byFrameStep': 1, - 'author': 'petrk', - 'expectedFiles': [ - 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting\\renders\\aftereffects\\petr_test_shot01_lighting_v001\\shot01_renderLightingDefault_v001.mov' - ], - 'slack_channel_message_profiles': [ - { - 'channels': [ - 'test_integration' - ], - 'upload_thumbnail': True, - 'message': 'Test message' - } - ], - 'slack_token': 'xoxb-1494100953104-2176825439264-jGqvQzfq9uZJPmyX5Q4o4TnP', - 'representations': [ - { - 'frameStart': 0, - 'frameEnd': 0, - 'name': 'mov', - 'ext': 'mov', - 'files': ' renderLightingDefault.mov', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'review' - ], - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' - ] - }, - { - 'name': 'thumbnail', - 'ext': 'jpg', - 'files': 'thumbnail.jpg', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'thumbnail' - ], - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' - ] - }, - { - 'frameStart': 0, - 'frameEnd': 0, - 'name': 'h264_mp4', - 'ext': 'mp4', - 'files': ' renderLightingDefault_h264burnin.mp4', - 'stagingDir': 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr', - 'tags': [ - 'review', - 'burnin', - 'ftrackreview' - ], - 'resolutionWidth': 1920, - 'resolutionHeight': 1080, - 'outputName': 'h264', - 'outputDef': { - 'ext': 'mp4', - 'tags': [ - 'burnin', - 'ftrackreview' - ], - 'burnins': [], - 'ffmpeg_args': { - 'video_filters': [], - 'audio_filters': [], - 'input': [ - '-apply_trc gamma22' - ], - 'output': [ - '-pix_fmt yuv420p', - '-crf 18', - '-intra' - ] - }, - 'filter': { - 'families': [ - 'render', - 'review', - 'ftrack' - ] - }, - 'overscan_crop': '', - 'overscan_color': [ - 0, - 0, - 0, - 255 - ], - 'width': 0, - 'height': 0, - 'bg_color': [ - 0, - 0, - 0, - 0 - ], - 'letter_box': { - 'enabled': False, - 'ratio': 0.0, - 'state': 'letterbox', - 'fill_color': [ - 0, - 0, - 0, - 255 - ], - 'line_thickness': 0, - 'line_color': [ - 255, - 0, - 0, - 255 - ] - }, - 'filename_suffix': 'h264' - }, - 'frameStartFtrack': 0, - 'frameEndFtrack': 0, - 'ffmpeg_cmd': 'C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\vendor\\bin\\ffmpeg\\windows\\bin\\ffmpeg -apply_trc gamma22 -i "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault.mov" -pix_fmt yuv420p -crf 18 -intra -y "C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264.mp4"', - 'published_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'publishedFiles': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - } - ], - 'assetEntity': { - '_id': ObjectId( - '5fabee9730a94666449245b7' - ), - 'name': 'shot01', - 'data': { - 'ftrackId': '0c5f548c-2425-11eb-b203-628b111fac3c', - 'entityType': 'Shot', - 'clipIn': 1, - 'resolutionWidth': 1920.0, - 'handleEnd': 0.0, - 'frameEnd': 1001, - 'resolutionHeight': 1080.0, - 'frameStart': 1001.0, - 'pixelAspect': 1.0, - 'fps': 25.0, - 'handleStart': 0.0, - 'clipOut': 1, - 'tools_env': [], - 'avalon_mongo_id': '5fabee9730a94666449245b7', - 'parents': [ - 'sequences', - 'seq01' - ], - 'hierarchy': 'sequences\\seq01', - 'tasks': { - 'lighting': { - 'type': 'Lighting' - }, - 'animation': { - 'type': 'Animation' - }, - 'compositing': { - 'type': 'Compositing' - } - }, - 'visualParent': ObjectId( - '5fabee9730a94666449245b6' - ) - }, - 'type': 'asset', - 'parent': ObjectId( - '5f2a6d2311e06a9818a1958b' - ), - 'schema': 'pype:asset-3.0' - }, - 'subsetEntity': { - '_id': ObjectId( - '61d723a271e6fce378bd428c' - ), - 'schema': 'openpype:subset-3.0', - 'type': 'subset', - 'name': 'renderLightingDefault', - 'data': { - 'families': [ - 'render', - 'review', - 'ftrack', - 'slack' - ] - }, - 'parent': ObjectId( - '5fabee9730a94666449245b7' - ) - }, - 'versionEntity': { - '_id': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'schema': 'openpype:version-3.0', - 'type': 'version', - 'parent': ObjectId( - '61d723a271e6fce378bd428c' - ), - 'name': 1, - 'data': { - 'families': [ - 'render', - 'render', - 'review', - 'ftrack', - 'slack' - ], - 'time': '20220106T181423Z', - 'author': 'petrk', - 'source': 'C:/projects/petr_test/sequences/seq01/shot01/work/lighting/petr_test_shot01_lighting_v001.aep', - 'comment': '', - 'machine': 'LAPTOP-UB778LHG', - 'fps': 25.0, - 'intent': '-', - 'frameStart': 0, - 'frameEnd': 0, - 'handleEnd': 0, - 'handleStart': 0, - 'inputLinks': [ - OrderedDict( - [ - ( - 'type', - 'generative' - ), - ( - 'id', - ObjectId( - '600ab849c411725a626b8c35' - )), - ( - 'linkedBy', - 'publish' - ) - ] - ) - ] - } - }, - 'transfers': [ - [ - 'C:\\Users\\petrk\\AppData\\Local\\Temp\\tmpwyhr_ecr\\ renderLightingDefault_h264burnin.mp4', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - ], - 'destination_list': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ], - 'published_representations': { - ObjectId( - '61d723a371e6fce378bd428e' - ): { - 'representation': { - '_id': ObjectId( - '61d723a371e6fce378bd428e' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'mov', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'ext': 'mov', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'mov', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a371e6fce378bd4291' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.mov', - 'size': 1654788, - 'hash': 'petr_test_shot01_renderLightingDefault_v001,mov|1641489300,6230524|1654788', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 15, - 264448) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'representation': 'mov', - 'ext': 'mov' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov' - ] - }, - ObjectId( - '61d723a371e6fce378bd4292' - ): { - 'representation': { - '_id': ObjectId( - '61d723a371e6fce378bd4292' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'thumbnail', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'ext': 'jpg', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'jpg', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a371e6fce378bd4295' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001.jpg', - 'size': 871, - 'hash': 'petr_test_shot01_renderLightingDefault_v001,jpg|1641489301,1720147|871', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 15, - 825446) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'representation': 'jpg', - 'ext': 'jpg' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg' - ] - }, - ObjectId( - '61d723a471e6fce378bd4296' - ): { - 'representation': { - '_id': ObjectId( - '61d723a471e6fce378bd4296' - ), - 'schema': 'openpype:representation-2.0', - 'type': 'representation', - 'parent': ObjectId( - '61d723a371e6fce378bd428d' - ), - 'name': 'h264_mp4', - 'data': { - 'path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'template': '{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{ext}' - }, - 'dependencies': [], - 'context': { - 'root': { - 'work': 'C:/projects' - }, - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'hierarchy': 'sequences/seq01', - 'asset': 'shot01', - 'family': 'render', - 'subset': 'renderLightingDefault', - 'version': 1, - 'output': 'h264', - 'ext': 'mp4', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'representation': 'mp4', - 'username': 'petrk' - }, - 'files': [ - { - '_id': ObjectId( - '61d723a471e6fce378bd4299' - ), - 'path': '{root[work]}/petr_test/sequences/seq01/shot01/publish/render/renderLightingDefault/v001/petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'size': 10227, - 'hash': 'petr_test_shot01_renderLightingDefault_v001_h264,mp4|1641489313,659368|10227', - 'sites': [ - { - 'name': 'studio', - 'created_dt': datetime.datetime(2022, - 1, - 6, - 18, - 15, - 16, - 53445) - } - ] - } - ] - }, - 'anatomy_data': { - 'project': { - 'name': 'petr_test', - 'code': 'petr_test' - }, - 'asset': 'shot01', - 'parent': 'seq01', - 'hierarchy': 'sequences/seq01', - 'task': { - 'name': 'lighting', - 'type': 'Lighting', - 'short': 'lgt' - }, - 'username': 'petrk', - 'app': 'aftereffects', - 'd': '6', - 'dd': '06', - 'ddd': 'Thu', - 'dddd': 'Thursday', - 'm': '1', - 'mm': '01', - 'mmm': 'Jan', - 'mmmm': 'January', - 'yy': '22', - 'yyyy': '2022', - 'H': '18', - 'HH': '18', - 'h': '6', - 'hh': '06', - 'ht': 'PM', - 'M': '14', - 'MM': '14', - 'S': '23', - 'SS': '23', - 'version': 1, - 'subset': 'renderLightingDefault', - 'family': 'render', - 'intent': '-', - 'resolution_width': 1920, - 'resolution_height': 1080, - 'fps': 25, - 'output': 'h264', - 'representation': 'mp4', - 'ext': 'mp4' - }, - 'published_files': [ - 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4' - ] - } - }, - 'ftrackComponentsList': [ - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': True, - 'component_data': { - 'name': 'thumbnail' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'ftrackreview-mp4', - 'metadata': { - 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' - } - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'thumbnail_src' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.jpg', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'ftrackreview-mp4_src', - 'metadata': { - 'ftr_meta': '{"frameIn": 0, "frameOut": 1, "frameRate": 25.0}' - } - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001_h264.mp4', - 'component_location': , - 'component': - }, - { - 'assettype_data': { - 'short': 'render' - }, - 'asset_data': { - 'name': 'renderLightingDefault' - }, - 'assetversion_data': { - 'version': 1 - }, - 'component_overwrite': False, - 'thumbnail': False, - 'component_data': { - 'name': 'mov' - }, - 'component_path': 'C:\\projects\\petr_test\\sequences\\seq01\\shot01\\publish\\render\\renderLightingDefault\\v001\\petr_test_shot01_renderLightingDefault_v001.mov', - 'component_location': , - 'component': - } - ], - 'ftrackIntegratedAssetVersions': [ - - ] -} \ No newline at end of file diff --git a/vendor/response.json b/vendor/response.json deleted file mode 100644 index 26a4fae2fdc..00000000000 --- a/vendor/response.json +++ /dev/null @@ -1 +0,0 @@ -{status: 200, headers: {'date': 'Tue, 11 Jan 2022 11:08:57 GMT', 'server': 'Apache', 'x-powered-by': 'HHVM/4.128.0', 'access-control-allow-origin': '*', 'referrer-policy': 'no-referrer', 'x-slack-backend': 'r', 'strict-transport-security': 'max-age=31536000; includeSubDomains; preload', 'access-control-allow-headers': 'slack-route, x-slack-version-ts, x-b3-traceid, x-b3-spanid, x-b3-parentspanid, x-b3-sampled, x-b3-flags', 'access-control-expose-headers': 'x-slack-req-id, retry-after', 'x-oauth-scopes': 'chat:write,chat:write.public,files:write,chat:write.customize', 'x-accepted-oauth-scopes': 'chat:write', 'expires': 'Mon, 26 Jul 1997 05:00:00 GMT', 'cache-control': 'private, no-cache, no-store, must-revalidate', 'pragma': 'no-cache', 'x-xss-protection': '0', 'x-content-type-options': 'nosniff', 'x-slack-req-id': '9d1d11399a44c8751f89bb4dcd2b91fb', 'vary': 'Accept-Encoding', 'content-type': 'application/json; charset=utf-8', 'x-envoy-upstream-service-time': '52', 'x-backend': 'main_normal main_bedrock_normal_with_overflow main_canary_with_overflow main_bedrock_canary_with_overflow main_control_with_overflow main_bedrock_control_with_overflow', 'x-server': 'slack-www-hhvm-main-iad-qno3', 'x-slack-shared-secret-outcome': 'no-match', 'via': 'envoy-www-iad-omsy, envoy-edge-iad-bgfx', 'x-edge-backend': 'envoy-www', 'x-slack-edge-shared-secret-outcome': 'no-match', 'connection': 'close', 'transfer-encoding': 'chunked'}, body: {"ok":true,"channel":"C024DUFM8MB","ts":"1641899337.001100","message":{"type":"message","subtype":"bot_message","text":"RenderCompositingDefault published for Jungle\n\nHere should be link to review C:\\projects\\petr_test\\assets\\locations\\Jungle\\publish\\render\\renderCompositingDefault\\v253\\petr_test_Jungle_renderCompositingDefault_v253_h264.mp4\n\n Attachment links: \n\n","ts":"1641899337.001100","username":"OpenPypeNotifier","icons":{"image_48":"https:\/\/s3-us-west-2.amazonaws.com\/slack-files2\/bot_icons\/2022-01-07\/2934353684385_48.png"},"bot_id":"B024H0P0CAE"}} \ No newline at end of file diff --git a/vendor/temp.json b/vendor/temp.json deleted file mode 100644 index 089174d26c3..00000000000 --- a/vendor/temp.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - project(name: "demo_Big_Episodic") { - representations( - first: 0, - after: 0, - localSite: "local", - remoteSite: "local" - ) { - edges { - node { - id - name - # Sorry: totalSize is not implemented, but it will be - # totalSize - fileCount - # overal sync state - localState{ - status - size - timestamp - } - remoteState{ - status - size - timestamp - } - # crawl to the top to get parent info - version { - version - subset { - family - name - folder { - name - } - } - } - } - } - pageInfo { - hasNextPage - endCursor - } - } - } -} \ No newline at end of file From e0f46635f5045ead05226504ed48b9f972145060 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:32:18 +0200 Subject: [PATCH 06/10] OP-4181 - clean up after review comments --- openpype/plugins/publish/integrate_hero_version.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index adc629352e2..661975993b5 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -14,7 +14,6 @@ ) from openpype.client.operations import ( OperationsSession, - _create_or_convert_to_mongo_id, new_hero_version_doc, prepare_hero_version_update_data, prepare_representation_update_data, @@ -193,9 +192,13 @@ def integrate_instance( op_session = OperationsSession() + entity_id = None + if old_version: + entity_id = old_version["_id"] new_hero_version = new_hero_version_doc( src_version_entity["_id"], - src_version_entity["parent"] + src_version_entity["parent"], + entity_id=entity_id ) if old_version: @@ -408,7 +411,7 @@ def integrate_instance( # Create representation else: - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre.pop("_id", None) op_session.create_entity(project_name, "representation", repre) From f6f5e77f3c4f8b384feba08cfb5713829192e532 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:34:46 +0200 Subject: [PATCH 07/10] OP-4181 - clean up after review comments - missed line --- openpype/plugins/publish/integrate_hero_version.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 661975993b5..84960ec6096 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -443,8 +443,7 @@ def integrate_instance( archived_repre["_id"], changes) else: - repre["old_id"] = repre["_id"] - repre["_id"] = _create_or_convert_to_mongo_id(None) + repre["old_id"] = repre.pop("_id") repre["type"] = "archived_representation" op_session.create_entity(project_name, "representation", repre) From b4644457501ee22d1f37ea0217835662365b19f3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:38:40 +0200 Subject: [PATCH 08/10] OP-4181 - fix - wrong entity type --- openpype/plugins/publish/integrate_hero_version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 84960ec6096..6d553a7a3cb 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -445,7 +445,8 @@ def integrate_instance( else: repre["old_id"] = repre.pop("_id") repre["type"] = "archived_representation" - op_session.create_entity(project_name, "representation", + op_session.create_entity(project_name, + "archived_representation", repre) op_session.commit() From b198e922b250fbfe5957be7b6e7c7d3b95a5517d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 6 Oct 2022 17:57:24 +0200 Subject: [PATCH 09/10] OP-4181 - fix - wrong entity type --- openpype/plugins/publish/integrate_hero_version.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 6d553a7a3cb..398a0226dff 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -389,7 +389,7 @@ def integrate_instance( old_repre, repre) op_session.update_entity( project_name, - "representation", + old_repre["type"], old_repre["_id"], update_data ) @@ -404,7 +404,7 @@ def integrate_instance( archived_repre, repre) op_session.update_entity( project_name, - "representation", + old_repre["type"], archived_repre["_id"], update_data ) From adc5d044d4047b9c403a359602205a195e68a9c5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 7 Oct 2022 12:20:09 +0200 Subject: [PATCH 10/10] OP-4181 - removed unneeded line _id set already higher when item is created. Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/plugins/publish/integrate_hero_version.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index 398a0226dff..5f4d2847407 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -203,7 +203,6 @@ def integrate_instance( if old_version: self.log.debug("Replacing old hero version.") - new_hero_version["_id"] = old_version["_id"] update_data = prepare_hero_version_update_data( old_version, new_hero_version )