From 392dd02ba94e7f1da1247c39e4daaba3999c362b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Tue, 21 Dec 2021 12:51:11 +0000 Subject: [PATCH 001/302] xml batch 1 --- .../help/validate_abc_primitive_to_detail.xml | 15 ++++++ .../help/validate_alembic_face_sets.xml | 22 +++++++++ .../help/validate_alembic_input_node.xml | 21 +++++++++ .../help/validate_animation_settings.xml | 31 ++++++++++++ .../publish/help/validate_vdb_input_node.xml | 22 +++++++++ .../plugins/publish/valiate_vdb_input_node.py | 47 ------------------- .../publish/validate_context_with_error.py | 1 + 7 files changed, 112 insertions(+), 47 deletions(-) create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml b/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml new file mode 100644 index 0000000000..0e2aa6c1f4 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml @@ -0,0 +1,15 @@ + + + +Primitive to Detail +## Invalid Primitive to Detail Attributes + +Primitives with inconsistent primitive to detail attributes were found. + +{message} + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml new file mode 100644 index 0000000000..7bc149d7c3 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml @@ -0,0 +1,22 @@ + + + +Alembic ROP Face Sets +## Invalid Alembic ROP Face Sets + +When groups are saved as Face Sets with the Alembic these show up +as shadingEngine connections in Maya - however, with animated groups +these connections in Maya won't work as expected, it won't update per +frame. Additionally, it can break shader assignments in some cases +where it requires to first break this connection to allow a shader to +be assigned. + +It is allowed to include Face Sets, so only an issue is logged to +identify that it could introduce issues down the pipeline. + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml new file mode 100644 index 0000000000..5be722ccb2 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml @@ -0,0 +1,21 @@ + + + +Alembic input +## Invalid Alembic input + +The node connected to the output is incorrect. +It contains primitive types that are not supported for alembic output. + +Problematic primitive is of type {primitive_type} + + + + + +The connected node cannot be of the following types for Alembic: + - VDB + - Volume + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml b/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml new file mode 100644 index 0000000000..8a2a396783 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml @@ -0,0 +1,31 @@ + + + +Frame token in output +## Frame range is missing frame token + +This validator will check the output parameter of the node if +the Valid Frame Range is not set to 'Render Current Frame' + +No frame token found in {nodepath} + +### How to repair? +Your you need to add `$F4` or similar frame based token to your path. +**Example:** + Good: 'my_vbd_cache.$F4.vdb' + Bad: 'my_vbd_cache.vdb' + + + + + + +If you render out a frame range it is mandatory to have the +frame token - '$F4' or similar - to ensure that each frame gets +written. If this is not the case you will override the same file +every time a frame is written out. + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml new file mode 100644 index 0000000000..8cc186a183 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml @@ -0,0 +1,22 @@ + + + +VDB input node +## Invalid VDB input node + +Validate that the node connected to the output node is of type VDB. + +Regardless of the amount of VDBs created the output will need to have an +equal amount of VDBs, points, primitives and vertices + +A VDB is an inherited type of Prim, holds the following data: + - Primitives: 1 + - Points: 1 + - Vertices: 1 + - VDBs: 1 + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py deleted file mode 100644 index 0ae1bc94eb..0000000000 --- a/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py +++ /dev/null @@ -1,47 +0,0 @@ -import pyblish.api -import openpype.api - - -class ValidateVDBInputNode(pyblish.api.InstancePlugin): - """Validate that the node connected to the output node is of type VDB. - - Regardless of the amount of VDBs create the output will need to have an - equal amount of VDBs, points, primitives and vertices - - A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - """ - - order = openpype.api.ValidateContentsOrder + 0.1 - families = ["vdbcache"] - hosts = ["houdini"] - label = "Validate Input Node (VDB)" - - def process(self, instance): - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Node connected to the output node is not" "of type VDB!" - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance.data["output_node"] - - prims = node.geometry().prims() - nr_of_prims = len(prims) - - nr_of_points = len(node.geometry().points()) - if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") - return [instance] - - for prim in prims: - if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") - return [instance] diff --git a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py index 46e996a569..20fb47513e 100644 --- a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py +++ b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py @@ -2,6 +2,7 @@ import pyblish.api from openpype.pipeline import PublishValidationError + class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" From dda5ddaa98537b60d02e5ca80a48b5208af9a788 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:09:09 +0100 Subject: [PATCH 002/302] Implemented change of validators to new publisher style for AE --- .../publish/help/validate_instance_asset.xml | 21 +++++++++++ .../publish/help/validate_scene_settings.xml | 36 +++++++++++++++++++ .../publish/validate_instance_asset.py | 12 +++---- .../publish/validate_scene_settings.py | 27 +++++++++++--- 4 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml create mode 100644 openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml new file mode 100644 index 0000000000..580b0e552d --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -0,0 +1,21 @@ + + + +Subset context + +## Invalid subset context + +Context of the given subset doesn't match your current scene. + +### How to repair? + +You can fix this with "repair" button on the right. + + +### __Detailed Info__ (optional) + +This might happen if you are reuse old workfile and open it in different context. + (Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..603ab4805d --- /dev/null +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,36 @@ + + + +Scene setting + +## Invalid scene setting found + +One of the settings in a scene doesn't match to asset settings in database. + + Invalid setting: + {invalid_setting_str} + +### How to repair? + +Change {invalid_keys_str} in the scene OR change them in asset database if they are wrong there. + + +### __Detailed Info__ (optional) + +This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. + Either value in the database or in the scene is wrong. + + + +Scene file doesn't exist + +## Scene file doesn't exist + +Collected scene {scene_url} doesn't exist. + +### How to repair? + +Re-save file, start publish from the beginning again. + + + \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py index eff89adcb3..2c8c1b4312 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -2,6 +2,7 @@ from avalon import api import pyblish.api import openpype.api from avalon import aftereffects +from openpype.pipeline import PublishValidationError class ValidateInstanceAssetRepair(pyblish.api.Action): @@ -29,7 +30,6 @@ class ValidateInstanceAssetRepair(pyblish.api.Action): data["asset"] = api.Session["AVALON_ASSET"] stub.imprint(instance[0], data) - class ValidateInstanceAsset(pyblish.api.InstancePlugin): """Validate the instance asset is the current selected context asset. @@ -53,9 +53,9 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): current_asset = api.Session["AVALON_ASSET"] msg = ( f"Instance asset {instance_asset} is not the same " - f"as current context {current_asset}. PLEASE DO:\n" - f"Repair with 'A' action to use '{current_asset}'.\n" - f"If that's not correct value, close workfile and " - f"reopen via Workfiles!" + f"as current context {current_asset}." ) - assert instance_asset == current_asset, msg + + # assert instance_asset == current_asset, msg + if instance_asset != current_asset: + raise PublishValidationError(msg, "Subset context", DESCRIPTION) \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py index 7fba11957c..50e55599e2 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py @@ -7,6 +7,7 @@ import pyblish.api from avalon import aftereffects +from openpype.pipeline import PublishXmlValidationError import openpype.hosts.aftereffects.api as api stub = aftereffects.stub() @@ -103,12 +104,14 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): self.log.info("current_settings:: {}".format(current_settings)) invalid_settings = [] + invalid_keys = set() for key, value in expected_settings.items(): if value != current_settings[key]: invalid_settings.append( "{} expected: {} found: {}".format(key, value, current_settings[key]) ) + invalid_keys.add(key) if ((expected_settings.get("handleStart") or expected_settings.get("handleEnd")) @@ -120,7 +123,23 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): msg = "Found invalid settings:\n{}".format( "\n".join(invalid_settings) ) - assert not invalid_settings, msg - assert os.path.exists(instance.data.get("source")), ( - "Scene file not found (saved under wrong name)" - ) + + if invalid_settings: + invalid_keys_str = ",".join(invalid_keys) + formatting_data = { + "invalid_setting_str": msg, + "invalid_keys_str": invalid_keys_str + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + if not os.path.exists(instance.data.get("source")): + scene_url = instance.data.get("source") + msg = "Scene file {} not found (saved under wrong name)".format( + scene_url + ) + formatting_data = { + "scene_url": scene_url + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) From 8a67dba15d67ecc8092b9e53efd65a0f4d79b143 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:18:26 +0100 Subject: [PATCH 003/302] Fix formatting --- .../plugins/publish/help/validate_instance_asset.xml | 2 +- .../plugins/publish/help/validate_scene_settings.xml | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml index 580b0e552d..13f03a9b9a 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_instance_asset.xml @@ -15,7 +15,7 @@ You can fix this with "repair" button on the right. ### __Detailed Info__ (optional) This might happen if you are reuse old workfile and open it in different context. - (Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) +(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) \ No newline at end of file diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 603ab4805d..983dde42ce 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -6,22 +6,21 @@ ## Invalid scene setting found One of the settings in a scene doesn't match to asset settings in database. - - Invalid setting: - {invalid_setting_str} +Invalid setting: +{invalid_setting_str} ### How to repair? -Change {invalid_keys_str} in the scene OR change them in asset database if they are wrong there. +Change {invalid_keys_str} setting in the scene OR change them in asset database if they are wrong there. ### __Detailed Info__ (optional) This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. - Either value in the database or in the scene is wrong. +Either value in the database or in the scene is wrong. - + Scene file doesn't exist ## Scene file doesn't exist From 3e195d58ccc16c33498159a364ca758ad604d9cf Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:37:41 +0100 Subject: [PATCH 004/302] Fix formatting --- .../plugins/publish/help/validate_scene_settings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 983dde42ce..6dc51d9953 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -6,12 +6,12 @@ ## Invalid scene setting found One of the settings in a scene doesn't match to asset settings in database. -Invalid setting: + {invalid_setting_str} ### How to repair? -Change {invalid_keys_str} setting in the scene OR change them in asset database if they are wrong there. +Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there. ### __Detailed Info__ (optional) From a14aaaf9b8e4feb4245bae8a4e07aa7a91043d33 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 21 Dec 2021 14:48:08 +0100 Subject: [PATCH 005/302] Fix formatting --- .../plugins/publish/help/validate_scene_settings.xml | 2 +- .../plugins/publish/validate_instance_asset.py | 5 ++--- .../plugins/publish/validate_scene_settings.py | 8 ++++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml index 6dc51d9953..36fa90456e 100644 --- a/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml +++ b/openpype/hosts/aftereffects/plugins/publish/help/validate_scene_settings.xml @@ -20,7 +20,7 @@ This error is shown when for example resolution in the scene doesn't match to re Either value in the database or in the scene is wrong. - + Scene file doesn't exist ## Scene file doesn't exist diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py index 2c8c1b4312..491e07b6c4 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_instance_asset.py @@ -2,7 +2,7 @@ from avalon import api import pyblish.api import openpype.api from avalon import aftereffects -from openpype.pipeline import PublishValidationError +from openpype.pipeline import PublishXmlValidationError class ValidateInstanceAssetRepair(pyblish.api.Action): @@ -56,6 +56,5 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin): f"as current context {current_asset}." ) - # assert instance_asset == current_asset, msg if instance_asset != current_asset: - raise PublishValidationError(msg, "Subset context", DESCRIPTION) \ No newline at end of file + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py index 50e55599e2..0e7a54005a 100644 --- a/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/aftereffects/plugins/publish/validate_scene_settings.py @@ -126,8 +126,12 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): if invalid_settings: invalid_keys_str = ",".join(invalid_keys) + break_str = "
" + invalid_setting_str = "Found invalid settings:
{}".\ + format(break_str.join(invalid_settings)) + formatting_data = { - "invalid_setting_str": msg, + "invalid_setting_str": invalid_setting_str, "invalid_keys_str": invalid_keys_str } raise PublishXmlValidationError(self, msg, @@ -141,5 +145,5 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): formatting_data = { "scene_url": scene_url } - raise PublishXmlValidationError(self, msg, + raise PublishXmlValidationError(self, msg, key="file_not_found", formatting_data=formatting_data) From 28040a2f6a4ae863578edf49baebd7bd5a85a514 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 14:55:14 +0100 Subject: [PATCH 006/302] exception description can be different per instance under title --- .../publisher/widgets/validations_widget.py | 83 ++++++++++++++----- 1 file changed, 64 insertions(+), 19 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 09e56d64cc..9f550725a5 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -10,6 +10,9 @@ from .widgets import ( ClickableFrame, IconValuePixmapLabel ) +from ..constants import ( + INSTANCE_ID_ROLE +) class ValidationErrorInstanceList(QtWidgets.QListView): @@ -47,6 +50,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): if there is a list (Valdation error may happen on context). """ selected = QtCore.Signal(int) + instance_changed = QtCore.Signal(int) def __init__(self, index, error_info, parent): super(ValidationErrorTitleWidget, self).__init__(parent) @@ -72,24 +76,37 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): title_frame_layout.addWidget(label_widget) instances_model = QtGui.QStandardItemModel() - instances = error_info["instances"] + error_info = error_info["error_info"] + + help_text_by_instance_id = {} context_validation = False if ( - not instances - or (len(instances) == 1 and instances[0] is None) + not error_info + or (len(error_info) == 1 and error_info[0][0] is None) ): context_validation = True toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) + help_text_by_instance_id[None] = error_info[0][1] else: items = [] - for instance in instances: + for instance, exception in error_info: label = instance.data.get("label") or instance.data.get("name") item = QtGui.QStandardItem(label) item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) - item.setData(instance.id) + item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) + dsc = exception.description + detail = exception.detail + if detail: + dsc += "

{}".format(detail) + + help_text = dsc + if commonmark: + help_text = commonmark.commonmark(dsc) + + help_text_by_instance_id[instance.id] = help_text instances_model.invisibleRootItem().appendRows(items) @@ -114,6 +131,10 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): if not context_validation: toggle_instance_btn.clicked.connect(self._on_toggle_btn_click) + instances_view.selectionModel().selectionChanged.connect( + self._on_seleciton_change + ) + self._title_frame = title_frame self._toggle_instance_btn = toggle_instance_btn @@ -121,6 +142,9 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._instances_model = instances_model self._instances_view = instances_view + self._context_validation = context_validation + self._help_text_by_instance_id = help_text_by_instance_id + def _mouse_release_callback(self): """Mark this widget as selected on click.""" self.set_selected(True) @@ -145,6 +169,17 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._title_frame.setProperty("selected", value) self._title_frame.style().polish(self._title_frame) + def current_desctiption_text(self): + if self._context_validation: + return self._help_text_by_instance_id[None] + index = self._instances_view.currentIndex() + # TODO make sure instance is selected + if not index.isValid(): + index = self._instances_model.index(0, 0) + + indence_id = index.data(INSTANCE_ID_ROLE) + return self._help_text_by_instance_id[indence_id] + def set_selected(self, selected=None): """Change selected state of widget.""" if selected is None: @@ -167,6 +202,9 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): else: self._toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) + def _on_seleciton_change(self): + self.instance_changed.emit(self._index) + class ActionButton(ClickableFrame): """Plugin's action callback button. @@ -440,28 +478,28 @@ class ValidationsWidget(QtWidgets.QWidget): errors_by_title = [] for plugin_info in errors: titles = [] - exception_by_title = {} - instances_by_title = {} + error_info_by_title = {} for error_info in plugin_info["errors"]: exception = error_info["exception"] title = exception.title if title not in titles: titles.append(title) - instances_by_title[title] = [] - exception_by_title[title] = exception - instances_by_title[title].append(error_info["instance"]) + error_info_by_title[title] = [] + error_info_by_title[title].append( + (error_info["instance"], exception) + ) for title in titles: errors_by_title.append({ "plugin": plugin_info["plugin"], - "exception": exception_by_title[title], - "instances": instances_by_title[title] + "error_info": error_info_by_title[title] }) for idx, item in enumerate(errors_by_title): widget = ValidationErrorTitleWidget(idx, item, self) widget.selected.connect(self._on_select) + widget.instance_changed.connect(self._on_instance_change) self._errors_layout.addWidget(widget) self._title_widgets[idx] = widget self._error_info[idx] = item @@ -480,11 +518,18 @@ class ValidationsWidget(QtWidgets.QWidget): self._previous_select = self._title_widgets[index] error_item = self._error_info[index] - - dsc = error_item["exception"].description - if commonmark: - html = commonmark.commonmark(dsc) - self._error_details_input.setHtml(html) - else: - self._error_details_input.setMarkdown(dsc) self._actions_widget.set_plugin(error_item["plugin"]) + + self._update_description() + + def _on_instance_change(self, index): + if self._previous_select and self._previous_select.index != index: + return + self._update_description() + + def _update_description(self): + description = self._previous_select.current_desctiption_text() + if commonmark: + self._error_details_input.setHtml(description) + else: + self._error_details_input.setMarkdown(description) From 72cdaecef46e0fecb72f880b5a0681fc7f4f6ca9 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 21 Dec 2021 15:02:13 +0100 Subject: [PATCH 007/302] fix context exception handling --- .../publisher/widgets/validations_widget.py | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 9f550725a5..ba4df2eb8e 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -68,8 +68,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): toggle_instance_btn.setArrowType(QtCore.Qt.RightArrow) toggle_instance_btn.setMaximumWidth(14) - exception = error_info["exception"] - label_widget = QtWidgets.QLabel(exception.title, title_frame) + label_widget = QtWidgets.QLabel(error_info["title"], title_frame) title_frame_layout = QtWidgets.QHBoxLayout(title_frame) title_frame_layout.addWidget(toggle_instance_btn) @@ -86,7 +85,8 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): ): context_validation = True toggle_instance_btn.setArrowType(QtCore.Qt.NoArrow) - help_text_by_instance_id[None] = error_info[0][1] + description = self._prepare_description(error_info[0][1]) + help_text_by_instance_id[None] = description else: items = [] for instance, exception in error_info: @@ -97,16 +97,8 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): ) item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) - dsc = exception.description - detail = exception.detail - if detail: - dsc += "

{}".format(detail) - - help_text = dsc - if commonmark: - help_text = commonmark.commonmark(dsc) - - help_text_by_instance_id[instance.id] = help_text + description = self._prepare_description(exception) + help_text_by_instance_id[instance.id] = description instances_model.invisibleRootItem().appendRows(items) @@ -145,6 +137,17 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._context_validation = context_validation self._help_text_by_instance_id = help_text_by_instance_id + def _prepare_description(self, exception): + dsc = exception.description + detail = exception.detail + if detail: + dsc += "

{}".format(detail) + + description = dsc + if commonmark: + description = commonmark.commonmark(dsc) + return description + def _mouse_release_callback(self): """Mark this widget as selected on click.""" self.set_selected(True) @@ -493,7 +496,8 @@ class ValidationsWidget(QtWidgets.QWidget): for title in titles: errors_by_title.append({ "plugin": plugin_info["plugin"], - "error_info": error_info_by_title[title] + "error_info": error_info_by_title[title], + "title": title }) for idx, item in enumerate(errors_by_title): From 4759b6db371c9724748be4099ce19f5e191bc3c1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:00:50 +0100 Subject: [PATCH 008/302] raise PublishXmlValidationError in validate asset name --- .../publish/help/validate_asset_name.xml | 22 ++++++++++++++++++ .../plugins/publish/validate_asset_name.py | 23 ++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml new file mode 100644 index 0000000000..ed8e36b1d9 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -0,0 +1,22 @@ + + + +Subset context +## Invalid subset context + +Context of the given subset doesn't match your current scene. + +### How to repair? + +Yout can fix with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. + +After that restart publishing with Reload button. + + +### How could this happen? + +The subset was created in different scene with different context +or the scene file was copy pasted from different context. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py index 4ce8d5347d..199b9a3b19 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_asset_name.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import pipeline +from openpype.pipeline import PublishXmlValidationError class FixAssetNames(pyblish.api.Action): @@ -27,7 +28,7 @@ class FixAssetNames(pyblish.api.Action): pipeline._write_instances(new_instance_items) -class ValidateMissingLayers(pyblish.api.ContextPlugin): +class ValidateAssetNames(pyblish.api.ContextPlugin): """Validate assset name present on instance. Asset name on instance should be the same as context's. @@ -48,8 +49,18 @@ class ValidateMissingLayers(pyblish.api.ContextPlugin): instance_label = ( instance.data.get("label") or instance.data["name"] ) - raise AssertionError(( - "Different asset name on instance then context's." - " Instance \"{}\" has asset name: \"{}\"" - " Context asset name is: \"{}\"" - ).format(instance_label, asset_name, context_asset_name)) + + raise PublishXmlValidationError( + self, + ( + "Different asset name on instance then context's." + " Instance \"{}\" has asset name: \"{}\"" + " Context asset name is: \"{}\"" + ).format( + instance_label, asset_name, context_asset_name + ), + formatting_data={ + "expected_asset": context_asset_name, + "found_asset": asset_name + } + ) From f13923d31ccae0a0455fb36bb09dd225c6b4e0e3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:04:10 +0100 Subject: [PATCH 009/302] raise PublishXmlValidationError in validate duplicated layer names --- .../help/validate_duplicated_layer_names.xml | 22 +++++++++++++++++++ .../validate_duplicated_layer_names.py | 15 +++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml new file mode 100644 index 0000000000..5d798544c0 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_duplicated_layer_names.xml @@ -0,0 +1,22 @@ + + + +Layer names +## Duplicated layer names + +Can't determine which layers should be published because there are duplicated layer names in the scene. + +### Duplicated layer names + +{layer_names} + +*Check layer names for all subsets in list on left side.* + +### How to repair? + +Hide/rename/remove layers that should not be published. + +If all of them should be published then you have duplicated subset names in the scene. In that case you have to recrete them and use different variant name. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py index efccf19ef9..9f61bdbcd0 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_duplicated_layer_names.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateLayersGroup(pyblish.api.InstancePlugin): @@ -30,14 +31,20 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): "\"{}\"".format(layer_name) for layer_name in duplicated_layer_names ]) - - # Raise an error - raise AssertionError( + detail_lines = [ + "- {}".format(layer_name) + for layer_name in set(duplicated_layer_names) + ] + raise PublishXmlValidationError( + self, ( "Layers have duplicated names for instance {}." # Description what's wrong " There are layers with same name and one of them is marked" " for publishing so it is not possible to know which should" " be published. Please look for layers with names: {}" - ).format(instance.data["label"], layers_msg) + ).format(instance.data["label"], layers_msg), + formatting_data={ + "layer_names": "
".join(detail_lines) + } ) From 3417dc716af9c1081a51ab9c132bcded0330623d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:12:42 +0100 Subject: [PATCH 010/302] raise PublishXmlValidationError in validate layers visibility --- .../help/validate_layers_visibility.xml | 20 +++++++++++++++++ .../publish/validate_layers_visibility.py | 22 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml new file mode 100644 index 0000000000..fc69d5fd7b --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -0,0 +1,20 @@ + + + +Layers visiblity +## All layers are not visible + +All layers for subset "{instance_name}" are hidden. + +### Layer names for **{instance_name}** + +{layer_names} + +*Check layer names for all subsets in list on left side.* + +### How to repair? + +Make sure that at least one layer in the scene is visible or disable the subset before hitting publish button after refresh. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py index 74ef34169e..7ea0587b8f 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_layers_visibility.py @@ -1,6 +1,8 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError +# TODO @iLLiCiTiT add repair action to disable instances? class ValidateLayersVisiblity(pyblish.api.InstancePlugin): """Validate existence of renderPass layers.""" @@ -9,8 +11,26 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin): families = ["review", "renderPass", "renderLayer"] def process(self, instance): + layer_names = set() for layer in instance.data["layers"]: + layer_names.add(layer["name"]) if layer["visible"]: return - raise AssertionError("All layers of instance are not visible.") + instance_label = ( + instance.data.get("label") or instance.data["name"] + ) + + raise PublishXmlValidationError( + self, + "All layers of instance \"{}\" are not visible.".format( + instance_label + ), + formatting_data={ + "instance_name": instance_label, + "layer_names": "
".join([ + "- {}".format(layer_name) + for layer_name in layer_names + ]) + } + ) From fba2191226388f73c3dbf5e7c20773e58cb1259c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 11:57:31 +0100 Subject: [PATCH 011/302] raise PublishXmlValidationError in validate marks --- .../publish/help/validate_asset_name.xml | 2 +- .../plugins/publish/help/validate_marks.xml | 21 ++++++++++ .../tvpaint/plugins/publish/validate_marks.py | 38 ++++++++++++++++--- 3 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml index ed8e36b1d9..33a9ca4247 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_asset_name.xml @@ -8,7 +8,7 @@ Context of the given subset doesn't match your current scene. ### How to repair? -Yout can fix with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. +Yout can fix this with "Repair" button on the right. This will use '{expected_asset}' asset name and overwrite '{found_asset}' asset name in scene metadata. After that restart publishing with Reload button.
diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml new file mode 100644 index 0000000000..f0e01ebaa7 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_marks.xml @@ -0,0 +1,21 @@ + + + +Frame range +## Invalid render frame range + +Scene frame range which will be rendered is defined by MarkIn and MarkOut. Expected frame range is {expected_frame_range} and current frame range is {current_frame_range}. + +It is also required that MarkIn and MarkOut are enabled in the scene. Their color is highlighted on timeline when are enabled. + +- MarkIn is {mark_in_enable_state} +- MarkOut is {mark_out_enable_state} + +### How to repair? + +Yout can fix this with "Repair" button on the right. That will change MarkOut to {expected_mark_out}. + +Or you can manually modify MarkIn and MarkOut in the scene timeline. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py index e2ef81e4a4..5f569d3ba7 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_marks.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_marks.py @@ -2,6 +2,7 @@ import json import pyblish.api from avalon.tvpaint import lib +from openpype.pipeline import PublishXmlValidationError class ValidateMarksRepair(pyblish.api.Action): @@ -73,9 +74,34 @@ class ValidateMarks(pyblish.api.ContextPlugin): "expected": expected_data[k] } - if invalid: - raise AssertionError( - "Marks does not match database:\n{}".format( - json.dumps(invalid, sort_keys=True, indent=4) - ) - ) + # Validation ends + if not invalid: + return + + current_frame_range = ( + (current_data["markOut"] - current_data["markIn"]) + 1 + ) + expected_frame_range = ( + (expected_data["markOut"] - expected_data["markIn"]) + 1 + ) + mark_in_enable_state = "disabled" + if current_data["markInState"]: + mark_in_enable_state = "enabled" + + mark_out_enable_state = "disabled" + if current_data["markOutState"]: + mark_out_enable_state = "enabled" + + raise PublishXmlValidationError( + self, + "Marks does not match database:\n{}".format( + json.dumps(invalid, sort_keys=True, indent=4) + ), + formatting_data={ + "current_frame_range": str(current_frame_range), + "expected_frame_range": str(expected_frame_range), + "mark_in_enable_state": mark_in_enable_state, + "mark_out_enable_state": mark_out_enable_state, + "expected_mark_out": expected_data["markOut"] + } + ) From d17de8492da6c9c4aba019dab129ecb8f4a88088 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 12:59:09 +0100 Subject: [PATCH 012/302] raise PublishXmlValidationError in validate missing layer names --- .../help/validate_missing_layer_names.xml | 18 ++++++++++++++++++ .../publish/validate_missing_layer_names.py | 17 +++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml new file mode 100644 index 0000000000..e96e7c5044 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_missing_layer_names.xml @@ -0,0 +1,18 @@ + + + +Missing layers +## Missing layers for render pass + +Render pass subset "{instance_name}" has stored layer names that belong to it's rendering scope but layers were not found in scene. + +### Missing layer names + +{layer_names} + +### How to repair? + +Find layers that belong to subset {instance_name} and rename them back to expected layer names or remove the subset and create new with right layers. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py b/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py index db9d354fcd..294ce6cf4f 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_missing_layer_names.py @@ -1,4 +1,5 @@ import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateMissingLayers(pyblish.api.InstancePlugin): @@ -30,13 +31,25 @@ class ValidateMissingLayers(pyblish.api.InstancePlugin): "\"{}\"".format(layer_name) for layer_name in missing_layer_names ]) + instance_label = ( + instance.data.get("label") or instance.data["name"] + ) + description_layer_names = "
".join([ + "- {}".format(layer_name) + for layer_name in missing_layer_names + ]) # Raise an error - raise AssertionError( + raise PublishXmlValidationError( + self, ( "Layers were not found by name for instance \"{}\"." # Description what's wrong " Layer names marked for publishing are not available" " in layers list. Missing layer names: {}" - ).format(instance.data["label"], layers_msg) + ).format(instance.data["label"], layers_msg), + formatting_data={ + "instance_name": instance_label, + "layer_names": description_layer_names + } ) From db5d03c9023235352b3935853b555cda70e9885e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:02:02 +0100 Subject: [PATCH 013/302] renamed validate_project_settings to validate_scene_settings --- ...te_project_settings.py => validate_scene_settings.py} | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) rename openpype/hosts/tvpaint/plugins/publish/{validate_project_settings.py => validate_scene_settings.py} (78%) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py similarity index 78% rename from openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py rename to openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py index 84c03a9857..7efa146c54 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -3,11 +3,10 @@ import json import pyblish.api -class ValidateProjectSettings(pyblish.api.ContextPlugin): - """Validate project settings against database. - """ +class ValidateSceneSettings(pyblish.api.ContextPlugin): + """Validate scene settings against database.""" - label = "Validate Project Settings" + label = "Validate Scene Settings" order = pyblish.api.ValidatorOrder optional = True @@ -28,7 +27,7 @@ class ValidateProjectSettings(pyblish.api.ContextPlugin): if invalid: raise AssertionError( - "Project settings does not match database:\n{}".format( + "Scene settings does not match database:\n{}".format( json.dumps(invalid, sort_keys=True, indent=4) ) ) From 6b6961a84a6e8b4a74eca4b4119d8ea4a7c41c8e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:02:32 +0100 Subject: [PATCH 014/302] raise PublishXmlValidationError in validate scene settings --- .../publish/help/validate_scene_settings.xml | 26 +++++++++++++++ .../publish/validate_scene_settings.py | 32 ++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..f741c71456 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,26 @@ + + + +Scene settings +## Invalid scene settings + +Scene settings do not match to expected values. + +**FPS** +- Expected value: {expected_fps} +- Current value: {current_fps} + +**Resolution** +- Expected value: {expected_width}x{expected_height} +- Current value: {current_width}x{current_height} + +**Pixel ratio** +- Expected value: {expected_pixel_ratio} +- Current value: {current_pixel_ratio} + +### How to repair? + +FPS and Pixel ratio can be modified in scene setting. Wrong resolution can be fixed with changing resolution of scene but due to TVPaint limitations it is possible that you will need to create new scene. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py index 7efa146c54..d235215ac9 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_scene_settings.py @@ -1,9 +1,11 @@ import json import pyblish.api +from openpype.pipeline import PublishXmlValidationError -class ValidateSceneSettings(pyblish.api.ContextPlugin): +# TODO @iLliCiTiT add fix action for fps +class ValidateProjectSettings(pyblish.api.ContextPlugin): """Validate scene settings against database.""" label = "Validate Scene Settings" @@ -11,6 +13,7 @@ class ValidateSceneSettings(pyblish.api.ContextPlugin): optional = True def process(self, context): + expected_data = context.data["assetEntity"]["data"] scene_data = { "fps": context.data.get("sceneFps"), "resolutionWidth": context.data.get("sceneWidth"), @@ -19,15 +22,28 @@ class ValidateSceneSettings(pyblish.api.ContextPlugin): } invalid = {} for k in scene_data.keys(): - expected_value = context.data["assetEntity"]["data"][k] + expected_value = expected_data[k] if scene_data[k] != expected_value: invalid[k] = { "current": scene_data[k], "expected": expected_value } - if invalid: - raise AssertionError( - "Scene settings does not match database:\n{}".format( - json.dumps(invalid, sort_keys=True, indent=4) - ) - ) + if not invalid: + return + + raise PublishXmlValidationError( + self, + "Scene settings does not match database:\n{}".format( + json.dumps(invalid, sort_keys=True, indent=4) + ), + formatting_data={ + "expected_fps": expected_data["fps"], + "current_fps": scene_data["fps"], + "expected_width": expected_data["resolutionWidth"], + "expected_height": expected_data["resolutionHeight"], + "current_width": scene_data["resolutionWidth"], + "current_height": scene_data["resolutionWidth"], + "expected_pixel_ratio": expected_data["pixelAspect"], + "current_pixel_ratio": scene_data["pixelAspect"] + } + ) From f99036eb1dbb74aa5a435d8f1f962303e170aecc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:27:56 +0100 Subject: [PATCH 015/302] raise PublishXmlValidationError in validate pass groups --- .../help/validate_render_pass_group.xml | 14 +++++++ .../publish/validate_render_pass_group.py | 40 +++++++++++++------ 2 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml new file mode 100644 index 0000000000..df7bdf36e5 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_render_pass_group.xml @@ -0,0 +1,14 @@ + + + +Render pass group +## Invalid group of Render Pass layers + +Layers of Render Pass {instance_name} belong to Render Group which is defined by TVPaint color group {expected_group}. But the layers are not in the group. + +### How to repair? + +Change the color group to {expected_group} on layers {layer_names}. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py index 5047b8d729..0fbfca6c56 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_render_pass_group.py @@ -1,5 +1,6 @@ import collections import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateLayersGroup(pyblish.api.InstancePlugin): @@ -26,11 +27,13 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): layer_names = instance.data["layer_names"] # Check if all layers from render pass are in right group invalid_layers_by_group_id = collections.defaultdict(list) + invalid_layer_names = set() for layer_name in layer_names: layer = layers_by_name.get(layer_name) _group_id = layer["group_id"] if _group_id != group_id: invalid_layers_by_group_id[_group_id].append(layer) + invalid_layer_names.add(layer_name) # Everything is OK and skip exception if not invalid_layers_by_group_id: @@ -61,16 +64,27 @@ class ValidateLayersGroup(pyblish.api.InstancePlugin): ) # Raise an error - raise AssertionError(( - # Short message - "Layers in wrong group." - # Description what's wrong - " Layers from render pass \"{}\" must be in group {} (id: {})." - # Detailed message - " Layers in wrong group: {}" - ).format( - instance.data["label"], - correct_group["name"], - correct_group["group_id"], - " | ".join(per_group_msgs) - )) + raise PublishXmlValidationError( + self, + ( + # Short message + "Layers in wrong group." + # Description what's wrong + " Layers from render pass \"{}\" must be in group {} (id: {})." + # Detailed message + " Layers in wrong group: {}" + ).format( + instance.data["label"], + correct_group["name"], + correct_group["group_id"], + " | ".join(per_group_msgs) + ), + formatting_data={ + "instance_name": ( + instance.data.get("label") or instance.data["name"] + ), + "expected_group": correct_group["name"], + "layer_names": ", ".join(invalid_layer_names) + + } + ) From df9e30eb7f8074d77a1701cc6bf2470ad82df487 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:33:11 +0100 Subject: [PATCH 016/302] raise PublishXmlValidationError in validate start frame --- .../plugins/publish/help/validate_start_frame.xml | 14 ++++++++++++++ .../plugins/publish/validate_start_frame.py | 12 +++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml new file mode 100644 index 0000000000..9052abf66c --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_start_frame.xml @@ -0,0 +1,14 @@ + + + +First frame +## MarkIn is not set to 0 + +MarkIn in your scene must start from 0 fram index but MarkIn is set to {current_start_frame}. + +### How to repair? + +You can modify MarkIn manually or hit the "Repair" button on the right which will change MarkIn to 0 (does not change MarkOut). + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py index d769d47736..48efd91055 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_start_frame.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import lib +from openpype.pipeline import PublishXmlValidationError class RepairStartFrame(pyblish.api.Action): @@ -24,4 +25,13 @@ class ValidateStartFrame(pyblish.api.ContextPlugin): def process(self, context): start_frame = lib.execute_george("tv_startframe") - assert int(start_frame) == 0, "Start frame has to be frame 0." + if start_frame == 0: + return + + raise PublishXmlValidationError( + self, + "Start frame has to be frame 0.", + formatting_data={ + "current_start_frame": start_frame + } + ) From 4c11ae83ee2843d1dc4bd4ac341d54137f3c3161 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 13:43:13 +0100 Subject: [PATCH 017/302] raise PublishXmlValidationError in validate workfile metadata --- .../help/validate_workfile_metadata.xml | 19 +++++++++++++++++++ .../publish/validate_workfile_metadata.py | 9 +++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml new file mode 100644 index 0000000000..7397f6ef0b --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_metadata.xml @@ -0,0 +1,19 @@ + + + +Missing metadata +## Your scene miss context metadata + +Your scene does not contain metadata about {missing_metadata}. + +### How to repair? + +Resave the scene using Workfiles tool or hit the "Repair" button on the right. + + +### How this could happend? + +You're using scene file that was not created using Workfiles tool. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py index 757da3294a..553d9af4e8 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_metadata.py @@ -1,5 +1,6 @@ import pyblish.api from avalon.tvpaint import save_file +from openpype.pipeline import PublishXmlValidationError class ValidateWorkfileMetadataRepair(pyblish.api.Action): @@ -42,8 +43,12 @@ class ValidateWorkfileMetadata(pyblish.api.ContextPlugin): missing_keys.append(key) if missing_keys: - raise AssertionError( + raise PublishXmlValidationError( + self, "Current workfile is missing metadata about {}.".format( ", ".join(missing_keys) - ) + ), + formatting_data={ + "missing_metadata": ", ".join(missing_keys) + } ) From d7f6db8d38d6df7010f886802367863b045dd130 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 22 Dec 2021 13:56:18 +0100 Subject: [PATCH 018/302] Added new style validators for New Publisher for Harmony --- .../plugins/publish/help/validate_audio.xml | 15 ++++++++ .../publish/help/validate_instances.xml | 25 +++++++++++++ .../publish/help/validate_scene_settings.xml | 35 +++++++++++++++++++ .../harmony/plugins/publish/validate_audio.py | 9 ++++- .../plugins/publish/validate_instances.py | 14 ++++++-- .../publish/validate_scene_settings.py | 32 ++++++++++++++--- 6 files changed, 123 insertions(+), 7 deletions(-) create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_audio.xml create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_instances.xml create mode 100644 openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml b/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml new file mode 100644 index 0000000000..e9a183c675 --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_audio.xml @@ -0,0 +1,15 @@ + + + +Missing audio file + +## Cannot locate linked audio file + +Audio file at {audio_url} cannot be found. + +### How to repair? + +Copy audio file to the highlighted location or remove audio link in the workfile. + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml b/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml new file mode 100644 index 0000000000..3b040e8ea8 --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_instances.xml @@ -0,0 +1,25 @@ + + + +Subset context + +## Invalid subset context + +Asset name found '{found}' in subsets, expected '{expected}'. + +### How to repair? + +You can fix this with `Repair` button on the right. This will use '{expected}' asset name and overwrite '{found}' asset name in scene metadata. + +After that restart `Publish` with a `Reload button`. + +If this is unwanted, close workfile and open again, that way different asset value would be used for context information. + + +### __Detailed Info__ (optional) + +This might happen if you are reuse old workfile and open it in different context. +(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.) + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml b/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml new file mode 100644 index 0000000000..36fa90456e --- /dev/null +++ b/openpype/hosts/harmony/plugins/publish/help/validate_scene_settings.xml @@ -0,0 +1,35 @@ + + + +Scene setting + +## Invalid scene setting found + +One of the settings in a scene doesn't match to asset settings in database. + +{invalid_setting_str} + +### How to repair? + +Change values for {invalid_keys_str} in the scene OR change them in the asset database if they are wrong there. + + +### __Detailed Info__ (optional) + +This error is shown when for example resolution in the scene doesn't match to resolution set on the asset in the database. +Either value in the database or in the scene is wrong. + + + +Scene file doesn't exist + +## Scene file doesn't exist + +Collected scene {scene_url} doesn't exist. + +### How to repair? + +Re-save file, start publish from the beginning again. + + + \ No newline at end of file diff --git a/openpype/hosts/harmony/plugins/publish/validate_audio.py b/openpype/hosts/harmony/plugins/publish/validate_audio.py index c043b31ca6..9322968a9d 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_audio.py +++ b/openpype/hosts/harmony/plugins/publish/validate_audio.py @@ -4,6 +4,8 @@ import pyblish.api from avalon import harmony +from openpype.pipeline import PublishXmlValidationError + class ValidateAudio(pyblish.api.InstancePlugin): """Ensures that there is an audio file in the scene. @@ -42,4 +44,9 @@ class ValidateAudio(pyblish.api.InstancePlugin): msg = "You are missing audio file:\n{}".format(audio_path) - assert os.path.isfile(audio_path), msg + formatting_data = { + "audio_url": audio_path + } + if os.path.isfile(audio_path): + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/harmony/plugins/publish/validate_instances.py b/openpype/hosts/harmony/plugins/publish/validate_instances.py index 78073a1978..9fb46dec49 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_instances.py +++ b/openpype/hosts/harmony/plugins/publish/validate_instances.py @@ -1,8 +1,10 @@ import os +from avalon import harmony import pyblish.api import openpype.api -from avalon import harmony + +from openpype.pipeline import PublishXmlValidationError class ValidateInstanceRepair(pyblish.api.Action): @@ -45,4 +47,12 @@ class ValidateInstance(pyblish.api.InstancePlugin): "Instance asset is not the same as current asset:" f"\nInstance: {instance_asset}\nCurrent: {current_asset}" ) - assert instance_asset == current_asset, msg + + formatting_data = { + "found": instance_asset, + "expected": current_asset + } + if instance_asset != current_asset: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + diff --git a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py index 0371e80095..e10adb885c 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py @@ -7,7 +7,9 @@ import re import pyblish.api from avalon import harmony + import openpype.hosts.harmony +from openpype.pipeline import PublishXmlValidationError class ValidateSceneSettingsRepair(pyblish.api.Action): @@ -102,6 +104,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): self.log.debug("current scene settings {}".format(current_settings)) invalid_settings = [] + invalid_keys = set() for key, value in expected_settings.items(): if value != current_settings[key]: invalid_settings.append({ @@ -109,6 +112,7 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): "expected": value, "current": current_settings[key] }) + invalid_keys.add(key) if ((expected_settings["handleStart"] or expected_settings["handleEnd"]) @@ -120,10 +124,30 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): msg = "Found invalid settings:\n{}".format( json.dumps(invalid_settings, sort_keys=True, indent=4) ) - assert not invalid_settings, msg - assert os.path.exists(instance.context.data.get("scenePath")), ( - "Scene file not found (saved under wrong name)" - ) + + if invalid_settings: + invalid_keys_str = ",".join(invalid_keys) + break_str = "
" + invalid_setting_str = "Found invalid settings:
{}".\ + format(break_str.join(invalid_settings)) + + formatting_data = { + "invalid_setting_str": invalid_setting_str, + "invalid_keys_str": invalid_keys_str + } + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + scene_url = instance.context.data.get("scenePath") + if not os.path.exists(scene_url): + msg = "Scene file {} not found (saved under wrong name)".format( + scene_url + ) + formatting_data = { + "scene_url": scene_url + } + raise PublishXmlValidationError(self, msg, key="file_not_found", + formatting_data=formatting_data) def _update_frames(expected_settings): From b9763e0e21342e11909563447047af201e1d70cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 14:13:03 +0100 Subject: [PATCH 019/302] raise PublishXmlValidationError in validate workfile project name --- .../help/validate_workfile_project_name.xml | 24 ++++++++++++++ .../publish/validate_workfile_project_name.py | 33 ++++++++++++------- 2 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml new file mode 100644 index 0000000000..c4ffafc8b5 --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_workfile_project_name.xml @@ -0,0 +1,24 @@ + + + +Project name +## Your scene is from different project + +It is not possible to publish into project "{workfile_project_name}" when TVPaint was opened with project "{env_project_name}" in context. + +### How to repair? + +If the workfile belongs to project "{env_project_name}" then use Workfiles tool to resave it. + +Otherwise close TVPaint and launch it again from project you want to publish in. + + +### How this could happend? + +You've opened workfile from different project. You've opened TVPaint on a task from "{env_project_name}" then you've opened TVPaint again on task from "{workfile_project_name}" without closing the TVPaint. Because TVPaint can run only once the project didn't change. + +### Why it is important? +Because project may affect how TVPaint works or change publishing behavior it is dangerous to allow change project context in many ways. For example publishing will not run as expected. + + + diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index cc664d8030..36230ae38b 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -1,5 +1,6 @@ import os import pyblish.api +from openpype.pipeline import PublishXmlValidationError class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): @@ -31,15 +32,23 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): return # Raise an error - raise AssertionError(( - # Short message - "Workfile from different Project ({})." - # Description what's wrong - " It is not possible to publish when TVPaint was launched in" - "context of different project. Current context project is \"{}\"." - " Launch TVPaint in context of project \"{}\" and then publish." - ).format( - workfile_project_name, - env_project_name, - workfile_project_name, - )) + raise AssertionError( + self, + ( + # Short message + "Workfile from different Project ({})." + # Description what's wrong + " It is not possible to publish when TVPaint was launched in" + "context of different project. Current context project is" + " \"{}\". Launch TVPaint in context of project \"{}\"" + " and then publish." + ).format( + workfile_project_name, + env_project_name, + workfile_project_name, + ), + formatting_data={ + "workfile_project_name": workfile_project_name, + "expected_project_name": env_project_name + } + ) From 9221d47b060695debe4bd39e226bcfdbe1dbe41f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 15:53:47 +0100 Subject: [PATCH 020/302] fix used exception --- .../tvpaint/plugins/publish/validate_workfile_project_name.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py index 36230ae38b..0f25f2f7be 100644 --- a/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py +++ b/openpype/hosts/tvpaint/plugins/publish/validate_workfile_project_name.py @@ -32,7 +32,7 @@ class ValidateWorkfileProjectName(pyblish.api.ContextPlugin): return # Raise an error - raise AssertionError( + raise PublishXmlValidationError( self, ( # Short message From 99feae84f28926a175eea31e1006705704314835 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:00:03 +0100 Subject: [PATCH 021/302] make sure all previous widget stay hidden --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index ba4df2eb8e..90bb4b062b 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -272,6 +272,7 @@ class ValidateActionsWidget(QtWidgets.QFrame): item = self._content_layout.takeAt(0) widget = item.widget() if widget: + widget.setVisible(False) widget.deleteLater() self._actions_mapping = {} From f6a7d3f51ab8b8f275615c4ac277c6813891a898 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:00:15 +0100 Subject: [PATCH 022/302] action icon is optional --- openpype/tools/publisher/widgets/validations_widget.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 90bb4b062b..003d78fa56 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -226,13 +226,15 @@ class ActionButton(ClickableFrame): action_label = action.label or action.__name__ action_icon = getattr(action, "icon", None) label_widget = QtWidgets.QLabel(action_label, self) + icon_label = None if action_icon: icon_label = IconValuePixmapLabel(action_icon, self) layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(5, 0, 5, 0) layout.addWidget(label_widget, 1) - layout.addWidget(icon_label, 0) + if icon_label is not None: + layout.addWidget(icon_label, 0) self.setSizePolicy( QtWidgets.QSizePolicy.Minimum, From 2813317a17587a0d290547ec68d2af98132de5ba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 20 Dec 2021 19:25:20 +0100 Subject: [PATCH 023/302] remove check of attr_plugins --- openpype/pipeline/create/context.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 7b0f50b1dc..2d748dd74f 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -306,8 +306,6 @@ class PublishAttributes: self._plugin_names_order = [] self._missing_plugins = [] self.attr_plugins = attr_plugins or [] - if not attr_plugins: - return origin_data = self._origin_data data = self._data From 43b5cc802d73c0af908216b674a8c214972c944a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:12:21 +0100 Subject: [PATCH 024/302] Change widget to Frame to make sure it has background with PySide --- openpype/tools/publisher/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/window.py b/openpype/tools/publisher/window.py index bb58813e55..b83c491c95 100644 --- a/openpype/tools/publisher/window.py +++ b/openpype/tools/publisher/window.py @@ -79,7 +79,7 @@ class PublisherWindow(QtWidgets.QDialog): # Content # Subset widget - subset_frame = QtWidgets.QWidget(self) + subset_frame = QtWidgets.QFrame(self) subset_views_widget = BorderedLabelWidget( "Subsets to publish", subset_frame From 3e683eadd0f5f133d99499bb0ce4a92935ad51eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 16:15:31 +0100 Subject: [PATCH 025/302] changed validation widget to frame to make sure it has background --- .../tools/publisher/widgets/validations_widget.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 003d78fa56..c9e5283d5f 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -414,8 +414,8 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidget(errors_widget) - error_details_widget = QtWidgets.QWidget(self) - error_details_input = QtWidgets.QTextEdit(error_details_widget) + error_details_frame = QtWidgets.QFrame(self) + error_details_input = QtWidgets.QTextEdit(error_details_frame) error_details_input.setObjectName("InfoText") error_details_input.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction @@ -424,7 +424,7 @@ class ValidationsWidget(QtWidgets.QWidget): actions_widget = ValidateActionsWidget(controller, self) actions_widget.setFixedWidth(140) - error_details_layout = QtWidgets.QHBoxLayout(error_details_widget) + error_details_layout = QtWidgets.QHBoxLayout(error_details_frame) error_details_layout.addWidget(error_details_input, 1) error_details_layout.addWidget(actions_widget, 0) @@ -433,7 +433,7 @@ class ValidationsWidget(QtWidgets.QWidget): content_layout.setContentsMargins(0, 0, 0, 0) content_layout.addWidget(errors_scroll, 0) - content_layout.addWidget(error_details_widget, 1) + content_layout.addWidget(error_details_frame, 1) top_label = QtWidgets.QLabel("Publish validation report", self) top_label.setObjectName("PublishInfoMainLabel") @@ -447,7 +447,7 @@ class ValidationsWidget(QtWidgets.QWidget): self._top_label = top_label self._errors_widget = errors_widget self._errors_layout = errors_layout - self._error_details_widget = error_details_widget + self._error_details_frame = error_details_frame self._error_details_input = error_details_input self._actions_widget = actions_widget @@ -467,7 +467,7 @@ class ValidationsWidget(QtWidgets.QWidget): widget.deleteLater() self._top_label.setVisible(False) - self._error_details_widget.setVisible(False) + self._error_details_frame.setVisible(False) self._errors_widget.setVisible(False) self._actions_widget.setVisible(False) @@ -478,7 +478,7 @@ class ValidationsWidget(QtWidgets.QWidget): return self._top_label.setVisible(True) - self._error_details_widget.setVisible(True) + self._error_details_frame.setVisible(True) self._errors_widget.setVisible(True) errors_by_title = [] From 8dff901f8d810b9f8363dbca7053e94788d741fd Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 22 Dec 2021 16:41:52 +0000 Subject: [PATCH 026/302] more xml validator messages --- ..._settings.xml => validate_frame_token.xml} | 12 +-- .../publish/help/validate_vdb_input_node.xml | 22 ------ .../publish/help/validate_vdb_output_node.xml | 48 ++++++++++++ .../publish/validate_animation_settings.py | 51 ------------ .../plugins/publish/validate_frame_token.py | 17 ++-- .../plugins/publish/validate_output_node.py | 77 ------------------- .../publish/validate_sop_output_node.py | 2 +- .../publish/validate_vdb_input_node.py | 47 ----------- .../publish/validate_vdb_output_node.py | 66 +++++++++++----- 9 files changed, 113 insertions(+), 229 deletions(-) rename openpype/hosts/houdini/plugins/publish/help/{validate_animation_settings.xml => validate_frame_token.xml} (74%) delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml create mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_animation_settings.py delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_output_node.py delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml b/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml similarity index 74% rename from openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml rename to openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml index 8a2a396783..925113362a 100644 --- a/openpype/hosts/houdini/plugins/publish/help/validate_animation_settings.xml +++ b/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml @@ -1,22 +1,22 @@ -Frame token in output -## Frame range is missing frame token +Output frame token +## Output path is missing frame token This validator will check the output parameter of the node if the Valid Frame Range is not set to 'Render Current Frame' -No frame token found in {nodepath} +No frame token found in: **{nodepath}** ### How to repair? -Your you need to add `$F4` or similar frame based token to your path. + +You need to add `$F4` or similar frame based token to your path. + **Example:** Good: 'my_vbd_cache.$F4.vdb' Bad: 'my_vbd_cache.vdb' - - diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml deleted file mode 100644 index 8cc186a183..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_input_node.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - -VDB input node -## Invalid VDB input node - -Validate that the node connected to the output node is of type VDB. - -Regardless of the amount of VDBs created the output will need to have an -equal amount of VDBs, points, primitives and vertices - -A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml new file mode 100644 index 0000000000..822d1836c1 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml @@ -0,0 +1,48 @@ + + + +VDB output node +## Invalid VDB output nodes + +Validate that the node connected to the output node is of type VDB. + +Regardless of the amount of VDBs created the output will need to have an +equal amount of VDBs, points, primitives and vertices + +A VDB is an inherited type of Prim, holds the following data: + +- Primitives: 1 +- Points: 1 +- Vertices: 1 +- VDBs: 1 + + + + + + + +No SOP path +## No SOP Path in output node + +SOP Output node in '{node}' does not exist. Ensure a valid SOP output path is set. + + + + + + + +Wrong SOP path +## Wrong SOP Path in output node + +Output node {nodepath} is not a SOP node. +SOP Path must point to a SOP node, +instead found category type: {categoryname} + + + + + + + \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py deleted file mode 100644 index 5eb8f93d03..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py +++ /dev/null @@ -1,51 +0,0 @@ -import pyblish.api - -from openpype.hosts.houdini.api import lib - - -class ValidateAnimationSettings(pyblish.api.InstancePlugin): - """Validate if the unexpanded string contains the frame ('$F') token - - This validator will only check the output parameter of the node if - the Valid Frame Range is not set to 'Render Current Frame' - - Rules: - If you render out a frame range it is mandatory to have the - frame token - '$F4' or similar - to ensure that each frame gets - written. If this is not the case you will override the same file - every time a frame is written out. - - Examples: - Good: 'my_vbd_cache.$F4.vdb' - Bad: 'my_vbd_cache.vdb' - - """ - - order = pyblish.api.ValidatorOrder - label = "Validate Frame Settings" - families = ["vdbcache"] - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Output settings do no match for '%s'" % instance - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance[0] - - # Check trange parm, 0 means Render Current Frame - frame_range = node.evalParm("trange") - if frame_range == 0: - return [] - - output_parm = lib.get_output_parameter(node) - unexpanded_str = output_parm.unexpandedString() - - if "$F" not in unexpanded_str: - cls.log.error("No frame token found in '%s'" % node.path()) - return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py index 76b5910576..f66238f159 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py +++ b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py @@ -1,12 +1,12 @@ import pyblish.api from openpype.hosts.houdini.api import lib - +from openpype.pipeline import PublishXmlValidationError class ValidateFrameToken(pyblish.api.InstancePlugin): - """Validate if the unexpanded string contains the frame ('$F') token. + """Validate if the unexpanded string contains the frame ('$F') token - This validator will *only* check the output parameter of the node if + This validator will only check the output parameter of the node if the Valid Frame Range is not set to 'Render Current Frame' Rules: @@ -28,9 +28,14 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) + data = { + "nodepath": instance + } if invalid: - raise RuntimeError( - "Output settings do no match for '%s'" % instance + raise PublishXmlValidationError( + self, + "Output path for '%s' is missing $F4 token" % instance, + formatting_data=data ) @classmethod @@ -47,5 +52,5 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): unexpanded_str = output_parm.unexpandedString() if "$F" not in unexpanded_str: - cls.log.error("No frame token found in '%s'" % node.path()) + # cls.log.info("No frame token found in '%s'" % node.path()) return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_output_node.py deleted file mode 100644 index 0b60ab5c48..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_output_node.py +++ /dev/null @@ -1,77 +0,0 @@ -import pyblish.api - - -class ValidateOutputNode(pyblish.api.InstancePlugin): - """Validate the instance SOP Output Node. - - This will ensure: - - The SOP Path is set. - - The SOP Path refers to an existing object. - - The SOP Path node is a SOP node. - - The SOP Path node has at least one input connection (has an input) - - The SOP Path has geometry data. - - """ - - order = pyblish.api.ValidatorOrder - families = ["pointcache", "vdbcache"] - hosts = ["houdini"] - label = "Validate Output Node" - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Output node(s) `%s` are incorrect. " - "See plug-in log for details." % invalid - ) - - @classmethod - def get_invalid(cls, instance): - - import hou - - output_node = instance.data["output_node"] - - if output_node is None: - node = instance[0] - cls.log.error( - "SOP Output node in '%s' does not exist. " - "Ensure a valid SOP output path is set." % node.path() - ) - - return [node.path()] - - # Output node must be a Sop node. - if not isinstance(output_node, hou.SopNode): - cls.log.error( - "Output node %s is not a SOP node. " - "SOP Path must point to a SOP node, " - "instead found category type: %s" - % (output_node.path(), output_node.type().category().name()) - ) - return [output_node.path()] - - # For the sake of completeness also assert the category type - # is Sop to avoid potential edge case scenarios even though - # the isinstance check above should be stricter than this category - assert output_node.type().category().name() == "Sop", ( - "Output node %s is not of category Sop. This is a bug.." - % output_node.path() - ) - - # Check if output node has incoming connections - if not output_node.inputConnections(): - cls.log.error( - "Output node `%s` has no incoming connections" - % output_node.path() - ) - return [output_node.path()] - - # Ensure the output node has at least Geometry data - if not output_node.geometry(): - cls.log.error( - "Output node `%s` has no geometry data." % output_node.path() - ) - return [output_node.path()] diff --git a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py index a5a07b1b1a..a37d376919 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py @@ -14,7 +14,7 @@ class ValidateSopOutputNode(pyblish.api.InstancePlugin): """ order = pyblish.api.ValidatorOrder - families = ["pointcache", "vdbcache"] + families = ["pointcache"] hosts = ["houdini"] label = "Validate Output Node" diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py deleted file mode 100644 index 0ae1bc94eb..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py +++ /dev/null @@ -1,47 +0,0 @@ -import pyblish.api -import openpype.api - - -class ValidateVDBInputNode(pyblish.api.InstancePlugin): - """Validate that the node connected to the output node is of type VDB. - - Regardless of the amount of VDBs create the output will need to have an - equal amount of VDBs, points, primitives and vertices - - A VDB is an inherited type of Prim, holds the following data: - - Primitives: 1 - - Points: 1 - - Vertices: 1 - - VDBs: 1 - - """ - - order = openpype.api.ValidateContentsOrder + 0.1 - families = ["vdbcache"] - hosts = ["houdini"] - label = "Validate Input Node (VDB)" - - def process(self, instance): - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Node connected to the output node is not" "of type VDB!" - ) - - @classmethod - def get_invalid(cls, instance): - - node = instance.data["output_node"] - - prims = node.geometry().prims() - nr_of_prims = len(prims) - - nr_of_points = len(node.geometry().points()) - if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") - return [instance] - - for prim in prims: - if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") - return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index 1ba840b71d..f6e54f3ae2 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -1,8 +1,7 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError import hou - - class ValidateVDBOutputNode(pyblish.api.InstancePlugin): """Validate that the node connected to the output node is of type VDB. @@ -23,32 +22,61 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): label = "Validate Output Node (VDB)" def process(self, instance): + + data = { + "node": instance + } + + output_node = instance.data["output_node"] + if output_node is None: + raise PublishXmlValidationError( + self, + "SOP Output node in '{node}' does not exist. Ensure a valid " + "SOP output path is set.".format(**data), + key="noSOP", + formatting_data=data + ) + + # Output node must be a Sop node. + if not isinstance(output_node, hou.SopNode): + data = { + "nodepath": output_node.path(), + "categoryname": output_node.type().category().name() + } + raise PublishXmlValidationError( + self, + "Output node {nodepath} is not a SOP node. SOP Path must" + "point to a SOP node, instead found category" + "type: {categoryname}".format(**data), + key="wrongSOP", + formatting_data=data + ) + return [node.path()] + invalid = self.get_invalid(instance) + if invalid: - raise RuntimeError( - "Node connected to the output node is not" " of type VDB!" + raise PublishXmlValidationError( + self, + "Output node(s) `{}` are incorrect. See plug-in" + "log for details.".format(invalid), + formatting_data=data ) @classmethod def get_invalid(cls, instance): - node = instance.data["output_node"] - if node is None: - cls.log.error( - "SOP path is not correctly set on " - "ROP node '%s'." % instance[0].path() - ) - return [instance] + output_node = instance.data["output_node"] frame = instance.data.get("frameStart", 0) - geometry = node.geometryAtFrame(frame) + geometry = output_node.geometryAtFrame(frame) if geometry is None: - # No geometry data on this node, maybe the node hasn't cooked? - cls.log.error( + # No geometry data on this output_node, maybe the node hasn't cooked? + cls.log.debug( "SOP node has no geometry data. " - "Is it cooked? %s" % node.path() + "Is it cooked? %s" % output_node.path() ) - return [node] + return [output_node] prims = geometry.prims() nr_of_prims = len(prims) @@ -57,17 +85,17 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): invalid_prim = False for prim in prims: if not isinstance(prim, hou.VDB): - cls.log.error("Found non-VDB primitive: %s" % prim) + cls.log.debug("Found non-VDB primitive: %s" % prim) invalid_prim = True if invalid_prim: return [instance] nr_of_points = len(geometry.points()) if nr_of_points != nr_of_prims: - cls.log.error("The number of primitives and points do not match") + cls.log.debug("The number of primitives and points do not match") return [instance] for prim in prims: if prim.numVertices() != 1: - cls.log.error("Found primitive with more than 1 vertex!") + cls.log.debug("Found primitive with more than 1 vertex!") return [instance] From df717bfb8c038bec50f4a949c9f9c2b356ca7a41 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 22 Dec 2021 18:53:25 +0100 Subject: [PATCH 027/302] Update openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml Co-authored-by: Milan Kolar --- .../tvpaint/plugins/publish/help/validate_layers_visibility.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml index fc69d5fd7b..2eaed22a19 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -10,7 +10,7 @@ All layers for subset "{instance_name}" are hidden. {layer_names} -*Check layer names for all subsets in list on left side.* +*Check layer names for all subsets in the list on the left side.* ### How to repair? From f06bfd7c861c32ac41fd63bb86e9b5bf3c68f19b Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 19:34:02 +0100 Subject: [PATCH 028/302] changed fixed width to min width --- openpype/tools/publisher/widgets/validations_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index c9e5283d5f..fc78f93856 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -407,7 +407,7 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidgetResizable(True) errors_widget = QtWidgets.QWidget(errors_scroll) - errors_widget.setFixedWidth(200) + errors_widget.setMinimumWidth(200) errors_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) errors_layout = QtWidgets.QVBoxLayout(errors_widget) errors_layout.setContentsMargins(0, 0, 0, 0) @@ -422,7 +422,7 @@ class ValidationsWidget(QtWidgets.QWidget): ) actions_widget = ValidateActionsWidget(controller, self) - actions_widget.setFixedWidth(140) + actions_widget.setMinimumWidth(140) error_details_layout = QtWidgets.QHBoxLayout(error_details_frame) error_details_layout.addWidget(error_details_input, 1) From ca5f1dbcba0c7d93c0840cf4d9bf0ca941534f5d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:15:20 +0100 Subject: [PATCH 029/302] make sure items has label in tooltip --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index fc78f93856..028e6a2ea3 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -95,6 +95,7 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) + item.setData(label, QtCore.Qt.ToolTipRole) item.setData(instance.id, INSTANCE_ID_ROLE) items.append(item) description = self._prepare_description(exception) From 197a5054cd8463892ffa3bb3f2de7931d4f101ec Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:15:30 +0100 Subject: [PATCH 030/302] disable horizontal scroll --- openpype/tools/publisher/widgets/validations_widget.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 028e6a2ea3..a3f2c2069a 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -25,6 +25,7 @@ class ValidationErrorInstanceList(QtWidgets.QListView): self.setObjectName("ValidationErrorInstanceList") + self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) self.setSelectionMode(QtWidgets.QListView.ExtendedSelection) def minimumSizeHint(self): From 64bb579c816d746a67a68322fd2c3a4f642e468d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Dec 2021 21:39:44 +0100 Subject: [PATCH 031/302] resize title by longest instance name --- .../publisher/widgets/validations_widget.py | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index a3f2c2069a..c013b0b2e0 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -29,16 +29,16 @@ class ValidationErrorInstanceList(QtWidgets.QListView): self.setSelectionMode(QtWidgets.QListView.ExtendedSelection) def minimumSizeHint(self): - result = super(ValidationErrorInstanceList, self).minimumSizeHint() - result.setHeight(self.sizeHint().height()) - return result + return self.sizeHint() def sizeHint(self): + result = super(ValidationErrorInstanceList, self).sizeHint() row_count = self.model().rowCount() height = 0 if row_count > 0: height = self.sizeHintForRow(0) * row_count - return QtCore.QSize(self.width(), height) + result.setHeight(height) + return result class ValidationErrorTitleWidget(QtWidgets.QWidget): @@ -133,12 +133,30 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._toggle_instance_btn = toggle_instance_btn + self._view_layout = view_layout + self._instances_model = instances_model self._instances_view = instances_view self._context_validation = context_validation self._help_text_by_instance_id = help_text_by_instance_id + def sizeHint(self): + result = super().sizeHint() + expected_width = 0 + for idx in range(self._view_layout.count()): + expected_width += self._view_layout.itemAt(idx).sizeHint().width() + + if expected_width < 200: + expected_width = 200 + + if result.width() < expected_width: + result.setWidth(expected_width) + return result + + def minimumSizeHint(self): + return self.sizeHint() + def _prepare_description(self, exception): dsc = exception.description detail = exception.detail @@ -409,7 +427,6 @@ class ValidationsWidget(QtWidgets.QWidget): errors_scroll.setWidgetResizable(True) errors_widget = QtWidgets.QWidget(errors_scroll) - errors_widget.setMinimumWidth(200) errors_widget.setAttribute(QtCore.Qt.WA_TranslucentBackground) errors_layout = QtWidgets.QVBoxLayout(errors_widget) errors_layout.setContentsMargins(0, 0, 0, 0) @@ -518,6 +535,8 @@ class ValidationsWidget(QtWidgets.QWidget): if self._title_widgets: self._title_widgets[0].set_selected(True) + self.updateGeometry() + def _on_select(self, index): if self._previous_select: if self._previous_select.index == index: From 7c6d63f8930a6338b194ae339012c532914ca5d3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 23 Dec 2021 12:38:00 +0100 Subject: [PATCH 032/302] Added new style validators for New Publisher for Standalone Publisher --- .../publish/help/validate_frame_ranges.xml | 15 ++++++++ .../publish/help/validate_shot_duplicates.xml | 15 ++++++++ .../plugins/publish/help/validate_sources.xml | 16 +++++++++ .../publish/help/validate_task_existence.xml | 16 +++++++++ .../publish/help/validate_texture_batch.xml | 15 ++++++++ .../help/validate_texture_has_workfile.xml | 15 ++++++++ .../publish/help/validate_texture_name.xml | 32 +++++++++++++++++ .../help/validate_texture_versions.xml | 35 +++++++++++++++++++ .../help/validate_texture_workfiles.xml | 23 ++++++++++++ .../plugins/publish/validate_frame_ranges.py | 18 +++++++--- .../publish/validate_shot_duplicates.py | 9 +++-- .../plugins/publish/validate_sources.py | 18 +++++++--- .../publish/validate_task_existence.py | 9 ++++- .../plugins/publish/validate_texture_batch.py | 8 +++-- .../publish/validate_texture_has_workfile.py | 6 +++- .../plugins/publish/validate_texture_name.py | 21 ++++++++--- .../publish/validate_texture_versions.py | 15 ++++++-- .../publish/validate_texture_workfiles.py | 17 ++++++--- 18 files changed, 275 insertions(+), 28 deletions(-) create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml new file mode 100644 index 0000000000..933df1c7c5 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_frame_ranges.xml @@ -0,0 +1,15 @@ + + + +Invalid frame range + +## Invalid frame range + +Expected duration or '{duration}' frames set in database, workfile contains only '{found}' frames. + +### How to repair? + +Modify configuration in the database or tweak frame range in the workfile. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml new file mode 100644 index 0000000000..77b8727162 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_shot_duplicates.xml @@ -0,0 +1,15 @@ + + + +Duplicate shots + +## Duplicate shot names + +Process contains duplicated shot names '{duplicates_str}'. + +### How to repair? + +Remove shot duplicates. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml new file mode 100644 index 0000000000..d527d2173e --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_sources.xml @@ -0,0 +1,16 @@ + + + +Files not found + +## Source files not found + +Process contains duplicated shot names: +'{files_not_found}' + +### How to repair? + +Add missing files or run Publish again to collect new publishable files. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml new file mode 100644 index 0000000000..a943f560d0 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_task_existence.xml @@ -0,0 +1,16 @@ + + + +Task not found + +## Task not found in database + +Process contains tasks that don't exist in database: +'{task_not_found}' + +### How to repair? + +Remove set task or add task into database into proper place. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml new file mode 100644 index 0000000000..a645df8d02 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_batch.xml @@ -0,0 +1,15 @@ + + + +No texture files found + +## Batch doesn't contain texture files + +Batch must contain at least one texture file. + +### How to repair? + +Add texture file to the batch or check name if it follows naming convention to match texture files to the batch. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml new file mode 100644 index 0000000000..077987a96d --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_has_workfile.xml @@ -0,0 +1,15 @@ + + + +No workfile found + +## Batch should contain workfile + +It is expected that published contains workfile that served as a source for textures. + +### How to repair? + +Add workfile to the batch, or disable this validator if you do not want workfile published. + + + \ No newline at end of file diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml new file mode 100644 index 0000000000..2610917736 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_name.xml @@ -0,0 +1,32 @@ + + + +Asset name not found + +## Couldn't parse asset name from a file + +Unable to parse asset name from '{file_name}'. File name doesn't match configured naming convention. + +### How to repair? + +Check Settings: project_settings/standalonepublisher/publish/CollectTextures for naming convention. + + +### __Detailed Info__ (optional) + +This error happens when parsing cannot figure out name of asset texture files belong under. + + + +Missing keys + +## Texture file name is missing some required keys + +Texture '{file_name}' is missing values for {missing_str} keys. + +### How to repair? + +Fix name of texture file and Publish again. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml new file mode 100644 index 0000000000..1e536e604f --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_versions.xml @@ -0,0 +1,35 @@ + + + +Texture version + +## Texture version mismatch with workfile + +Workfile '{file_name}' version doesn't match with '{version}' of a texture. + +### How to repair? + +Rename either workfile or texture to contain matching versions + + +### __Detailed Info__ (optional) + +This might happen if you are trying to publish textures for older version of workfile (or the other way). +(Eg. publishing 'workfile_v001' and 'texture_file_v002') + + + +Too many versions + +## Too many versions published at same time + +It is currently expected to publish only batch with single version. + +Found {found} versions. + +### How to repair? + +Please remove files with different version and split publishing into multiple steps. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml new file mode 100644 index 0000000000..8187eb0bc8 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_texture_workfiles.xml @@ -0,0 +1,23 @@ + + + +No secondary workfile + +## No secondary workfile found + +Current process expects that primary workfile (for example with a extension '{extension}') will contain also 'secondary' workfile. + +Secondary workfile for '{file_name}' wasn't found. + +### How to repair? + +Attach secondary workfile or disable this validator and Publish again. + + +### __Detailed Info__ (optional) + +This process was implemented for a possible use case of first workfile coming from Mari, secondary workfile for textures from Substance. +Publish should contain both if primary workfile is present. + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py index 943cb73b98..c7a2e755b6 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py @@ -1,8 +1,10 @@ import re import pyblish.api + import openpype.api from openpype import lib +from openpype.pipeline import PublishXmlValidationError class ValidateFrameRange(pyblish.api.InstancePlugin): @@ -48,9 +50,15 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): files = [files] frames = len(files) - err_msg = "Frame duration from DB:'{}' ". format(int(duration)) +\ - " doesn't match number of files:'{}'".format(frames) +\ - " Please change frame range for Asset or limit no. of files" - assert frames == duration, err_msg + msg = "Frame duration from DB:'{}' ". format(int(duration)) +\ + " doesn't match number of files:'{}'".format(frames) +\ + " Please change frame range for Asset or limit no. of files" - self.log.debug("Valid ranges {} - {}".format(int(duration), frames)) + formatting_data = {"duration": duration, + "found": frames} + if frames == duration: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + self.log.debug("Valid ranges expected '{}' - found '{}'". + format(int(duration), frames)) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py index 85ec9379ce..0f957acad6 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py @@ -1,6 +1,7 @@ import pyblish.api -import openpype.api +import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateShotDuplicates(pyblish.api.ContextPlugin): """Validating no duplicate names are in context.""" @@ -20,4 +21,8 @@ class ValidateShotDuplicates(pyblish.api.ContextPlugin): shot_names.append(name) msg = "There are duplicate shot names:\n{}".format(duplicate_names) - assert not duplicate_names, msg + + formatting_data = {"duplicate_str": ','.join(duplicate_names)} + if duplicate_names: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py index eec675e97f..316f58988f 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_sources.py @@ -1,8 +1,10 @@ -import pyblish.api -import openpype.api - import os +import pyblish.api + +import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateSources(pyblish.api.InstancePlugin): """Validates source files. @@ -11,7 +13,6 @@ class ValidateSources(pyblish.api.InstancePlugin): got deleted between starting of SP and now. """ - order = openpype.api.ValidateContentsOrder label = "Check source files" @@ -22,6 +23,7 @@ class ValidateSources(pyblish.api.InstancePlugin): def process(self, instance): self.log.info("instance {}".format(instance.data)) + missing_files = set() for repre in instance.data.get("representations") or []: files = [] if isinstance(repre["files"], str): @@ -34,4 +36,10 @@ class ValidateSources(pyblish.api.InstancePlugin): file_name) if not os.path.exists(source_file): - raise ValueError("File {} not found".format(source_file)) + missing_files.add(source_file) + + msg = "Files '{}' not found".format(','.join(missing_files)) + formatting_data = {"files_not_found": ' - {}'.join(missing_files)} + if missing_files: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py index e3b2ae1646..825092c81b 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_task_existence.py @@ -1,6 +1,8 @@ import pyblish.api from avalon import io +from openpype.pipeline import PublishXmlValidationError + class ValidateTaskExistence(pyblish.api.ContextPlugin): """Validating tasks on instances are filled and existing.""" @@ -53,4 +55,9 @@ class ValidateTaskExistence(pyblish.api.ContextPlugin): "Asset: \"{}\" Task: \"{}\"".format(*missing_pair) ) - raise AssertionError(msg.format("\n".join(pair_msgs))) + msg = msg.format("\n".join(pair_msgs)) + + formatting_data = {"task_not_found": ' - {}'.join(pair_msgs)} + if pair_msgs: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py index d592a4a059..d66fb257bb 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_batch.py @@ -1,6 +1,8 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError + class ValidateTextureBatch(pyblish.api.InstancePlugin): """Validates that some texture files are present.""" @@ -15,8 +17,10 @@ class ValidateTextureBatch(pyblish.api.InstancePlugin): present = False for instance in instance.context: if instance.data["family"] == "textures": - self.log.info("Some textures present.") + self.log.info("At least some textures present.") return - assert present, "No textures found in published batch!" + msg = "No textures found in published batch!" + if not present: + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py index 7cd540668c..0e67464f59 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_has_workfile.py @@ -1,5 +1,7 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureHasWorkfile(pyblish.api.InstancePlugin): @@ -17,4 +19,6 @@ class ValidateTextureHasWorkfile(pyblish.api.InstancePlugin): def process(self, instance): wfile = instance.data["versionData"].get("workfile") - assert wfile, "Textures are missing attached workfile" + msg = "Textures are missing attached workfile" + if not wfile: + raise PublishXmlValidationError(self, msg) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py index f210be3631..751ad917ca 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_name.py @@ -1,6 +1,7 @@ import pyblish.api -import openpype.api +import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): """Validates that all instances had properly formatted name.""" @@ -16,12 +17,16 @@ class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): if isinstance(file_name, list): file_name = file_name[0] - msg = "Couldnt find asset name in '{}'\n".format(file_name) + \ + msg = "Couldn't find asset name in '{}'\n".format(file_name) + \ "File name doesn't follow configured pattern.\n" + \ "Please rename the file." - assert "NOT_AVAIL" not in instance.data["asset_build"], msg - instance.data.pop("asset_build") + formatting_data = {"file_name": file_name} + if "NOT_AVAIL" in instance.data["asset_build"]: + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data) + + instance.data.pop("asset_build") # not needed anymore if instance.data["family"] == "textures": file_name = instance.data["representations"][0]["files"][0] @@ -47,4 +52,10 @@ class ValidateTextureBatchNaming(pyblish.api.InstancePlugin): "Name of the texture file doesn't match expected pattern.\n" + \ "Please rename file(s) {}".format(file_name) - assert not missing_key_values, msg + missing_str = ','.join(["'{}'".format(key) + for key in missing_key_values]) + formatting_data = {"file_name": file_name, + "missing_str": missing_str} + if missing_key_values: + raise PublishXmlValidationError(self, msg, key="missing_values", + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py index 90d0e8e512..84d9def895 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_versions.py @@ -1,5 +1,7 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchVersions(pyblish.api.InstancePlugin): @@ -25,14 +27,21 @@ class ValidateTextureBatchVersions(pyblish.api.InstancePlugin): self.log.info("No workfile present for textures") return - msg = "Not matching version: texture v{:03d} - workfile {}" - assert version_str in wfile, \ + if version_str not in wfile: + msg = "Not matching version: texture v{:03d} - workfile {}" msg.format( instance.data["version"], wfile ) + raise PublishXmlValidationError(self, msg) present_versions = set() for instance in instance.context: present_versions.add(instance.data["version"]) - assert len(present_versions) == 1, "Too many versions in a batch!" + if len(present_versions) != 1: + msg = "Too many versions in a batch!" + found = ','.join(["'{}'".format(val) for val in present_versions]) + formatting_data = {"found": found} + + raise PublishXmlValidationError(self, msg, key="too_many", + formatting_data=formatting_data) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py index 25bb5aea4a..fa492a80d8 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_texture_workfiles.py @@ -1,11 +1,13 @@ import pyblish.api + import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateTextureBatchWorkfiles(pyblish.api.InstancePlugin): """Validates that textures workfile has collected resources (optional). - Collected recourses means secondary workfiles (in most cases). + Collected resources means secondary workfiles (in most cases). """ label = "Validate Texture Workfile Has Resources" @@ -24,6 +26,13 @@ class ValidateTextureBatchWorkfiles(pyblish.api.InstancePlugin): self.log.warning("Only secondary workfile present!") return - msg = "No secondary workfiles present for workfile {}".\ - format(instance.data["name"]) - assert instance.data.get("resources"), msg + if not instance.data.get("resources"): + msg = "No secondary workfile present for workfile '{}'". \ + format(instance.data["name"]) + ext = self.main_workfile_extensions[0] + formatting_data = {"file_name": instance.data["name"], + "extension": ext} + + raise PublishXmlValidationError(self, msg, + formatting_data=formatting_data + ) From 830086516a772fc967379ba553961f16eda6bc43 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 7 Jan 2022 11:04:01 +0100 Subject: [PATCH 033/302] modified text of layers visibility validator exception --- .../plugins/publish/help/validate_layers_visibility.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml index 2eaed22a19..e7be735888 100644 --- a/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml +++ b/openpype/hosts/tvpaint/plugins/publish/help/validate_layers_visibility.xml @@ -4,7 +4,7 @@ Layers visiblity ## All layers are not visible -All layers for subset "{instance_name}" are hidden. +Layers visibility was changed during publishing which caused that all layers for subset "{instance_name}" are hidden. ### Layer names for **{instance_name}** @@ -14,7 +14,7 @@ All layers for subset "{instance_name}" are hidden. ### How to repair? -Make sure that at least one layer in the scene is visible or disable the subset before hitting publish button after refresh. +Reset publishing and do not change visibility of layers after hitting publish button. From 6fd45d99f9bcee675a3590d36396ca9b5acaea02 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 12 Jan 2022 18:10:17 +0100 Subject: [PATCH 034/302] Fix - wrong expression --- .../plugins/publish/validate_frame_ranges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py index c7a2e755b6..005157af62 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_frame_ranges.py @@ -56,7 +56,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): formatting_data = {"duration": duration, "found": frames} - if frames == duration: + if frames != duration: raise PublishXmlValidationError(self, msg, formatting_data=formatting_data) From edd0fb1ce9e06f374994ca37c401fffa30a29f9e Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 13 Jan 2022 11:04:47 +0100 Subject: [PATCH 035/302] Fix - typo in key --- .../plugins/publish/validate_shot_duplicates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py index 0f957acad6..fe655f6b74 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_shot_duplicates.py @@ -22,7 +22,7 @@ class ValidateShotDuplicates(pyblish.api.ContextPlugin): msg = "There are duplicate shot names:\n{}".format(duplicate_names) - formatting_data = {"duplicate_str": ','.join(duplicate_names)} + formatting_data = {"duplicates_str": ','.join(duplicate_names)} if duplicate_names: raise PublishXmlValidationError(self, msg, formatting_data=formatting_data) From ef695cb153e6bfd7676f08f294b7b75ce999fadb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 13 Jan 2022 11:09:03 +0100 Subject: [PATCH 036/302] Added new style validation for check for editorial resources --- .../help/validate_editorial_resources.xml | 17 +++++++++++++++++ .../publish/validate_editorial_resources.py | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml diff --git a/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml new file mode 100644 index 0000000000..803de6bf11 --- /dev/null +++ b/openpype/hosts/standalonepublisher/plugins/publish/help/validate_editorial_resources.xml @@ -0,0 +1,17 @@ + + + +Missing source video file + +## No attached video file found + +Process expects presence of source video file with same name prefix as an editorial file in same folder. +(example `simple_editorial_setup_Layer1.edl` expects `simple_editorial_setup.mp4` in same folder) + + +### How to repair? + +Copy source video file to the folder next to `.edl` file. (On a disk, do not put it into Standalone Publisher.) + + + diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py index 6759b87ceb..7987bbc2d9 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py @@ -1,5 +1,6 @@ import pyblish.api import openpype.api +from openpype.pipeline import PublishXmlValidationError class ValidateEditorialResources(pyblish.api.InstancePlugin): @@ -19,5 +20,7 @@ class ValidateEditorialResources(pyblish.api.InstancePlugin): f"Instance: {instance}, Families: " f"{[instance.data['family']] + instance.data['families']}") check_file = instance.data["editorialSourcePath"] - msg = f"Missing \"{check_file}\"." - assert check_file, msg + msg = f"Missing source video file." + + if not check_file: + raise PublishXmlValidationError(self, msg) From 5a5a172c3051a789ce0adbe94ab1e1c428fecea7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 17 Jan 2022 15:31:21 +0100 Subject: [PATCH 037/302] Update openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/validate_editorial_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py index 7987bbc2d9..afb828474d 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/validate_editorial_resources.py @@ -20,7 +20,7 @@ class ValidateEditorialResources(pyblish.api.InstancePlugin): f"Instance: {instance}, Families: " f"{[instance.data['family']] + instance.data['families']}") check_file = instance.data["editorialSourcePath"] - msg = f"Missing source video file." + msg = "Missing source video file." if not check_file: raise PublishXmlValidationError(self, msg) From c5d737faad0ee755b9b967eb529e0185291e1077 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:47:57 +0100 Subject: [PATCH 038/302] Draft implementation of Update all to latest button --- openpype/tools/sceneinventory/window.py | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index e363a99d07..a05d820ec6 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -1,5 +1,6 @@ import os import sys +import logging from Qt import QtWidgets, QtCore from avalon.vendor import qtawesome @@ -20,6 +21,9 @@ from .model import ( ) from .view import SceneInvetoryView +from ..utils.lib import iter_model_rows + +log = logging.getLogger(__name__) module = sys.modules[__name__] module.window = None @@ -54,6 +58,10 @@ class SceneInventoryWindow(QtWidgets.QDialog): outdated_only_checkbox.setToolTip("Show outdated files only") outdated_only_checkbox.setChecked(False) + icon = qtawesome.icon("fa.arrow-up", color="white") + update_all_button = QtWidgets.QPushButton(self) + update_all_button.setIcon(icon) + icon = qtawesome.icon("fa.refresh", color="white") refresh_button = QtWidgets.QPushButton(self) refresh_button.setIcon(icon) @@ -62,6 +70,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): control_layout.addWidget(filter_label) control_layout.addWidget(text_filter) control_layout.addWidget(outdated_only_checkbox) + control_layout.addWidget(update_all_button) control_layout.addWidget(refresh_button) # endregion control @@ -102,7 +111,9 @@ class SceneInventoryWindow(QtWidgets.QDialog): ) view.data_changed.connect(self.refresh) refresh_button.clicked.connect(self.refresh) + update_all_button.clicked.connect(self._on_update_all) + self._update_all_button = update_all_button self._outdated_only_checkbox = outdated_only_checkbox self._view = view self._model = model @@ -158,6 +169,42 @@ class SceneInventoryWindow(QtWidgets.QDialog): self._outdated_only_checkbox.isChecked() ) + def _on_update_all(self): + """Update all items that are currently 'outdated' in the view""" + + # Get all items from outdated groups + outdated_items = [] + for index in iter_model_rows(self._model, + column=0, + include_root=False): + item = index.data(self._model.ItemRole) + + if not item.get("isGroupNode"): + continue + + # Only the group nodes contain the "highest_version" data and as + # such we find only the groups and take its children. + if not self._model.outdated(item): + continue + + # Collect all children which we want to update + children = item.children() + outdated_items.extend(children) + + if not outdated_items: + log.info("Nothing to update.") + return + + # Trigger update to latest + # Logic copied from SceneInventoryView._build_item_menu_for_selection + for item in outdated_items: + try: + api.update(item, -1) + except AssertionError: + self._show_version_error_dialog(None, [item]) + log.warning("Update failed", exc_info=True) + self._view.data_changed.emit() + def show(root=None, debug=False, parent=None, items=None): """Display Scene Inventory GUI From 0b9e669d3b6421dceefd96e87aed81166708ba52 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:49:28 +0100 Subject: [PATCH 039/302] Fix typos in class name and functions --- openpype/tools/sceneinventory/view.py | 4 ++-- openpype/tools/sceneinventory/window.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 80f26a881d..2ae8c95be4 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -20,12 +20,12 @@ DEFAULT_COLOR = "#fb9c15" log = logging.getLogger("SceneInventory") -class SceneInvetoryView(QtWidgets.QTreeView): +class SceneInventoryView(QtWidgets.QTreeView): data_changed = QtCore.Signal() hierarchy_view_changed = QtCore.Signal(bool) def __init__(self, parent=None): - super(SceneInvetoryView, self).__init__(parent=parent) + super(SceneInventoryView, self).__init__(parent=parent) # view settings self.setIndentation(12) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index a05d820ec6..d92c1f00d4 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -19,7 +19,7 @@ from .model import ( InventoryModel, FilterProxyModel ) -from .view import SceneInvetoryView +from .view import SceneInventoryView from ..utils.lib import iter_model_rows @@ -82,7 +82,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): proxy.setDynamicSortFilter(True) proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - view = SceneInvetoryView(self) + view = SceneInventoryView(self) view.setModel(proxy) # set some nice default widths for the view @@ -107,7 +107,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): self._on_outdated_state_change ) view.hierarchy_view_changed.connect( - self._on_hiearchy_view_change + self._on_hierarchy_view_change ) view.data_changed.connect(self.refresh) refresh_button.clicked.connect(self.refresh) @@ -157,7 +157,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): kwargs["selected"] = self._view._selected self._model.refresh(**kwargs) - def _on_hiearchy_view_change(self, enabled): + def _on_hierarchy_view_change(self, enabled): self._proxy.set_hierarchy_view(enabled) self._model.set_hierarchy_view(enabled) From 430f0428a2dd72e3b2d8502741302247cebdd95b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 24 Feb 2022 10:52:15 +0100 Subject: [PATCH 040/302] Add tooltips --- openpype/tools/sceneinventory/window.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index d92c1f00d4..d9d34dbb08 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -60,10 +60,12 @@ class SceneInventoryWindow(QtWidgets.QDialog): icon = qtawesome.icon("fa.arrow-up", color="white") update_all_button = QtWidgets.QPushButton(self) + update_all_button.setToolTip("Update all outdated to latest version") update_all_button.setIcon(icon) icon = qtawesome.icon("fa.refresh", color="white") refresh_button = QtWidgets.QPushButton(self) + update_all_button.setToolTip("Refresh") refresh_button.setIcon(icon) control_layout = QtWidgets.QHBoxLayout() From 02d3a5fa5764da970102229052ca6654581ccd0e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 12:30:14 +0100 Subject: [PATCH 041/302] nuke: add reformat settings for baking mov presets publish plugin --- .../defaults/project_settings/nuke.json | 25 ++++++++++++- .../schemas/schema_nuke_publish.json | 35 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 5a819e6904..238d21d43a 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -122,7 +122,30 @@ "viewer_process_override": "", "bake_viewer_process": true, "bake_viewer_input_process": true, - "add_tags": [] + "add_tags": [], + "reformat_node_add": false, + "reformat_node_config": [ + { + "name": "type", + "value": "to format" + }, + { + "name": "format", + "value": "HD_1080" + }, + { + "name": "filter", + "value": "Lanczos6" + }, + { + "name": "black_outside", + "value": "true" + }, + { + "name": "pbb", + "value": "false" + } + ] } } }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 39390f355a..81e5d2cc3f 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -226,6 +226,41 @@ "label": "Add additional tags to representations", "type": "list", "object_type": "text" + }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "reformat_node_add", + "label": "Add Reformat Node" + }, + { + "type": "collapsible-wrap", + "label": "Reformat Node Knobs", + "collapsible": true, + "collapsed": false, + "children": [ + { + "type": "list", + "key": "reformat_node_config", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Knob Name" + }, + { + "type": "text", + "key": "value", + "label": "Knob Value" + } + ] + } + } + ] } ] } From 15d4047b1fc76acec4a84633ec5621eaec8261c3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 13:59:08 +0100 Subject: [PATCH 042/302] Nuke: adding reformat to bake mov worfklow procedure --- openpype/hosts/nuke/api/plugin.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index fd754203d4..79413bab6c 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -446,6 +446,8 @@ class ExporterReviewMov(ExporterReview): return path def generate_mov(self, farm=False, **kwargs): + reformat_node_add = kwargs["reformat_node_add"] + reformat_node_config = kwargs["reformat_node_config"] bake_viewer_process = kwargs["bake_viewer_process"] bake_viewer_input_process_node = kwargs[ "bake_viewer_input_process"] @@ -483,6 +485,25 @@ class ExporterReviewMov(ExporterReview): self.previous_node = r_node self.log.debug("Read... `{}`".format(self._temp_nodes[subset])) + # add reformat node + if reformat_node_add: + rf_node = nuke.createNode("Reformat") + for kn_conf in reformat_node_config: + k_name = str(kn_conf["name"]) + k_value = str(kn_conf["value"]) + if k_value == "true": + k_value = True + if k_value == "false": + k_value = False + rf_node[k_name].setValue(k_value) + + # connect + rf_node.setInput(0, self.previous_node) + self._temp_nodes[subset].append(rf_node) + self.previous_node = rf_node + self.log.debug( + "Reformat... `{}`".format(self._temp_nodes[subset])) + # only create colorspace baking if toggled on if bake_viewer_process: if bake_viewer_input_process_node: @@ -555,7 +576,7 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - self.clean_nodes(subset) + # self.clean_nodes(subset) nuke.scriptSave() return self.data From 33cd5af26a897ffc1901617d2955a8af6e03878e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 17:24:51 +0100 Subject: [PATCH 043/302] nuke: reverse clearing disable --- openpype/hosts/nuke/api/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 79413bab6c..67c5203cda 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -576,7 +576,7 @@ class ExporterReviewMov(ExporterReview): self.log.debug("Representation... `{}`".format(self.data)) - # self.clean_nodes(subset) + self.clean_nodes(subset) nuke.scriptSave() return self.data From 8750bdae707711a64df490485efc63192853b1d2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 25 Feb 2022 17:27:18 +0100 Subject: [PATCH 044/302] global: letter box calculated on output as last process --- openpype/plugins/publish/extract_review.py | 133 ++++++++++++++------- 1 file changed, 88 insertions(+), 45 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 5f286a53e6..9d7ad26a40 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -972,11 +972,8 @@ class ExtractReview(pyblish.api.InstancePlugin): def get_letterbox_filters( self, letter_box_def, - input_res_ratio, - output_res_ratio, - pixel_aspect, - scale_factor_by_width, - scale_factor_by_height + output_width, + output_height ): output = [] @@ -996,70 +993,119 @@ class ExtractReview(pyblish.api.InstancePlugin): l_red, l_green, l_blue ) line_color_alpha = float(l_alpha) / 255 - - if input_res_ratio == output_res_ratio: - ratio /= pixel_aspect - elif input_res_ratio < output_res_ratio: - ratio /= scale_factor_by_width - else: - ratio /= scale_factor_by_height - + height_letterbox = int(output_height - (output_width * (1 / ratio))) if state == "letterbox": if fill_color_alpha > 0: top_box = ( - "drawbox=0:0:iw:round((ih-(iw*(1/{})))/2):t=fill:c={}@{}" - ).format(ratio, fill_color_hex, fill_color_alpha) + "drawbox=0:0:{widht}:round(" + "({height}-({widht}*(1/{ratio})))/2)" + ":t=fill:c={color}@{alpha}" + ).format( + widht=output_width, + height=output_height, + ratio=ratio, + color=fill_color_hex, + alpha=fill_color_alpha + ) bottom_box = ( - "drawbox=0:ih-round((ih-(iw*(1/{0})))/2)" - ":iw:round((ih-(iw*(1/{0})))/2):t=fill:c={1}@{2}" - ).format(ratio, fill_color_hex, fill_color_alpha) + "drawbox=0:{height}-round(" + "({height}-({widht}*(1/{ratio})))/2)" + ":{widht}:round(({height}-({widht}" + "*(1/{ratio})))/2):t=fill:" + "c={color}@{alpha}" + ).format( + widht=output_width, + height=output_height, + ratio=ratio, + color=fill_color_hex, + alpha=fill_color_alpha + ) - output.extend([top_box, bottom_box]) + if height_letterbox > 0: + output.extend([top_box, bottom_box]) if line_color_alpha > 0 and line_thickness > 0: top_line = ( - "drawbox=0:round((ih-(iw*(1/{0})))/2)-{1}:iw:{1}:" - "t=fill:c={2}@{3}" + "drawbox=0:round(({height}-({widht}" + "*(1/{ratio})))/2)-{l_thick}:{widht}:{l_thick}:" + "t=fill:c={l_color}@{l_alpha}" ).format( - ratio, line_thickness, line_color_hex, line_color_alpha + widht=output_width, + height=output_height, + ratio=ratio, + l_thick=line_thickness, + l_color=line_color_hex, + l_alpha=line_color_alpha ) bottom_line = ( - "drawbox=0:ih-round((ih-(iw*(1/{})))/2)" - ":iw:{}:t=fill:c={}@{}" + "drawbox=0:{height}-round(({height}-({widht}" + "*(1/{ratio})))/2)" + ":{widht}:{l_thick}:t=fill:c={l_color}@{l_alpha}" ).format( - ratio, line_thickness, line_color_hex, line_color_alpha + widht=output_width, + height=output_height, + ratio=ratio, + l_thick=line_thickness, + l_color=line_color_hex, + l_alpha=line_color_alpha ) - output.extend([top_line, bottom_line]) + if height_letterbox > 0: + output.extend([top_line, bottom_line]) elif state == "pillar": if fill_color_alpha > 0: left_box = ( - "drawbox=0:0:round((iw-(ih*{}))/2):ih:t=fill:c={}@{}" - ).format(ratio, fill_color_hex, fill_color_alpha) + "drawbox=0:0:round(({widht}-({height}" + "*{ratio}))/2):{height}:t=fill:c={color}@{alpha}" + ).format( + widht=output_width, + height=output_height, + ratio=ratio, + color=fill_color_hex, + alpha=fill_color_alpha + ) right_box = ( - "drawbox=iw-round((iw-(ih*{0}))/2))" - ":0:round((iw-(ih*{0}))/2):ih:t=fill:c={1}@{2}" - ).format(ratio, fill_color_hex, fill_color_alpha) - - output.extend([left_box, right_box]) + "drawbox={widht}-round(({widht}-({height}*{ratio}))/2))" + ":0:round(({widht}-({height}*{ratio}))/2):{height}" + ":t=fill:c={color}@{alpha}" + ).format( + widht=output_width, + height=output_height, + ratio=ratio, + color=fill_color_hex, + alpha=fill_color_alpha + ) + if height_letterbox > 0: + output.extend([left_box, right_box]) if line_color_alpha > 0 and line_thickness > 0: left_line = ( - "drawbox=round((iw-(ih*{}))/2):0:{}:ih:t=fill:c={}@{}" + "drawbox=round(({widht}-({height}*{ratio}))/2)" + ":0:{l_thick}:{height}:t=fill:c={l_color}@{l_alpha}" ).format( - ratio, line_thickness, line_color_hex, line_color_alpha + widht=output_width, + height=output_height, + ratio=ratio, + l_thick=line_thickness, + l_color=line_color_hex, + l_alpha=line_color_alpha ) right_line = ( - "drawbox=iw-round((iw-(ih*{}))/2))" - ":0:{}:ih:t=fill:c={}@{}" + "drawbox={widht}-round(({widht}-({height}*{ratio}))/2))" + ":0:{l_thick}:{height}:t=fill:c={l_color}@{l_alpha}" ).format( - ratio, line_thickness, line_color_hex, line_color_alpha + widht=output_width, + height=output_height, + ratio=ratio, + l_thick=line_thickness, + l_color=line_color_hex, + l_alpha=line_color_alpha ) - - output.extend([left_line, right_line]) + if height_letterbox > 0: + output.extend([left_line, right_line]) else: raise ValueError( @@ -1259,11 +1305,8 @@ class ExtractReview(pyblish.api.InstancePlugin): filters.extend( self.get_letterbox_filters( letter_box_def, - input_res_ratio, - output_res_ratio, - pixel_aspect, - scale_factor_by_width, - scale_factor_by_height + output_width, + output_height ) ) From 9ab32bd634382a0f877b45487d7e89676f2f86e6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 28 Feb 2022 11:39:14 +0100 Subject: [PATCH 045/302] Added possibility to create texture subset from dynamically parsed group names --- .../plugins/publish/collect_texture.py | 81 +++++++++++++------ 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index 596a8ccfd2..8cf36fd489 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -147,6 +147,13 @@ class CollectTextures(pyblish.api.ContextPlugin): } resource_files[workfile_subset].append(item) + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["textures"], + self.input_naming_groups["textures"], + self.color_space + ) + if ext in self.texture_extensions: c_space = self._get_color_space( repre_file, @@ -167,13 +174,15 @@ class CollectTextures(pyblish.api.ContextPlugin): self.color_space ) - formatting_data = { + explicit_data = { "color_space": c_space or '', # None throws exception "channel": channel or '', "shader": shader or '', "subset": parsed_subset or '' } + formatting_data.update(explicit_data) + fill_pairs = prepare_template_data(formatting_data) subset = format_template_with_optional_keys( fill_pairs, self.texture_subset_template) @@ -320,13 +329,14 @@ class CollectTextures(pyblish.api.ContextPlugin): """ asset_name = "NOT_AVAIL" - return self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'asset') or asset_name + return (self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'asset') or + asset_name) def _get_version(self, name, input_naming_patterns, input_naming_groups, color_spaces): - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'version') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'version') if found: return found.replace('v', '') @@ -336,8 +346,8 @@ class CollectTextures(pyblish.api.ContextPlugin): def _get_udim(self, name, input_naming_patterns, input_naming_groups, color_spaces): """Parses from 'name' udim value.""" - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'udim') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'udim') if found: return found @@ -375,8 +385,8 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'shader') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'shader') if found: return found @@ -389,15 +399,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse(name, input_naming_patterns, input_naming_groups, - color_spaces, 'channel') + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, 'channel') if found: return found self.log.warning("Didn't find channel in {}".format(name)) - def _parse(self, name, input_naming_patterns, input_naming_groups, - color_spaces, key): + def _parse_key(self, name, input_naming_patterns, input_naming_groups, + color_spaces, key): """Universal way to parse 'name' with configurable regex groups. Args: @@ -411,23 +421,46 @@ class CollectTextures(pyblish.api.ContextPlugin): Raises: ValueError - if broken 'input_naming_groups' """ + parsed_groups = self._get_parsed_groups(name, + input_naming_patterns, + input_naming_groups, + color_spaces) + + try: + parsed_value = parsed_groups[key] + return parsed_value + except IndexError: + msg = ("input_naming_groups must " + + "have '{}' key".format(key)) + raise ValueError(msg) + + def _get_parsed_groups(self, name, input_naming_patterns, + input_naming_groups, color_spaces): + """Universal way to parse 'name' with configurable regex groups. + + Args: + name (str): workfile name or texture name + input_naming_patterns (list): + [workfile_pattern] or [texture_pattern] + input_naming_groups (list) + ordinal position of regex groups matching to input_naming.. + color_spaces (list) - predefined color spaces + + Returns: + (dict) {group_name:parsed_value} + """ for input_pattern in input_naming_patterns: for cs in color_spaces: pattern = input_pattern.replace('{color_space}', cs) regex_result = re.findall(pattern, name) if regex_result: - idx = list(input_naming_groups).index(key) - if idx < 0: - msg = "input_naming_groups must " +\ - "have '{}' key".format(key) - raise ValueError(msg) + if len(regex_result[0]) == len(input_naming_groups): + return dict(zip(input_naming_groups, regex_result[0])) + else: + self.log.warning("No of parsed groups doesn't match " + "no of group labels") - try: - parsed_value = regex_result[0][idx] - return parsed_value - except IndexError: - self.log.warning("Wrong index, probably " - "wrong name {}".format(name)) + return {} def _update_representations(self, upd_representations): """Frames dont have sense for textures, add collected udims instead.""" From 52a5d13ca74df5626b46197e4616715c5b6eaa99 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 28 Feb 2022 13:50:07 +0100 Subject: [PATCH 046/302] Added possibility to create workfile subset from dynamically parsed group names --- .../plugins/publish/collect_texture.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index 8cf36fd489..e441218ca7 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -81,14 +81,10 @@ class CollectTextures(pyblish.api.ContextPlugin): parsed_subset = instance.data["subset"].replace( instance.data["family"], '') - fill_pairs = { + explicit_data = { "subset": parsed_subset } - fill_pairs = prepare_template_data(fill_pairs) - workfile_subset = format_template_with_optional_keys( - fill_pairs, self.workfile_subset_template) - processed_instance = False for repre in instance.data["representations"]: ext = repre["ext"].replace('.', '') @@ -102,6 +98,18 @@ class CollectTextures(pyblish.api.ContextPlugin): if ext in self.main_workfile_extensions or \ ext in self.other_workfile_extensions: + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["workfile"], + self.input_naming_groups["workfile"], + self.color_space + ) + + formatting_data.update(explicit_data) + fill_pairs = prepare_template_data(formatting_data) + workfile_subset = format_template_with_optional_keys( + fill_pairs, self.workfile_subset_template) + asset_build = self._get_asset_build( repre_file, self.input_naming_patterns["workfile"], From 8eddbab5035f7367bbe6c79107dafc4241a33cdb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 28 Feb 2022 20:51:47 +0100 Subject: [PATCH 047/302] implement function which looks for executable --- openpype/lib/vendor_bin_utils.py | 53 +++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 4c2cf93dfa..bfdfd3174d 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -5,7 +5,58 @@ import platform import subprocess import distutils -log = logging.getLogger("FFmpeg utils") +log = logging.getLogger("Vendor utils") + + +def find_executable(executable): + """Find full path to executable. + + Also tries additional extensions if passed executable does not contain one. + + Paths where it is looked for executable is defined by 'PATH' environment + variable, 'os.confstr("CS_PATH")' or 'os.defpath'. + + Args: + executable(str): Name of executable with or without extension. Can be + path to file. + + Returns: + str: Full path to executable with extension (is file). + None: When the executable was not found. + """ + if os.path.isfile(executable): + return executable + + low_platform = platform.system().lower() + _, ext = os.path.splitext(executable) + variants = [executable] + if not ext: + if low_platform == "windows": + exts = [".exe", ".ps1", ".bat"] + for ext in os.getenv("PATHEXT", "").split(os.pathsep): + ext = ext.lower() + if ext and ext not in exts: + exts.append(ext) + else: + exts = [".sh"] + + for ext in exts: + variant = executable + ext + if os.path.isfile(variant): + return variant + variants.append(variant) + + path_str = os.environ.get("PATH", None) + if path_str is None: + if hasattr(os, "confstr"): + path_str = os.confstr("CS_PATH") + elif hasattr(os, "defpath"): + path_str = os.defpath + + if not path_str: + return None + + paths = path_str.split(os.pathsep) def get_vendor_bin_path(bin_app): From 77232a07efd1eaefebb313d714d98f6487a27100 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 28 Feb 2022 20:52:21 +0100 Subject: [PATCH 048/302] replace distutils find_executable with custom version --- openpype/lib/__init__.py | 17 +++++++++-------- openpype/lib/applications.py | 8 +++++--- openpype/lib/execute.py | 4 ++-- openpype/lib/vendor_bin_utils.py | 17 +++++++++-------- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 882ff03e61..63173941c5 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -16,6 +16,14 @@ sys.path.insert(0, python_version_dir) site.addsitedir(python_version_dir) +from .vendor_bin_utils import ( + find_executable, + get_vendor_bin_path, + get_oiio_tools_path, + get_ffmpeg_tool_path, + ffprobe_streams, + is_oiio_supported +) from .env_tools import ( env_value_to_bool, get_paths_from_environ, @@ -48,14 +56,6 @@ from .anatomy import ( from .config import get_datetime_data -from .vendor_bin_utils import ( - get_vendor_bin_path, - get_oiio_tools_path, - get_ffmpeg_tool_path, - ffprobe_streams, - is_oiio_supported -) - from .python_module_tools import ( import_filepath, modules_from_path, @@ -184,6 +184,7 @@ from .openpype_version import ( terminal = Terminal __all__ = [ + "find_executable", "get_openpype_execute_args", "get_pype_execute_args", "get_linux_launcher_args", diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 0b51a6629c..5613d8cccf 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -35,8 +35,10 @@ from .python_module_tools import ( modules_from_path, classes_from_module ) -from .execute import get_linux_launcher_args - +from .execute import ( + find_executable, + get_linux_launcher_args +) _logger = None @@ -646,7 +648,7 @@ class ApplicationExecutable: def _realpath(self): """Check if path is valid executable path.""" # Check for executable in PATH - result = distutils.spawn.find_executable(self.executable_path) + result = find_executable(self.executable_path) if result is not None: return result diff --git a/openpype/lib/execute.py b/openpype/lib/execute.py index f2eb97c5f5..c3e35772f3 100644 --- a/openpype/lib/execute.py +++ b/openpype/lib/execute.py @@ -4,9 +4,9 @@ import subprocess import platform import json import tempfile -import distutils.spawn from .log import PypeLogger as Logger +from .vendor_bin_utils import find_executable # MSDN process creation flag (Windows only) CREATE_NO_WINDOW = 0x08000000 @@ -341,7 +341,7 @@ def get_linux_launcher_args(*args): os.path.dirname(openpype_executable), filename ) - executable_path = distutils.spawn.find_executable(new_executable) + executable_path = find_executable(new_executable) if executable_path is None: return None launch_args = [executable_path] diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index bfdfd3174d..6571e2f515 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -3,7 +3,6 @@ import logging import json import platform import subprocess -import distutils log = logging.getLogger("Vendor utils") @@ -57,6 +56,12 @@ def find_executable(executable): return None paths = path_str.split(os.pathsep) + for path in paths: + for variant in variants: + filepath = os.path.abspath(os.path.join(path, executable)) + if os.path.isfile(filepath): + return filepath + return None def get_vendor_bin_path(bin_app): @@ -92,11 +97,7 @@ def get_oiio_tools_path(tool="oiiotool"): Default is "oiiotool". """ oiio_dir = get_vendor_bin_path("oiio") - if platform.system().lower() == "windows" and not tool.lower().endswith( - ".exe" - ): - tool = "{}.exe".format(tool) - return os.path.join(oiio_dir, tool) + return find_executable(os.path.join(oiio_dir, tool)) def get_ffmpeg_tool_path(tool="ffmpeg"): @@ -112,7 +113,7 @@ def get_ffmpeg_tool_path(tool="ffmpeg"): ffmpeg_dir = get_vendor_bin_path("ffmpeg") if platform.system().lower() == "windows": ffmpeg_dir = os.path.join(ffmpeg_dir, "bin") - return os.path.join(ffmpeg_dir, tool) + return find_executable(os.path.join(ffmpeg_dir, tool)) def ffprobe_streams(path_to_file, logger=None): @@ -173,7 +174,7 @@ def is_oiio_supported(): """ loaded_path = oiio_path = get_oiio_tools_path() if oiio_path: - oiio_path = distutils.spawn.find_executable(oiio_path) + oiio_path = find_executable(oiio_path) if not oiio_path: log.debug("OIIOTool is not configured or not present at {}".format( From f04ea594e1d86a86234cd3e39ab4c533956380db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 11:48:25 +0100 Subject: [PATCH 049/302] removed duplicated code --- .../tools/publisher/widgets/validations_widget.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/openpype/tools/publisher/widgets/validations_widget.py b/openpype/tools/publisher/widgets/validations_widget.py index 0db30acd6e..798c1f9d92 100644 --- a/openpype/tools/publisher/widgets/validations_widget.py +++ b/openpype/tools/publisher/widgets/validations_widget.py @@ -203,17 +203,6 @@ class ValidationErrorTitleWidget(QtWidgets.QWidget): self._title_frame.setProperty("selected", value) self._title_frame.style().polish(self._title_frame) - def current_desctiption_text(self): - if self._context_validation: - return self._help_text_by_instance_id[None] - index = self._instances_view.currentIndex() - # TODO make sure instance is selected - if not index.isValid(): - index = self._instances_model.index(0, 0) - - indence_id = index.data(INSTANCE_ID_ROLE) - return self._help_text_by_instance_id[indence_id] - def set_selected(self, selected=None): """Change selected state of widget.""" if selected is None: @@ -557,9 +546,6 @@ class ValidationsWidget(QtWidgets.QWidget): self._previous_select = self._title_widgets[index] error_item = self._error_info[index] - self._actions_widget.set_plugin(error_item["plugin"]) - - self._update_description() self._actions_widget.set_plugin(error_item["plugin"]) From 863753705680abde97b6f0795521dd8cdfa527ba Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 12:20:19 +0100 Subject: [PATCH 050/302] remove adding of exe to maketx --- openpype/hosts/maya/plugins/publish/extract_look.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_look.py b/openpype/hosts/maya/plugins/publish/extract_look.py index a9a2a7b60c..a8893072d0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_look.py +++ b/openpype/hosts/maya/plugins/publish/extract_look.py @@ -4,7 +4,6 @@ import os import sys import json import tempfile -import platform import contextlib import subprocess from collections import OrderedDict @@ -64,10 +63,6 @@ def maketx(source, destination, *args): maketx_path = get_oiio_tools_path("maketx") - if platform.system().lower() == "windows": - # Ensure .exe extension - maketx_path += ".exe" - if not os.path.exists(maketx_path): print( "OIIO tool not found in {}".format(maketx_path)) From 06783daf8eee247170250cf09daff858bd8ebf43 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 12:22:07 +0100 Subject: [PATCH 051/302] fix usage of variables --- openpype/lib/vendor_bin_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 6571e2f515..742023a0d7 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -58,7 +58,7 @@ def find_executable(executable): paths = path_str.split(os.pathsep) for path in paths: for variant in variants: - filepath = os.path.abspath(os.path.join(path, executable)) + filepath = os.path.abspath(os.path.join(path, variant)) if os.path.isfile(filepath): return filepath return None From c999dd5a918448a1c395a41d2370dd21f90b0b7c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 12:24:27 +0100 Subject: [PATCH 052/302] added few comments --- openpype/lib/vendor_bin_utils.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 742023a0d7..5698ede16a 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -23,12 +23,16 @@ def find_executable(executable): str: Full path to executable with extension (is file). None: When the executable was not found. """ + # Skip if passed path is file if os.path.isfile(executable): return executable low_platform = platform.system().lower() _, ext = os.path.splitext(executable) + + # Prepare variants for which it will be looked variants = [executable] + # Add other extension variants only if passed executable does not have one if not ext: if low_platform == "windows": exts = [".exe", ".ps1", ".bat"] @@ -45,6 +49,7 @@ def find_executable(executable): return variant variants.append(variant) + # Get paths where to look for executable path_str = os.environ.get("PATH", None) if path_str is None: if hasattr(os, "confstr"): @@ -52,15 +57,13 @@ def find_executable(executable): elif hasattr(os, "defpath"): path_str = os.defpath - if not path_str: - return None - - paths = path_str.split(os.pathsep) - for path in paths: - for variant in variants: - filepath = os.path.abspath(os.path.join(path, variant)) - if os.path.isfile(filepath): - return filepath + if path_str: + paths = path_str.split(os.pathsep) + for path in paths: + for variant in variants: + filepath = os.path.abspath(os.path.join(path, variant)) + if os.path.isfile(filepath): + return filepath return None From 5bf2bd2efad7a1ae0e68aeb55ecbc89aed881607 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 14:08:56 +0100 Subject: [PATCH 053/302] removed unused import --- openpype/lib/applications.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index 5613d8cccf..89b016922d 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -7,7 +7,6 @@ import platform import collections import inspect import subprocess -import distutils.spawn from abc import ABCMeta, abstractmethod import six From e0e26a5d1cf7774c32b0d83b0d7800a48892a681 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Mar 2022 16:42:03 +0100 Subject: [PATCH 054/302] general: removing obsolete way of nuke bake farm publishing --- .../deadline/plugins/publish/submit_publish_job.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index c7a14791e4..1de1c37575 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -516,7 +516,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): """ representations = [] collections, remainders = clique.assemble(exp_files) - bake_renders = instance.get("bakingNukeScripts", []) # create representation for every collected sequento ce for collection in collections: @@ -534,9 +533,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): preview = True break - if bake_renders: - preview = False - # toggle preview on if multipart is on if instance.get("multipartExr", False): preview = True @@ -610,16 +606,6 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): }) self._solve_families(instance, True) - if (bake_renders - and remainder in bake_renders[0]["bakeRenderPath"]): - rep.update({ - "fps": instance.get("fps"), - "tags": ["review", "delete"] - }) - # solve families with `preview` attributes - self._solve_families(instance, True) - representations.append(rep) - return representations def _solve_families(self, instance, preview=False): From d8a3ffe5125cebdc3ac13d44d8629bc2ce4c353c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Mar 2022 16:44:30 +0100 Subject: [PATCH 055/302] nuke: including representation even it is from farm --- .../hosts/nuke/plugins/publish/extract_review_data_mov.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 5bbc88266a..d8c94dfdec 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -113,9 +113,11 @@ class ExtractReviewDataMov(openpype.api.Extractor): }) else: data = exporter.generate_mov(**o_data) - generated_repres.extend(data["representations"]) - self.log.info(generated_repres) + # add representation generated by exporter + generated_repres.extend(data["representations"]) + self.log.debug( + "__ generated_repres: {}".format(generated_repres)) if generated_repres: # assign to representations From c82ee012ab7f9566e4b2971c3fb7089abd1d556c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 1 Mar 2022 16:48:28 +0100 Subject: [PATCH 056/302] nuke: baking generator returning representation even on farm --- openpype/hosts/nuke/api/plugin.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index fd754203d4..32b69d4604 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -152,6 +152,7 @@ class ExporterReview(object): """ data = None + publish_on_farm = False def __init__(self, klass, @@ -210,6 +211,9 @@ class ExporterReview(object): if self.multiple_presets: repre["outputName"] = self.name + if self.publish_on_farm: + repre["tags"].append("publish_on_farm") + self.data["representations"].append(repre) def get_view_input_process_node(self): @@ -446,6 +450,7 @@ class ExporterReviewMov(ExporterReview): return path def generate_mov(self, farm=False, **kwargs): + self.publish_on_farm = farm bake_viewer_process = kwargs["bake_viewer_process"] bake_viewer_input_process_node = kwargs[ "bake_viewer_input_process"] @@ -537,7 +542,7 @@ class ExporterReviewMov(ExporterReview): # ---------- end nodes creation # ---------- render or save to nk - if farm: + if self.publish_on_farm: nuke.scriptSave() path_nk = self.save_file() self.data.update({ @@ -547,11 +552,12 @@ class ExporterReviewMov(ExporterReview): }) else: self.render(write_node.name()) - # ---------- generate representation data - self.get_representation_data( - tags=["review", "delete"] + add_tags, - range=True - ) + + # ---------- generate representation data + self.get_representation_data( + tags=["review", "delete"] + add_tags, + range=True + ) self.log.debug("Representation... `{}`".format(self.data)) From 0568a8061881300aefd6e3746e093ded4076a4b7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 1 Mar 2022 17:00:08 +0100 Subject: [PATCH 057/302] added 'is_file_executable' to check if file can be executed --- openpype/lib/vendor_bin_utils.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 5698ede16a..4be016f656 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -7,6 +7,25 @@ import subprocess log = logging.getLogger("Vendor utils") +def is_file_executable(filepath): + """Filepath lead to executable file. + + Args: + filepath(str): Full path to file. + """ + if not filepath: + return False + + if os.path.isfile(filepath): + if os.access(filepath, os.X_OK): + return True + + log.info( + "Filepath is not available for execution \"{}\"".format(filepath) + ) + return False + + def find_executable(executable): """Find full path to executable. @@ -24,7 +43,7 @@ def find_executable(executable): None: When the executable was not found. """ # Skip if passed path is file - if os.path.isfile(executable): + if is_file_executable(executable): return executable low_platform = platform.system().lower() @@ -45,7 +64,7 @@ def find_executable(executable): for ext in exts: variant = executable + ext - if os.path.isfile(variant): + if is_file_executable(variant): return variant variants.append(variant) @@ -62,7 +81,7 @@ def find_executable(executable): for path in paths: for variant in variants: filepath = os.path.abspath(os.path.join(path, variant)) - if os.path.isfile(filepath): + if is_file_executable(filepath): return filepath return None From 51957dd3aec5d09c83ffca921966229d4a382b3f Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Wed, 2 Mar 2022 15:37:53 +0700 Subject: [PATCH 058/302] Deformer node ids validation plugin for Maya --- .../validate_node_ids_deformer_transfer.py | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py diff --git a/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py b/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py new file mode 100644 index 0000000000..67b4aff136 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py @@ -0,0 +1,105 @@ +from maya import cmds + +import pyblish.api +import openpype.api +import openpype.hosts.maya.api.action +from openpype.hosts.maya.api import lib + + +class ValidateNodeIdsDeformerTransfer(pyblish.api.InstancePlugin): + """Validate if deformed shapes have related IDs to the original + shapes. + + When a deformer is applied in the scene on a mesh, + Maya creates a new "deformer" shape node for the mesh. + This new node does not get the original ID and later references + to the original node ID don't match. + + This validator checks whether the IDs are valid on all the shape + nodes in the instance. + """ + + order = openpype.api.ValidateContentsOrder + families = ['rig'] + hosts = ['maya'] + label = 'Deformed shape ids transferred' + actions = [ + openpype.hosts.maya.api.action.SelectInvalidAction, + openpype.api.RepairAction + ] + + def process(self, instance): + """Process all the nodes in the instance""" + + # Ensure nodes with sibling share the same ID + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Shapes found that are considered 'Deformed'" + " with invalid object ids: {0}".format(invalid) + ) + + @classmethod + def get_invalid(cls, instance): + """Get all nodes which do not match the criteria""" + + shapes = cmds.ls(instance[:], + dag=True, + leaf=True, + shapes=True, + long=True, + noIntermediate=True) + + invalid = [] + for shape in shapes: + sibling_id = cls._get_id_from_sibling(shape) + if not sibling_id: + continue + + current_id = lib.get_id(shape) + if current_id != sibling_id: + invalid.append(shape) + + return invalid + + @classmethod + def _get_id_from_sibling(cls, node): + """In some cases, the history of the deformed shapes cannot be used + to get the original shape, as the relation with the orignal shape + has been lost. + The original shape can be found as a sibling of the deformed shape + (sharing same transform parent), which has the "intermediate object" + attribute set. + The ID of that shape node can then be transferred to the deformed + shape node. + """ + + # Get long name + node = cmds.ls(node, long=True)[0] + + parent = cmds.listRelatives(node, parent=True, fullPath=True) + + # Get siblings of same type + node_type = cmds.nodeType(node) + similar_nodes = cmds.listRelatives(parent, type=node_type, fullPath=1) + # Exclude itself + similar_nodes = [x for x in similar_nodes if x != node] + + for similar_node in similar_nodes: + # Make sure it is an "intermediate object" + if cmds.getAttr(similar_node + ".io"): + _id = lib.get_id(similar_node) + if _id: + return _id + + @classmethod + def repair(cls, instance): + + for node in cls.get_invalid(instance): + # Get the original id from sibling + sibling_id = cls._get_id_from_sibling(node) + if not sibling_id: + cls.log.error("Could not find ID from sibling for '%s'", node) + continue + + lib.set_id(node, sibling_id, overwrite=True) From d714e52921ca6a36d2568e1a9e98fc7da8085662 Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Wed, 2 Mar 2022 18:47:07 +0700 Subject: [PATCH 059/302] Refactor to existing lib function + plugin --- openpype/hosts/maya/api/lib.py | 30 ++++- ...date_animation_out_set_related_node_ids.py | 4 +- .../validate_node_ids_deformed_shapes.py | 4 +- .../validate_node_ids_deformer_transfer.py | 105 ------------------ .../publish/validate_rig_out_set_node_ids.py | 16 +-- 5 files changed, 38 insertions(+), 121 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 41528f20ba..2f7a09d4c4 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1751,18 +1751,24 @@ def remove_other_uv_sets(mesh): cmds.removeMultiInstance(attr, b=True) -def get_id_from_history(node): +def get_id_from_sibling(node, history_only=True): """Return first node id in the history chain that matches this node. The nodes in history must be of the exact same node type and must be parented under the same parent. + If no matching node is found in history, the siblings of the node + are checked. Additionally to having the same parent, the sibling must + be marked as 'intermediate object'. + Args: - node (str): node to retrieve the + node (str): node to retrieve the history from + history_only (bool): also looks in node's siblings if True + and if nothing found in history Returns: - str or None: The id from the node in history or None when no id found - on any valid nodes in the history. + str or None: The id from the sibling node or None when no id found + on any valid nodes in the history or siblings. """ @@ -1791,6 +1797,22 @@ def get_id_from_history(node): if _id: return _id + if not history_only: + # Get siblings of same type + similar_nodes = cmds.listRelatives(parent, + type=node_type, + fullPath=True) + # Exclude itself + similar_nodes = [x for x in similar_nodes if x != node] + + for similar_node in similar_nodes: + # Check if "intermediate object" + if cmds.getAttr(similar_node + ".io"): + _id = get_id(similar_node) + if _id: + return _id + + # Project settings def set_scene_fps(fps, update=True): diff --git a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py index 00f0d38775..7c1c695237 100644 --- a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py @@ -65,7 +65,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): invalid.append(node) continue - history_id = lib.get_id_from_history(node) + history_id = lib.get_id_from_sibling(node) if history_id is not None and node_id != history_id: invalid.append(node) @@ -76,7 +76,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): for node in cls.get_invalid(instance): # Get the original id from history - history_id = lib.get_id_from_history(node) + history_id = lib.get_id_from_sibling(node) if not history_id: cls.log.error("Could not find ID in history for '%s'", node) continue diff --git a/openpype/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py b/openpype/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py index a4d4d2bcc2..0324be9fc9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py +++ b/openpype/hosts/maya/plugins/publish/validate_node_ids_deformed_shapes.py @@ -48,7 +48,7 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin): invalid = [] for shape in shapes: - history_id = lib.get_id_from_history(shape) + history_id = lib.get_id_from_sibling(shape) if history_id: current_id = lib.get_id(shape) if current_id != history_id: @@ -61,7 +61,7 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin): for node in cls.get_invalid(instance): # Get the original id from history - history_id = lib.get_id_from_history(node) + history_id = lib.get_id_from_sibling(node) if not history_id: cls.log.error("Could not find ID in history for '%s'", node) continue diff --git a/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py b/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py deleted file mode 100644 index 67b4aff136..0000000000 --- a/openpype/hosts/maya/plugins/publish/validate_node_ids_deformer_transfer.py +++ /dev/null @@ -1,105 +0,0 @@ -from maya import cmds - -import pyblish.api -import openpype.api -import openpype.hosts.maya.api.action -from openpype.hosts.maya.api import lib - - -class ValidateNodeIdsDeformerTransfer(pyblish.api.InstancePlugin): - """Validate if deformed shapes have related IDs to the original - shapes. - - When a deformer is applied in the scene on a mesh, - Maya creates a new "deformer" shape node for the mesh. - This new node does not get the original ID and later references - to the original node ID don't match. - - This validator checks whether the IDs are valid on all the shape - nodes in the instance. - """ - - order = openpype.api.ValidateContentsOrder - families = ['rig'] - hosts = ['maya'] - label = 'Deformed shape ids transferred' - actions = [ - openpype.hosts.maya.api.action.SelectInvalidAction, - openpype.api.RepairAction - ] - - def process(self, instance): - """Process all the nodes in the instance""" - - # Ensure nodes with sibling share the same ID - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Shapes found that are considered 'Deformed'" - " with invalid object ids: {0}".format(invalid) - ) - - @classmethod - def get_invalid(cls, instance): - """Get all nodes which do not match the criteria""" - - shapes = cmds.ls(instance[:], - dag=True, - leaf=True, - shapes=True, - long=True, - noIntermediate=True) - - invalid = [] - for shape in shapes: - sibling_id = cls._get_id_from_sibling(shape) - if not sibling_id: - continue - - current_id = lib.get_id(shape) - if current_id != sibling_id: - invalid.append(shape) - - return invalid - - @classmethod - def _get_id_from_sibling(cls, node): - """In some cases, the history of the deformed shapes cannot be used - to get the original shape, as the relation with the orignal shape - has been lost. - The original shape can be found as a sibling of the deformed shape - (sharing same transform parent), which has the "intermediate object" - attribute set. - The ID of that shape node can then be transferred to the deformed - shape node. - """ - - # Get long name - node = cmds.ls(node, long=True)[0] - - parent = cmds.listRelatives(node, parent=True, fullPath=True) - - # Get siblings of same type - node_type = cmds.nodeType(node) - similar_nodes = cmds.listRelatives(parent, type=node_type, fullPath=1) - # Exclude itself - similar_nodes = [x for x in similar_nodes if x != node] - - for similar_node in similar_nodes: - # Make sure it is an "intermediate object" - if cmds.getAttr(similar_node + ".io"): - _id = lib.get_id(similar_node) - if _id: - return _id - - @classmethod - def repair(cls, instance): - - for node in cls.get_invalid(instance): - # Get the original id from sibling - sibling_id = cls._get_id_from_sibling(node) - if not sibling_id: - cls.log.error("Could not find ID from sibling for '%s'", node) - continue - - lib.set_id(node, sibling_id, overwrite=True) diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index e2090080f6..c1029366e8 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -51,10 +51,10 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): noIntermediate=True) for shape in shapes: - history_id = lib.get_id_from_history(shape) - if history_id: + sibling_id = lib.get_id_from_sibling(shape, history_only=False) + if sibling_id: current_id = lib.get_id(shape) - if current_id != history_id: + if current_id != sibling_id: invalid.append(shape) return invalid @@ -63,10 +63,10 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): def repair(cls, instance): for node in cls.get_invalid(instance): - # Get the original id from history - history_id = lib.get_id_from_history(node) - if not history_id: - cls.log.error("Could not find ID in history for '%s'", node) + # Get the original id from sibling + sibling_id = lib.get_id_from_sibling(node, history_only=False) + if not sibling_id: + cls.log.error("Could not find ID in siblings for '%s'", node) continue - lib.set_id(node, history_id, overwrite=True) + lib.set_id(node, sibling_id, overwrite=True) From d2ee9c023f795abdac88a420511fce4ea20c89ee Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 2 Mar 2022 13:23:57 +0100 Subject: [PATCH 060/302] Fix validate properly expected files without any frames Applicable for .mov or other formats like that. --- .../validate_expected_and_rendered_files.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py b/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py index d49e314179..c2426e0d78 100644 --- a/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py +++ b/openpype/modules/deadline/plugins/publish/validate_expected_and_rendered_files.py @@ -107,6 +107,10 @@ class ValidateExpectedFiles(pyblish.api.InstancePlugin): explicitly and manually changed the frame list on the Deadline job. """ + # no frames in file name at all, eg 'renderCompositingMain.withLut.mov' + if not frame_placeholder: + return set([file_name_template]) + real_expected_rendered = set() src_padding_exp = "%0{}d".format(len(frame_placeholder)) for frames in frame_list: @@ -130,14 +134,13 @@ class ValidateExpectedFiles(pyblish.api.InstancePlugin): # There might be cases where clique was unable to collect # collections in `collect_frames` - thus we capture that case - if frame is None: - self.log.warning("Unable to detect frame from filename: " - "{}".format(file_name)) - continue + if frame is not None: + frame_placeholder = "#" * len(frame) - frame_placeholder = "#" * len(frame) - file_name_template = os.path.basename( - file_name.replace(frame, frame_placeholder)) + file_name_template = os.path.basename( + file_name.replace(frame, frame_placeholder)) + else: + file_name_template = file_name break return file_name_template, frame_placeholder From 4740616310b90bfbbb78ac4360648a8e41571794 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Mar 2022 15:51:07 +0100 Subject: [PATCH 061/302] nuke: rework reformat knob defining --- .../defaults/project_settings/nuke.json | 9 +- .../schemas/schema_nuke_publish.json | 92 +++++++++++++++++-- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 238d21d43a..e30296d0ad 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -126,24 +126,29 @@ "reformat_node_add": false, "reformat_node_config": [ { + "type": "string", "name": "type", "value": "to format" }, { + "type": "string", "name": "format", "value": "HD_1080" }, { + "type": "string", "name": "filter", "value": "Lanczos6" }, { + "type": "bool", "name": "black_outside", - "value": "true" + "value": true }, { + "type": "bool", "name": "pbb", - "value": "false" + "value": false } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 81e5d2cc3f..f53c53c2f8 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -245,17 +245,93 @@ "type": "list", "key": "reformat_node_config", "object_type": { - "type": "dict", - "children": [ + "type": "dict-conditional", + "enum_key": "type", + "enum_label": "Type", + "enum_children": [ { - "type": "text", - "key": "name", - "label": "Knob Name" + "key": "string", + "label": "String", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] }, { - "type": "text", - "key": "value", - "label": "Knob Value" + "key": "bool", + "label": "Boolean", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "key": "number", + "label": "Number", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "list-strict", + "key": "value", + "label": "Value", + "object_types": [ + { + "type": "number", + "key": "number", + "decimal": 4 + } + ] + } + + ] + }, + { + "key": "list_numbers", + "label": "2 Numbers", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "list-strict", + "key": "value", + "label": "Value", + "object_types": [ + { + "type": "number", + "key": "x", + "decimal": 4 + }, + { + "type": "number", + "key": "y", + "decimal": 4 + } + ] + } + ] } ] } From c70015cbe675453107edb846f4f257cdb3ff1761 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 2 Mar 2022 16:34:13 +0100 Subject: [PATCH 062/302] nuke: connect api to new reformat config settings --- openpype/hosts/nuke/api/plugin.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 67c5203cda..5cc1db41c7 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -489,12 +489,14 @@ class ExporterReviewMov(ExporterReview): if reformat_node_add: rf_node = nuke.createNode("Reformat") for kn_conf in reformat_node_config: + _type = kn_conf["type"] k_name = str(kn_conf["name"]) - k_value = str(kn_conf["value"]) - if k_value == "true": - k_value = True - if k_value == "false": - k_value = False + k_value = kn_conf["value"] + + # to remove unicode as nuke doesn't like it + if _type == "string": + k_value = str(kn_conf["value"]) + rf_node[k_name].setValue(k_value) # connect From 17eaec1f5be0d8841da88403db5bcf6355f7fd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 2 Mar 2022 17:12:46 +0100 Subject: [PATCH 063/302] quick fix crypto --- openpype/plugins/publish/extract_jpeg_exr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_jpeg_exr.py b/openpype/plugins/publish/extract_jpeg_exr.py index d80b7bb9c3..99feadcc0b 100644 --- a/openpype/plugins/publish/extract_jpeg_exr.py +++ b/openpype/plugins/publish/extract_jpeg_exr.py @@ -34,7 +34,7 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): self.log.info("subset {}".format(instance.data['subset'])) # skip crypto passes. - if 'crypto' in instance.data['subset']: + if 'crypto' in instance.data['subset'].lower(): self.log.info("Skipping crypto passes.") return From bdd4e088b73c12988a48a7db916f81914c321346 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 2 Mar 2022 22:24:23 +0100 Subject: [PATCH 064/302] added todo --- openpype/plugins/publish/extract_jpeg_exr.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/plugins/publish/extract_jpeg_exr.py b/openpype/plugins/publish/extract_jpeg_exr.py index 99feadcc0b..468ed96199 100644 --- a/openpype/plugins/publish/extract_jpeg_exr.py +++ b/openpype/plugins/publish/extract_jpeg_exr.py @@ -34,6 +34,11 @@ class ExtractJpegEXR(pyblish.api.InstancePlugin): self.log.info("subset {}".format(instance.data['subset'])) # skip crypto passes. + # TODO: This is just a quick fix and has its own side-effects - it is + # affecting every subset name with `crypto` in its name. + # This must be solved properly, maybe using tags on + # representation that can be determined much earlier and + # with better precision. if 'crypto' in instance.data['subset'].lower(): self.log.info("Skipping crypto passes.") return From c95752be966e5f99f0ac25490a035a47c1264f1a Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 14:57:35 +0700 Subject: [PATCH 065/302] 'history only' as plugin setting --- .../publish/validate_rig_out_set_node_ids.py | 9 +++++++-- .../schemas/schema_maya_publish.json | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index c1029366e8..c272c5c485 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -24,6 +24,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): openpype.hosts.maya.api.action.SelectInvalidAction, openpype.api.RepairAction ] + allow_history_only = False def process(self, instance): """Process all meshes""" @@ -51,7 +52,9 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): noIntermediate=True) for shape in shapes: - sibling_id = lib.get_id_from_sibling(shape, history_only=False) + sibling_id = \ + lib.get_id_from_sibling(shape, + history_only=cls.allow_history_only) if sibling_id: current_id = lib.get_id(shape) if current_id != sibling_id: @@ -64,7 +67,9 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): for node in cls.get_invalid(instance): # Get the original id from sibling - sibling_id = lib.get_id_from_sibling(node, history_only=False) + sibling_id = \ + lib.get_id_from_sibling(node, + history_only=cls.allow_history_only) if not sibling_id: cls.log.error("Could not find ID in siblings for '%s'", node) continue diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 7c9a5a6b46..0c82997cce 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -396,6 +396,26 @@ "label": "Validate Rig Controllers" } ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateRigOutSetNodeIds", + "label": "Validate Rig Out Set Node Ids", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "allow_history_only", + "label": "Allow history only" + } + ] } ] }, From acd86c30914f1a2e52dcd3e2f3c9a63d9b3be7d7 Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 16:25:38 +0700 Subject: [PATCH 066/302] Full attribute name for readability --- openpype/hosts/maya/api/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 2f7a09d4c4..c9e10c7041 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1807,7 +1807,7 @@ def get_id_from_sibling(node, history_only=True): for similar_node in similar_nodes: # Check if "intermediate object" - if cmds.getAttr(similar_node + ".io"): + if cmds.getAttr(similar_node + ".intermediateObject"): _id = get_id(similar_node) if _id: return _id From cd498441a6aa86d02cc2deb8be6c33102797e1ff Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 16:55:40 +0700 Subject: [PATCH 067/302] Code style fix --- .../publish/validate_rig_out_set_node_ids.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index c272c5c485..ed1d36261a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -52,9 +52,10 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): noIntermediate=True) for shape in shapes: - sibling_id = \ - lib.get_id_from_sibling(shape, - history_only=cls.allow_history_only) + sibling_id = lib.get_id_from_sibling( + shape, + history_only=cls.allow_history_only + ) if sibling_id: current_id = lib.get_id(shape) if current_id != sibling_id: @@ -67,9 +68,10 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): for node in cls.get_invalid(instance): # Get the original id from sibling - sibling_id = \ - lib.get_id_from_sibling(node, - history_only=cls.allow_history_only) + sibling_id = lib.get_id_from_sibling( + node, + history_only=cls.allow_history_only + ) if not sibling_id: cls.log.error("Could not find ID in siblings for '%s'", node) continue From cc4894d899696d9f3ef80e6b9e5c21473ae610b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 3 Mar 2022 11:19:28 +0100 Subject: [PATCH 068/302] fix value changes --- openpype/settings/entities/dict_conditional.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/settings/entities/dict_conditional.py b/openpype/settings/entities/dict_conditional.py index 963fd406ed..19f326aea7 100644 --- a/openpype/settings/entities/dict_conditional.py +++ b/openpype/settings/entities/dict_conditional.py @@ -584,8 +584,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_default_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_default_value(child_value) def update_studio_value(self, value): @@ -620,8 +621,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_studio_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_studio_value(child_value) def update_project_value(self, value): @@ -656,8 +658,9 @@ class DictConditionalEntity(ItemEntity): self.enum_entity.update_project_value(enum_value) for children_by_key in self.non_gui_children.values(): + value_copy = copy.deepcopy(value) for key, child_obj in children_by_key.items(): - child_value = value.get(key, NOT_SET) + child_value = value_copy.get(key, NOT_SET) child_obj.update_project_value(child_value) def _discard_changes(self, on_change_trigger): From 4ba40f2a175ec78ac8371dbf62ea2a3eeb61998b Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 17:48:40 +0700 Subject: [PATCH 069/302] Exact type for siblings --- openpype/hosts/maya/api/lib.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index c9e10c7041..bbd7786b36 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1802,6 +1802,8 @@ def get_id_from_sibling(node, history_only=True): similar_nodes = cmds.listRelatives(parent, type=node_type, fullPath=True) + similar_nodes = cmds.ls(similar_nodes, exactType=node_type, long=True) + # Exclude itself similar_nodes = [x for x in similar_nodes if x != node] From 024a7220fe36f8a3df60347df10873aa01a1cd6b Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 17:52:18 +0700 Subject: [PATCH 070/302] Plugin setting default value --- openpype/settings/defaults/project_settings/maya.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index a756071106..b6fa3719ef 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -351,6 +351,10 @@ "optional": true, "active": true }, + "ValidateRigOutSetNodeIds": { + "enabled": true, + "allow_history_only": false + }, "ValidateCameraAttributes": { "enabled": false, "optional": true, From 8f92f392586b9e43d8c27317b0ab118dd9185582 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 12:57:33 +0100 Subject: [PATCH 071/302] general: improving letter/pillar box ratio exception --- openpype/plugins/publish/extract_review.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 9d7ad26a40..ae96f668f2 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -993,8 +993,13 @@ class ExtractReview(pyblish.api.InstancePlugin): l_red, l_green, l_blue ) line_color_alpha = float(l_alpha) / 255 - height_letterbox = int(output_height - (output_width * (1 / ratio))) - if state == "letterbox": + test_ratio_width = int( + (output_height - (output_width * (1 / ratio))) / 2 + ) + test_ratio_height = int( + (output_width - (output_height * ratio)) / 2 + ) + if state == "letterbox" and test_ratio_width: if fill_color_alpha > 0: top_box = ( "drawbox=0:0:{widht}:round(" @@ -1022,8 +1027,7 @@ class ExtractReview(pyblish.api.InstancePlugin): alpha=fill_color_alpha ) - if height_letterbox > 0: - output.extend([top_box, bottom_box]) + output.extend([top_box, bottom_box]) if line_color_alpha > 0 and line_thickness > 0: top_line = ( @@ -1050,10 +1054,10 @@ class ExtractReview(pyblish.api.InstancePlugin): l_color=line_color_hex, l_alpha=line_color_alpha ) - if height_letterbox > 0: - output.extend([top_line, bottom_line]) - elif state == "pillar": + output.extend([top_line, bottom_line]) + + elif state == "pillar" and test_ratio_height: if fill_color_alpha > 0: left_box = ( "drawbox=0:0:round(({widht}-({height}" From 62695b36c4c559cb461a6d4438a754e251e53112 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 13:00:33 +0100 Subject: [PATCH 072/302] hound catches --- openpype/plugins/publish/extract_review.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index ae96f668f2..c75eea4e06 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1026,7 +1026,6 @@ class ExtractReview(pyblish.api.InstancePlugin): color=fill_color_hex, alpha=fill_color_alpha ) - output.extend([top_box, bottom_box]) if line_color_alpha > 0 and line_thickness > 0: @@ -1054,7 +1053,6 @@ class ExtractReview(pyblish.api.InstancePlugin): l_color=line_color_hex, l_alpha=line_color_alpha ) - output.extend([top_line, bottom_line]) elif state == "pillar" and test_ratio_height: @@ -1081,8 +1079,7 @@ class ExtractReview(pyblish.api.InstancePlugin): color=fill_color_hex, alpha=fill_color_alpha ) - if height_letterbox > 0: - output.extend([left_box, right_box]) + output.extend([left_box, right_box]) if line_color_alpha > 0 and line_thickness > 0: left_line = ( @@ -1108,8 +1105,7 @@ class ExtractReview(pyblish.api.InstancePlugin): l_color=line_color_hex, l_alpha=line_color_alpha ) - if height_letterbox > 0: - output.extend([left_line, right_line]) + output.extend([left_line, right_line]) else: raise ValueError( From 3b0cee19ba2b3f9351e87c49f690950d86587b90 Mon Sep 17 00:00:00 2001 From: Derek Severin Date: Thu, 3 Mar 2022 19:20:52 +0700 Subject: [PATCH 073/302] Adapted/corrected comment --- openpype/hosts/maya/api/lib.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index bbd7786b36..62de5a96eb 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1757,14 +1757,16 @@ def get_id_from_sibling(node, history_only=True): The nodes in history must be of the exact same node type and must be parented under the same parent. - If no matching node is found in history, the siblings of the node - are checked. Additionally to having the same parent, the sibling must - be marked as 'intermediate object'. + Optionally, if no matching node is found from the history, all the + siblings of the node that are of the same type are checked. + Additionally to having the same parent, the sibling must be marked as + 'intermediate object'. Args: node (str): node to retrieve the history from - history_only (bool): also looks in node's siblings if True - and if nothing found in history + history_only (bool): if True and if nothing found in history, + look for an 'intermediate object' in all the node's siblings + of same type Returns: str or None: The id from the sibling node or None when no id found From 522770a1605297be693856d50bf6ef4ae1060c49 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 16:07:22 +0100 Subject: [PATCH 074/302] nuke: adding `reformated` tag to differentiate repre for extract review --- openpype/hosts/nuke/api/plugin.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 5cc1db41c7..3e61caedf9 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -487,6 +487,9 @@ class ExporterReviewMov(ExporterReview): # add reformat node if reformat_node_add: + # append reformated tag + add_tags.append("reformated") + rf_node = nuke.createNode("Reformat") for kn_conf in reformat_node_config: _type = kn_conf["type"] From ff6fecdc5dbc04f583c11d5735eb4ea7116752dd Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 3 Mar 2022 16:07:58 +0100 Subject: [PATCH 075/302] global: adding `reformated` tag exception into extract review --- openpype/plugins/publish/extract_review.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 5f286a53e6..b4a5117959 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1171,6 +1171,9 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("input_width: `{}`".format(input_width)) self.log.debug("input_height: `{}`".format(input_height)) + reformat_in_baking = bool("reformated" in new_repre["tags"]) + self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) + # Use instance resolution if output definition has not set it. if output_width is None or output_height is None: output_width = temp_data["resolution_width"] @@ -1182,6 +1185,17 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = input_width output_height = input_height + if reformat_in_baking: + self.log.debug(( + "Using resolution from input. It is already " + "reformated from baking process" + )) + output_width = input_width + output_height = input_height + pixel_aspect = 1 + new_repre["resolutionWidth"] = input_width + new_repre["resolutionHeight"] = input_height + output_width = int(output_width) output_height = int(output_height) From 1aecfef38a5ab0220f9e0856984ff13961e12fdd Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 3 Mar 2022 17:02:51 +0100 Subject: [PATCH 076/302] add loaded containers to published instance --- .../hosts/maya/plugins/load/load_reference.py | 21 +++++++++ .../plugins/publish/extract_maya_scene_raw.py | 45 ++++++++++++++++++- .../defaults/project_settings/maya.json | 6 +++ .../schemas/schema_maya_publish.json | 24 ++++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 0565b0b95c..859e1d339a 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -1,9 +1,11 @@ import os from maya import cmds from avalon import api +from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import get_project_settings from openpype.lib import get_creator_by_name import openpype.hosts.maya.api.plugin +from openpype.hosts.maya.api.pipeline import AVALON_CONTAINERS from openpype.hosts.maya.api.lib import maintained_selection @@ -125,6 +127,11 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes + def load(self, context, name=None, namespace=None, options=None): + super(ReferenceLoader, self).load(context, name, namespace, options) + # clean containers if present to AVALON_CONTAINERS + self._organize_containers(self[:]) + def switch(self, container, representation): self.update(container, representation) @@ -158,3 +165,17 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): options={"useSelection": True}, data={"dependencies": dependency} ) + + @staticmethod + def _organize_containers(nodes): + # type: (list) -> None + for node in nodes: + id_attr = "{}.id".format(node) + if not cmds.attributeQuery("id", node=node, exists=True): + print("-" * 80) + print("skipping {}".format(node)) + continue + if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: + print("=" * 80) + print("moving {}".format(node)) + cmds.sets(node, forceElement=AVALON_CONTAINERS) diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 9c432cbc67..591789917e 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -6,6 +6,7 @@ from maya import cmds import openpype.api from openpype.hosts.maya.api.lib import maintained_selection +from avalon.pipeline import AVALON_CONTAINER_ID class ExtractMayaSceneRaw(openpype.api.Extractor): @@ -57,10 +58,22 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): else: members = instance[:] + loaded_containers = None + if {f.lower() for f in self.add_for_families}.intersection( + {f.lower() for f in instance.data.get("families")}, + {instance.data.get("family").lower()}, + ): + loaded_containers = self._add_loaded_containers(members) + + selection = members + if loaded_containers: + self.log.info(loaded_containers) + selection += loaded_containers + # Perform extraction self.log.info("Performing extraction ...") with maintained_selection(): - cmds.select(members, noExpand=True) + cmds.select(selection, noExpand=True) cmds.file(path, force=True, typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501 @@ -83,3 +96,33 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): instance.data["representations"].append(representation) self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) + + @staticmethod + def _add_loaded_containers(members): + # type: (list) -> list + refs_to_include = [ + cmds.referenceQuery(ref, referenceNode=True) + for ref in members + if cmds.referenceQuery(ref, isNodeReferenced=True) + ] + + refs_to_include = set(refs_to_include) + + obj_sets = cmds.ls("*.id", long=True, type="objectSet", recursive=True, + objectsOnly=True) + + loaded_containers = [] + for obj_set in obj_sets: + + if not cmds.attributeQuery("id", node=obj_set, exists=True): + continue + + id_attr = "{}.id".format(obj_set) + if cmds.getAttr(id_attr) != AVALON_CONTAINER_ID: + continue + + set_content = set(cmds.sets(obj_set, query=True)) + if set_content.intersection(refs_to_include): + loaded_containers.append(obj_set) + + return loaded_containers diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index c25f416562..74ecf502d1 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -502,6 +502,12 @@ } } }, + "ExtractMayaSceneRaw": { + "enabled": true, + "add_for_families": [ + "layout" + ] + }, "ExtractCameraAlembic": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index bf7b8a22e7..5c17e3db2c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -547,6 +547,30 @@ "type": "schema", "name": "schema_maya_capture" }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractMayaSceneRaw", + "label": "Maya Scene (Raw)", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Add loaded instances to those published families:" + }, + { + "key": "add_for_families", + "label": "Families", + "type": "list", + "object_type": "text" + } + ] + }, { "type": "dict", "collapsible": true, From 3697fba45313594fa9ebdf6599916e8c174ffab3 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 4 Mar 2022 00:59:22 +0100 Subject: [PATCH 077/302] remove debug prints --- openpype/hosts/maya/plugins/load/load_reference.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 859e1d339a..0a0ce4536f 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -172,10 +172,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): for node in nodes: id_attr = "{}.id".format(node) if not cmds.attributeQuery("id", node=node, exists=True): - print("-" * 80) - print("skipping {}".format(node)) continue if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: - print("=" * 80) - print("moving {}".format(node)) cmds.sets(node, forceElement=AVALON_CONTAINERS) From 2b470aeacca5dcab37ecc5aa92e455437b165970 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:13 +0100 Subject: [PATCH 078/302] copied functions related to change of context --- openpype/lib/avalon_context.py | 155 +++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 1e8d21852b..9f6a9f9cdc 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -644,6 +644,161 @@ def get_workdir( ) +def template_data_from_session(session): + """ Return dictionary with template from session keys. + + Args: + session (dict, Optional): The Session to use. If not provided use the + currently active global Session. + Returns: + dict: All available data from session. + """ + from avalon import io + + if session is None: + session = avalon.api.Session + + project_name = session["AVALON_PROJECT"] + project_doc = io._database[project_name].find_one({"type": "project"}) + asset_doc = io._database[project_name].find_one({ + "type": "asset", + "name": session["AVALON_ASSET"] + }) + task_name = session["AVALON_TASK"] + host_name = session["AVALON_APP"] + return get_workdir_data(project_doc, asset_doc, task_name, host_name) + + +def compute_session_changes( + session, task=None, asset=None, app=None, template_key=None +): + """Compute the changes for a Session object on asset, task or app switch + + This does *NOT* update the Session object, but returns the changes + required for a valid update of the Session. + + Args: + session (dict): The initial session to compute changes to. + This is required for computing the full Work Directory, as that + also depends on the values that haven't changed. + task (str, Optional): Name of task to switch to. + asset (str or dict, Optional): Name of asset to switch to. + You can also directly provide the Asset dictionary as returned + from the database to avoid an additional query. (optimization) + app (str, Optional): Name of app to switch to. + + Returns: + dict: The required changes in the Session dictionary. + + """ + changes = dict() + + # If no changes, return directly + if not any([task, asset, app]): + return changes + + # Get asset document and asset + asset_document = None + asset_tasks = None + if isinstance(asset, dict): + # Assume asset database document + asset_document = asset + asset_tasks = asset_document.get("data", {}).get("tasks") + asset = asset["name"] + + if not asset_document or not asset_tasks: + from avalon import io + + # Assume asset name + asset_document = io.find_one( + { + "name": asset, + "type": "asset" + }, + {"data.tasks": True} + ) + assert asset_document, "Asset must exist" + + # Detect any changes compared session + mapping = { + "AVALON_ASSET": asset, + "AVALON_TASK": task, + "AVALON_APP": app, + } + changes = { + key: value + for key, value in mapping.items() + if value and value != session.get(key) + } + if not changes: + return changes + + # Compute work directory (with the temporary changed session so far) + _session = session.copy() + _session.update(changes) + + changes["AVALON_WORKDIR"] = get_workdir_from_session(_session) + + return changes + + +def get_workdir_from_session(session, template_key=None): + project_name = session["AVALON_PROJECT"] + host_name = session["AVALON_APP"] + anatomy = Anatomy(project_name) + template_data = template_data_from_session(session) + anatomy_filled = anatomy.format(template_data) + + if not template_key: + task_type = template_data["task"]["type"] + template_key = get_workfile_template_key( + task_type, + host_name, + project_name=project_name + ) + return anatomy_filled[template_key]["folder"] + + +def update_current_task(task=None, asset=None, app=None, template_key=None): + """Update active Session to a new task work area. + + This updates the live Session to a different `asset`, `task` or `app`. + + Args: + task (str): The task to set. + asset (str): The asset to set. + app (str): The app to set. + + Returns: + dict: The changed key, values in the current Session. + + """ + import avalon.api + from avalon.pipeline import emit + + changes = compute_session_changes( + avalon.api.Session, + task=task, + asset=asset, + app=app, + template_key=template_key + ) + + # Update the Session and environments. Pop from environments all keys with + # value set to None. + for key, value in changes.items(): + avalon.api.Session[key] = value + if value is None: + os.environ.pop(key, None) + else: + os.environ[key] = value + + # Emit session change + emit("taskChanged", changes.copy()) + + return changes + + @with_avalon def get_workfile_doc(asset_id, task_name, filename, dbcon=None): """Return workfile document for entered context. From 81d8e4d4ccd3668fca5f3a4f54e932131691d96d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:30 +0100 Subject: [PATCH 079/302] use change context function in workfiles tool --- openpype/tools/workfiles/app.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 3a772a038c..aece7bfb4f 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -29,6 +29,10 @@ from openpype.lib import ( create_workdir_extra_folders, get_system_general_anatomy_data ) +from openpype.lib.avalon_context import ( + update_current_task, + compute_session_changes +) from .model import FilesModel from .view import FilesView @@ -667,7 +671,7 @@ class FilesWidget(QtWidgets.QWidget): session["AVALON_APP"], project_name=session["AVALON_PROJECT"] ) - changes = pipeline.compute_session_changes( + changes = compute_session_changes( session, asset=self._get_asset_doc(), task=self._task_name, @@ -681,7 +685,7 @@ class FilesWidget(QtWidgets.QWidget): """Enter the asset and task session currently selected""" session = api.Session.copy() - changes = pipeline.compute_session_changes( + changes = compute_session_changes( session, asset=self._get_asset_doc(), task=self._task_name, @@ -692,7 +696,7 @@ class FilesWidget(QtWidgets.QWidget): # to avoid any unwanted Task Changed callbacks to be triggered. return - api.update_current_task( + update_current_task( asset=self._get_asset_doc(), task=self._task_name, template_key=self.template_key From 8f88e7b9250d541c2428e479150f9a78db664125 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:01:42 +0100 Subject: [PATCH 080/302] modifid assetcreator imports --- openpype/tools/assetcreator/app.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/tools/assetcreator/app.py b/openpype/tools/assetcreator/app.py index 1d332d647e..60ef31e859 100644 --- a/openpype/tools/assetcreator/app.py +++ b/openpype/tools/assetcreator/app.py @@ -4,9 +4,11 @@ from subprocess import Popen import ftrack_api from Qt import QtWidgets, QtCore +from openpype import style from openpype.api import get_current_project_settings +from openpype.lib.avalon_context import update_current_task from openpype.tools.utils.lib import qt_app_context -from avalon import io, api, style, schema +from avalon import io, api, schema from . import widget, model module = sys.modules[__name__] @@ -463,12 +465,12 @@ class Window(QtWidgets.QDialog): return task_name = task_model.itemData(index)[0] try: - api.update_current_task(task=task_name, asset=asset_name) + update_current_task(task=task_name, asset=asset_name) self.open_app() finally: if origin_task is not None and origin_asset is not None: - api.update_current_task( + update_current_task( task=origin_task, asset=origin_asset ) From 59c5c464ccd3960dbf2e5260024d42c2c58436eb Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:02:19 +0100 Subject: [PATCH 081/302] change how fusion calcuates workdir path --- openpype/hosts/fusion/scripts/fusion_switch_shot.py | 9 +++------ openpype/hosts/fusion/utility_scripts/switch_ui.py | 13 +++---------- openpype/scripts/fusion_switch_shot.py | 9 ++++----- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/fusion/scripts/fusion_switch_shot.py b/openpype/hosts/fusion/scripts/fusion_switch_shot.py index 9dd8a351e4..ed6a06bb34 100644 --- a/openpype/hosts/fusion/scripts/fusion_switch_shot.py +++ b/openpype/hosts/fusion/scripts/fusion_switch_shot.py @@ -5,11 +5,12 @@ import logging # Pipeline imports import avalon.api -from avalon import io, pipeline +from avalon import io from openpype.lib import version_up from openpype.hosts.fusion import api from openpype.hosts.fusion.api import lib +from openpype.lib.avalon_context import get_workdir_from_session log = logging.getLogger("Update Slap Comp") @@ -46,12 +47,8 @@ def _format_version_folder(folder): def _get_work_folder(session): """Convenience function to get the work folder path of the current asset""" - # Get new filename, create path based on asset and work template - template_work = self._project["config"]["template"]["work"] - work_path = pipeline._format_work_template(template_work, session) - - return os.path.normpath(work_path) + return get_workdir_from_session(session) def _get_fusion_instance(): diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index fe324d9a41..854c2fd415 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -5,11 +5,12 @@ import logging from Qt import QtWidgets, QtCore import avalon.api -from avalon import io, pipeline +from avalon import io from avalon.vendor import qtawesome as qta from openpype import style from openpype.hosts.fusion import api +from openpype.lib.avalon_context import get_workdir_from_session log = logging.getLogger("Fusion Switch Shot") @@ -158,15 +159,7 @@ class App(QtWidgets.QWidget): switch_shot.switch(asset_name=asset, filepath=file_name, new=True) def _get_context_directory(self): - - project = io.find_one({"type": "project", - "name": avalon.api.Session["AVALON_PROJECT"]}, - projection={"config": True}) - - template = project["config"]["template"]["work"] - dir = pipeline._format_work_template(template, avalon.api.Session) - - return dir + return get_workdir_from_session(avalon.api.Session) def collect_slap_comps(self, directory): items = glob.glob("{}/*.comp".format(directory)) diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index 26f5356336..a8ac6812b5 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -4,13 +4,15 @@ import sys import logging # Pipeline imports -from avalon import api, io, pipeline +from avalon import api, io import avalon.fusion # Config imports import openpype.lib as pype import openpype.hosts.fusion.lib as fusion_lib +from openpype.lib.avalon_context import get_workdir_from_session + log = logging.getLogger("Update Slap Comp") self = sys.modules[__name__] @@ -48,10 +50,7 @@ def _get_work_folder(session): """Convenience function to get the work folder path of the current asset""" # Get new filename, create path based on asset and work template - template_work = self._project["config"]["template"]["work"] - work_path = pipeline._format_work_template(template_work, session) - - return os.path.normpath(work_path) + return get_workdir_from_session(session) def _get_fusion_instance(): From 598882113129e80e1619f7d1c9b256b27847b64e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 11:26:26 +0100 Subject: [PATCH 082/302] reduced functions in fusion --- openpype/hosts/fusion/scripts/fusion_switch_shot.py | 10 ++-------- openpype/hosts/fusion/utility_scripts/switch_ui.py | 5 +---- openpype/lib/avalon_context.py | 9 +++++++-- openpype/scripts/fusion_switch_shot.py | 11 ++--------- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/fusion/scripts/fusion_switch_shot.py b/openpype/hosts/fusion/scripts/fusion_switch_shot.py index ed6a06bb34..ca7efb9136 100644 --- a/openpype/hosts/fusion/scripts/fusion_switch_shot.py +++ b/openpype/hosts/fusion/scripts/fusion_switch_shot.py @@ -45,12 +45,6 @@ def _format_version_folder(folder): return version_folder -def _get_work_folder(session): - """Convenience function to get the work folder path of the current asset""" - # Get new filename, create path based on asset and work template - return get_workdir_from_session(session) - - def _get_fusion_instance(): fusion = getattr(sys.modules["__main__"], "fusion", None) if fusion is None: @@ -69,7 +63,7 @@ def _format_filepath(session): asset = session["AVALON_ASSET"] # Save updated slap comp - work_path = _get_work_folder(session) + work_path = get_workdir_from_session(session) walk_to_dir = os.path.join(work_path, "scenes", "slapcomp") slapcomp_dir = os.path.abspath(walk_to_dir) @@ -109,7 +103,7 @@ def _update_savers(comp, session): None """ - new_work = _get_work_folder(session) + new_work = get_workdir_from_session(session) renders = os.path.join(new_work, "renders") version_folder = _format_version_folder(renders) renders_version = os.path.join(renders, version_folder) diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index 854c2fd415..afb39f7041 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -124,7 +124,7 @@ class App(QtWidgets.QWidget): def _on_open_from_dir(self): - start_dir = self._get_context_directory() + start_dir = get_workdir_from_session() comp_file, _ = QtWidgets.QFileDialog.getOpenFileName( self, "Choose comp", start_dir) @@ -158,9 +158,6 @@ class App(QtWidgets.QWidget): import colorbleed.scripts.fusion_switch_shot as switch_shot switch_shot.switch(asset_name=asset, filepath=file_name, new=True) - def _get_context_directory(self): - return get_workdir_from_session(avalon.api.Session) - def collect_slap_comps(self, directory): items = glob.glob("{}/*.comp".format(directory)) return items diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 9f6a9f9cdc..0bfd3f6de0 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -644,7 +644,7 @@ def get_workdir( ) -def template_data_from_session(session): +def template_data_from_session(session=None): """ Return dictionary with template from session keys. Args: @@ -654,6 +654,7 @@ def template_data_from_session(session): dict: All available data from session. """ from avalon import io + import avalon.api if session is None: session = avalon.api.Session @@ -742,7 +743,11 @@ def compute_session_changes( return changes -def get_workdir_from_session(session, template_key=None): +def get_workdir_from_session(session=None, template_key=None): + import avalon.api + + if session is None: + session = avalon.api.Session project_name = session["AVALON_PROJECT"] host_name = session["AVALON_APP"] anatomy = Anatomy(project_name) diff --git a/openpype/scripts/fusion_switch_shot.py b/openpype/scripts/fusion_switch_shot.py index a8ac6812b5..6db8ff36a8 100644 --- a/openpype/scripts/fusion_switch_shot.py +++ b/openpype/scripts/fusion_switch_shot.py @@ -46,13 +46,6 @@ def _format_version_folder(folder): return version_folder -def _get_work_folder(session): - """Convenience function to get the work folder path of the current asset""" - - # Get new filename, create path based on asset and work template - return get_workdir_from_session(session) - - def _get_fusion_instance(): fusion = getattr(sys.modules["__main__"], "fusion", None) if fusion is None: @@ -71,7 +64,7 @@ def _format_filepath(session): asset = session["AVALON_ASSET"] # Save updated slap comp - work_path = _get_work_folder(session) + work_path = get_workdir_from_session(session) walk_to_dir = os.path.join(work_path, "scenes", "slapcomp") slapcomp_dir = os.path.abspath(walk_to_dir) @@ -102,7 +95,7 @@ def _update_savers(comp, session): None """ - new_work = _get_work_folder(session) + new_work = get_workdir_from_session(session) renders = os.path.join(new_work, "renders") version_folder = _format_version_folder(renders) renders_version = os.path.join(renders, version_folder) From 37cba59fb2172ba14e101c3eacbc028c2026f203 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Mar 2022 11:46:35 +0100 Subject: [PATCH 083/302] nuke: settings adding default states --- .../projects_schema/schemas/schema_nuke_publish.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index f53c53c2f8..4c94801796 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -233,7 +233,8 @@ { "type": "boolean", "key": "reformat_node_add", - "label": "Add Reformat Node" + "label": "Add Reformat Node", + "default": false }, { "type": "collapsible-wrap", @@ -298,6 +299,7 @@ { "type": "number", "key": "number", + "default": 1, "decimal": 4 } ] @@ -322,11 +324,13 @@ { "type": "number", "key": "x", + "default": 1, "decimal": 4 }, { "type": "number", "key": "y", + "default": 1, "decimal": 4 } ] From 882a17b04a164c833de3bdbca06a1af5505eaf78 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 11:50:36 +0100 Subject: [PATCH 084/302] Move update all logic to from window to view --- openpype/tools/sceneinventory/view.py | 37 +++++++++++++++++++++++++ openpype/tools/sceneinventory/window.py | 37 +------------------------ 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 1ed3c9fcb6..ec48b10e47 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -796,3 +796,40 @@ class SceneInventoryView(QtWidgets.QTreeView): ).format(version_str) dialog.setText(msg) dialog.exec_() + + def update_all(self): + """Update all items that are currently 'outdated' in the view""" + # Get the source model through the proxy model + model = self.model().sourceModel() + + # Get all items from outdated groups + outdated_items = [] + for index in iter_model_rows(model, + column=0, + include_root=False): + item = index.data(model.ItemRole) + + if not item.get("isGroupNode"): + continue + + # Only the group nodes contain the "highest_version" data and as + # such we find only the groups and take its children. + if not model.outdated(item): + continue + + # Collect all children which we want to update + children = item.children() + outdated_items.extend(children) + + if not outdated_items: + log.info("Nothing to update.") + return + + # Trigger update to latest + for item in outdated_items: + try: + api.update(item, -1) + except AssertionError: + self._show_version_error_dialog(None, [item]) + log.warning("Update failed", exc_info=True) + self.data_changed.emit() diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index d9d34dbb08..b23c45c0f4 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -21,8 +21,6 @@ from .model import ( ) from .view import SceneInventoryView -from ..utils.lib import iter_model_rows - log = logging.getLogger(__name__) module = sys.modules[__name__] @@ -172,40 +170,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): ) def _on_update_all(self): - """Update all items that are currently 'outdated' in the view""" - - # Get all items from outdated groups - outdated_items = [] - for index in iter_model_rows(self._model, - column=0, - include_root=False): - item = index.data(self._model.ItemRole) - - if not item.get("isGroupNode"): - continue - - # Only the group nodes contain the "highest_version" data and as - # such we find only the groups and take its children. - if not self._model.outdated(item): - continue - - # Collect all children which we want to update - children = item.children() - outdated_items.extend(children) - - if not outdated_items: - log.info("Nothing to update.") - return - - # Trigger update to latest - # Logic copied from SceneInventoryView._build_item_menu_for_selection - for item in outdated_items: - try: - api.update(item, -1) - except AssertionError: - self._show_version_error_dialog(None, [item]) - log.warning("Update failed", exc_info=True) - self._view.data_changed.emit() + self._view.update_all() def show(root=None, debug=False, parent=None, items=None): From b24ea2cecd2e7cb4d909d52310ce70b4f829dac4 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 4 Mar 2022 11:07:27 +0000 Subject: [PATCH 085/302] Fixed parameters for FBX export of the camera --- openpype/hosts/blender/plugins/publish/extract_camera.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/blender/plugins/publish/extract_camera.py b/openpype/hosts/blender/plugins/publish/extract_camera.py index 597dcecd21..b2c7611b58 100644 --- a/openpype/hosts/blender/plugins/publish/extract_camera.py +++ b/openpype/hosts/blender/plugins/publish/extract_camera.py @@ -50,6 +50,10 @@ class ExtractCamera(api.Extractor): filepath=filepath, use_active_collection=False, use_selection=True, + bake_anim_use_nla_strips=False, + bake_anim_use_all_actions=False, + add_leaf_bones=False, + armature_nodetype='ROOT', object_types={'CAMERA'}, bake_anim_simplify_factor=0.0 ) From f65b202ae34d58981df916ac6ca7897ba303b69a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 4 Mar 2022 13:23:09 +0100 Subject: [PATCH 086/302] Added more log messages --- .../plugins/publish/collect_texture.py | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index e441218ca7..ea0b6cdf41 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -104,6 +104,9 @@ class CollectTextures(pyblish.api.ContextPlugin): self.input_naming_groups["workfile"], self.color_space ) + self.log.info("Parsed groups from workfile " + "name '{}': {}".format(repre_file, + formatting_data)) formatting_data.update(explicit_data) fill_pairs = prepare_template_data(formatting_data) @@ -155,19 +158,24 @@ class CollectTextures(pyblish.api.ContextPlugin): } resource_files[workfile_subset].append(item) - formatting_data = self._get_parsed_groups( - repre_file, - self.input_naming_patterns["textures"], - self.input_naming_groups["textures"], - self.color_space - ) - if ext in self.texture_extensions: + formatting_data = self._get_parsed_groups( + repre_file, + self.input_naming_patterns["textures"], + self.input_naming_groups["textures"], + self.color_space + ) + + self.log.info("Parsed groups from texture " + "name '{}': {}".format(repre_file, + formatting_data)) + c_space = self._get_color_space( repre_file, self.color_space ) + # optional value channel = self._get_channel_name( repre_file, self.input_naming_patterns["textures"], @@ -175,6 +183,7 @@ class CollectTextures(pyblish.api.ContextPlugin): self.color_space ) + # optional value shader = self._get_shader_name( repre_file, self.input_naming_patterns["textures"], @@ -260,6 +269,13 @@ class CollectTextures(pyblish.api.ContextPlugin): for asset_build, version, subset, family in asset_builds: if not main_version: main_version = version + + try: + version_int = int(version or main_version or 1) + except ValueError: + self.log.error("Parsed version {} is not " + "an number".format(version)) + new_instance = context.create_instance(subset) new_instance.data.update( { @@ -268,7 +284,7 @@ class CollectTextures(pyblish.api.ContextPlugin): "label": subset, "name": subset, "family": family, - "version": int(version or main_version or 1), + "version": version_int, "asset_build": asset_build # remove in validator } ) @@ -393,12 +409,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse_key(name, input_naming_patterns, - input_naming_groups, color_spaces, 'shader') - if found: - return found + found = None + try: + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, + 'shader') + except ValueError: + self.log.warning("Didn't find shader in {}".format(name)) - self.log.warning("Didn't find shader in {}".format(name)) + return found def _get_channel_name(self, name, input_naming_patterns, input_naming_groups, color_spaces): @@ -407,12 +426,15 @@ class CollectTextures(pyblish.api.ContextPlugin): Unknown format of channel name and color spaces >> cs are known list - 'color_space' used as a placeholder """ - found = self._parse_key(name, input_naming_patterns, - input_naming_groups, color_spaces, 'channel') - if found: - return found + found = None + try: + found = self._parse_key(name, input_naming_patterns, + input_naming_groups, color_spaces, + 'channel') + except ValueError: + self.log.warning("Didn't find channel in {}".format(name)) - self.log.warning("Didn't find channel in {}".format(name)) + return found def _parse_key(self, name, input_naming_patterns, input_naming_groups, color_spaces, key): @@ -437,8 +459,8 @@ class CollectTextures(pyblish.api.ContextPlugin): try: parsed_value = parsed_groups[key] return parsed_value - except IndexError: - msg = ("input_naming_groups must " + + except (IndexError, KeyError): + msg = ("'Textures group positions' must " + "have '{}' key".format(key)) raise ValueError(msg) @@ -468,7 +490,8 @@ class CollectTextures(pyblish.api.ContextPlugin): self.log.warning("No of parsed groups doesn't match " "no of group labels") - return {} + raise ValueError("Name '{}' cannot be parsed by any " + "'{}' patterns".format(name, input_naming_patterns)) def _update_representations(self, upd_representations): """Frames dont have sense for textures, add collected udims instead.""" From 69b0012fd9427c554df753f7c2fdc43fc1c60bea Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Mar 2022 13:46:32 +0100 Subject: [PATCH 087/302] nuke: subset filtering on baking presets --- .../publish/extract_review_data_mov.py | 26 ++++++++++++++++++- .../defaults/project_settings/nuke.json | 3 ++- .../schemas/schema_nuke_publish.json | 6 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py index 5bbc88266a..1071834497 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py +++ b/openpype/hosts/nuke/plugins/publish/extract_review_data_mov.py @@ -1,4 +1,5 @@ import os +import re import pyblish.api import openpype from openpype.hosts.nuke.api import plugin @@ -25,6 +26,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): def process(self, instance): families = instance.data["families"] task_type = instance.context.data["taskType"] + subset = instance.data["subset"] self.log.info("Creating staging dir...") if "representations" not in instance.data: @@ -46,6 +48,7 @@ class ExtractReviewDataMov(openpype.api.Extractor): for o_name, o_data in self.outputs.items(): f_families = o_data["filter"]["families"] f_task_types = o_data["filter"]["task_types"] + f_subsets = o_data["filter"]["sebsets"] # test if family found in context test_families = any([ @@ -69,11 +72,25 @@ class ExtractReviewDataMov(openpype.api.Extractor): bool(not f_task_types) ]) + # test subsets from filter + test_subsets = any([ + # check if any of subset filter inputs + # converted to regex patern is not found in subset + # we keep strict case sensitivity + bool(next(( + s for s in f_subsets + if re.search(re.compile(s), subset) + ), None)), + # but if no subsets were set then make this acuntable too + bool(not f_subsets) + ]) + # we need all filters to be positive for this # preset to be activated test_all = all([ test_families, - test_task_types + test_task_types, + test_subsets ]) # if it is not positive then skip this preset @@ -120,6 +137,13 @@ class ExtractReviewDataMov(openpype.api.Extractor): if generated_repres: # assign to representations instance.data["representations"] += generated_repres + else: + instance.data["families"].remove("review") + self.log.info(( + "Removing `review` from families. " + "Not available baking profile." + )) + self.log.debug(instance.data["families"]) self.log.debug( "_ representations: {}".format( diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index e30296d0ad..6992fb6e3e 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -116,7 +116,8 @@ "baking": { "filter": { "task_types": [], - "families": [] + "families": [], + "sebsets": [] }, "extension": "mov", "viewer_process_override": "", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json index 4c94801796..1636a8d700 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_publish.json @@ -195,6 +195,12 @@ "label": "Families", "type": "list", "object_type": "text" + }, + { + "key": "sebsets", + "label": "Subsets", + "type": "list", + "object_type": "text" } ] }, From a0cd2870d6f3bd0b969f1e62b94a0fe5076eec9b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:18:17 +0100 Subject: [PATCH 088/302] recreate removed classes and functions --- openpype/pipeline/__init__.py | 2 + openpype/pipeline/publish/__init__.py | 12 +++-- openpype/pipeline/publish/lib.py | 56 ++++++++++++++++++++ openpype/pipeline/publish/publish_plugins.py | 26 ++++++++- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e968df4011..79d6ce4d54 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -9,6 +9,7 @@ from .create import ( from .publish import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, OpenPypePyblishPluginMixin ) @@ -23,6 +24,7 @@ __all__ = ( "CreatedInstance", "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin" ) diff --git a/openpype/pipeline/publish/__init__.py b/openpype/pipeline/publish/__init__.py index ca958816fe..c2729a46ce 100644 --- a/openpype/pipeline/publish/__init__.py +++ b/openpype/pipeline/publish/__init__.py @@ -1,20 +1,26 @@ from .publish_plugins import ( PublishValidationError, + PublishXmlValidationError, KnownPublishError, - OpenPypePyblishPluginMixin + OpenPypePyblishPluginMixin, ) from .lib import ( DiscoverResult, - publish_plugins_discover + publish_plugins_discover, + load_help_content_from_plugin, + load_help_content_from_filepath, ) __all__ = ( "PublishValidationError", + "PublishXmlValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin", "DiscoverResult", - "publish_plugins_discover" + "publish_plugins_discover", + "load_help_content_from_plugin", + "load_help_content_from_filepath", ) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index d3e4ec8a02..739b2c8806 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -1,6 +1,8 @@ import os import sys import types +import inspect +import xml.etree.ElementTree import six import pyblish.plugin @@ -28,6 +30,60 @@ class DiscoverResult: self.plugins[item] = value +class HelpContent: + def __init__(self, title, description, detail=None): + self.title = title + self.description = description + self.detail = detail + + +def load_help_content_from_filepath(filepath): + """Load help content from xml file. + Xml file may containt errors and warnings. + """ + errors = {} + warnings = {} + output = { + "errors": errors, + "warnings": warnings + } + if not os.path.exists(filepath): + return output + tree = xml.etree.ElementTree.parse(filepath) + root = tree.getroot() + for child in root: + child_id = child.attrib.get("id") + if child_id is None: + continue + + # Make sure ID is string + child_id = str(child_id) + + title = child.find("title").text + description = child.find("description").text + detail_node = child.find("detail") + detail = None + if detail_node is not None: + detail = detail_node.text + if child.tag == "error": + errors[child_id] = HelpContent(title, description, detail) + elif child.tag == "warning": + warnings[child_id] = HelpContent(title, description, detail) + return output + + +def load_help_content_from_plugin(plugin): + cls = plugin + if not inspect.isclass(plugin): + cls = plugin.__class__ + plugin_filepath = inspect.getfile(cls) + plugin_dir = os.path.dirname(plugin_filepath) + basename = os.path.splitext(os.path.basename(plugin_filepath))[0] + filename = basename + ".xml" + filepath = os.path.join(plugin_dir, "help", filename) + return load_help_content_from_filepath(filepath) + + def publish_plugins_discover(paths=None): """Find and return available pyblish plug-ins diff --git a/openpype/pipeline/publish/publish_plugins.py b/openpype/pipeline/publish/publish_plugins.py index b60b9f43a7..bce64ec709 100644 --- a/openpype/pipeline/publish/publish_plugins.py +++ b/openpype/pipeline/publish/publish_plugins.py @@ -1,3 +1,6 @@ +from .lib import load_help_content_from_plugin + + class PublishValidationError(Exception): """Validation error happened during publishing. @@ -12,13 +15,34 @@ class PublishValidationError(Exception): description(str): Detailed description of an error. It is possible to use Markdown syntax. """ - def __init__(self, message, title=None, description=None): + def __init__(self, message, title=None, description=None, detail=None): self.message = message self.title = title or "< Missing title >" self.description = description or message + self.detail = detail super(PublishValidationError, self).__init__(message) +class PublishXmlValidationError(PublishValidationError): + def __init__( + self, plugin, message, key=None, formatting_data=None + ): + if key is None: + key = "main" + + if not formatting_data: + formatting_data = {} + result = load_help_content_from_plugin(plugin) + content_obj = result["errors"][key] + description = content_obj.description.format(**formatting_data) + detail = content_obj.detail + if detail: + detail = detail.format(**formatting_data) + super(PublishXmlValidationError, self).__init__( + message, content_obj.title, description, detail + ) + + class KnownPublishError(Exception): """Publishing crashed because of known error. From bb01f66ba708f810ec11fcd85fbc2a550f53ed57 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:28:38 +0100 Subject: [PATCH 089/302] hound fixes --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index f6e54f3ae2..f672e78b5f 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -51,7 +51,6 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): key="wrongSOP", formatting_data=data ) - return [node.path()] invalid = self.get_invalid(instance) @@ -71,7 +70,8 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): frame = instance.data.get("frameStart", 0) geometry = output_node.geometryAtFrame(frame) if geometry is None: - # No geometry data on this output_node, maybe the node hasn't cooked? + # No geometry data on this output_node + # - maybe the node hasn't cooked? cls.log.debug( "SOP node has no geometry data. " "Is it cooked? %s" % output_node.path() From 0260e4f269e4b58932df08622bb1d3ac2c90e0a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 14:30:47 +0100 Subject: [PATCH 090/302] add 2 spaces --- .../hosts/houdini/plugins/publish/validate_vdb_output_node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index f672e78b5f..0345f27d72 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -2,6 +2,8 @@ import pyblish.api import openpype.api from openpype.pipeline import PublishXmlValidationError import hou + + class ValidateVDBOutputNode(pyblish.api.InstancePlugin): """Validate that the node connected to the output node is of type VDB. From 10fb4f68b9fae26820b4033cdeed73639eee70bc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 14:59:29 +0100 Subject: [PATCH 091/302] Remove log Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/tools/sceneinventory/window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index b23c45c0f4..7dee32e90b 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -21,7 +21,6 @@ from .model import ( ) from .view import SceneInventoryView -log = logging.getLogger(__name__) module = sys.modules[__name__] module.window = None From e27896f4bbae2d9bcf02abdea39b534e1ebe6ef2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 4 Mar 2022 15:00:17 +0100 Subject: [PATCH 092/302] Remove unused import --- openpype/tools/sceneinventory/window.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 7dee32e90b..095d30cac0 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -1,6 +1,5 @@ import os import sys -import logging from Qt import QtWidgets, QtCore from avalon.vendor import qtawesome From 0cce15d7450769941167e15c99e594d7267a840d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 4 Mar 2022 15:19:17 +0100 Subject: [PATCH 093/302] Removed submodule repos/avalon-unreal-integration --- repos/avalon-unreal-integration | 1 - 1 file changed, 1 deletion(-) delete mode 160000 repos/avalon-unreal-integration diff --git a/repos/avalon-unreal-integration b/repos/avalon-unreal-integration deleted file mode 160000 index 43f6ea9439..0000000000 --- a/repos/avalon-unreal-integration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43f6ea943980b29c02a170942b566ae11f2b7080 From 1e0883cd0f1f63027c3ce4986c7be0bdb3e13534 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Mar 2022 15:26:30 +0100 Subject: [PATCH 094/302] Revert "Merge pull request #2438 from pypeclub/feature/validations_exceptions_houdini" This reverts commit f1693e20d710abeaa2007710a8d59b2d576a3c22. --- .../help/validate_abc_primitive_to_detail.xml | 15 ---- .../help/validate_alembic_face_sets.xml | 22 ------ .../help/validate_alembic_input_node.xml | 21 ----- .../publish/help/validate_frame_token.xml | 31 -------- .../publish/help/validate_vdb_output_node.xml | 48 ------------ .../plugins/publish/valiate_vdb_input_node.py | 47 +++++++++++ .../publish/validate_animation_settings.py | 51 ++++++++++++ .../plugins/publish/validate_frame_token.py | 17 ++-- .../plugins/publish/validate_output_node.py | 77 +++++++++++++++++++ .../publish/validate_sop_output_node.py | 2 +- .../publish/validate_vdb_input_node.py | 47 +++++++++++ .../publish/validate_vdb_output_node.py | 64 ++++----------- .../publish/validate_context_with_error.py | 1 - 13 files changed, 246 insertions(+), 197 deletions(-) delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml delete mode 100644 openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml create mode 100644 openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py create mode 100644 openpype/hosts/houdini/plugins/publish/validate_animation_settings.py create mode 100644 openpype/hosts/houdini/plugins/publish/validate_output_node.py create mode 100644 openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml b/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml deleted file mode 100644 index 0e2aa6c1f4..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_abc_primitive_to_detail.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - -Primitive to Detail -## Invalid Primitive to Detail Attributes - -Primitives with inconsistent primitive to detail attributes were found. - -{message} - - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml deleted file mode 100644 index 7bc149d7c3..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_face_sets.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - -Alembic ROP Face Sets -## Invalid Alembic ROP Face Sets - -When groups are saved as Face Sets with the Alembic these show up -as shadingEngine connections in Maya - however, with animated groups -these connections in Maya won't work as expected, it won't update per -frame. Additionally, it can break shader assignments in some cases -where it requires to first break this connection to allow a shader to -be assigned. - -It is allowed to include Face Sets, so only an issue is logged to -identify that it could introduce issues down the pipeline. - - - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml deleted file mode 100644 index 5be722ccb2..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_alembic_input_node.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - -Alembic input -## Invalid Alembic input - -The node connected to the output is incorrect. -It contains primitive types that are not supported for alembic output. - -Problematic primitive is of type {primitive_type} - - - - - -The connected node cannot be of the following types for Alembic: - - VDB - - Volume - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml b/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml deleted file mode 100644 index 925113362a..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_frame_token.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - -Output frame token -## Output path is missing frame token - -This validator will check the output parameter of the node if -the Valid Frame Range is not set to 'Render Current Frame' - -No frame token found in: **{nodepath}** - -### How to repair? - -You need to add `$F4` or similar frame based token to your path. - -**Example:** - Good: 'my_vbd_cache.$F4.vdb' - Bad: 'my_vbd_cache.vdb' - - - - -If you render out a frame range it is mandatory to have the -frame token - '$F4' or similar - to ensure that each frame gets -written. If this is not the case you will override the same file -every time a frame is written out. - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml b/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml deleted file mode 100644 index 822d1836c1..0000000000 --- a/openpype/hosts/houdini/plugins/publish/help/validate_vdb_output_node.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - -VDB output node -## Invalid VDB output nodes - -Validate that the node connected to the output node is of type VDB. - -Regardless of the amount of VDBs created the output will need to have an -equal amount of VDBs, points, primitives and vertices - -A VDB is an inherited type of Prim, holds the following data: - -- Primitives: 1 -- Points: 1 -- Vertices: 1 -- VDBs: 1 - - - - - - - -No SOP path -## No SOP Path in output node - -SOP Output node in '{node}' does not exist. Ensure a valid SOP output path is set. - - - - - - - -Wrong SOP path -## Wrong SOP Path in output node - -Output node {nodepath} is not a SOP node. -SOP Path must point to a SOP node, -instead found category type: {categoryname} - - - - - - - \ No newline at end of file diff --git a/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py new file mode 100644 index 0000000000..0ae1bc94eb --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/valiate_vdb_input_node.py @@ -0,0 +1,47 @@ +import pyblish.api +import openpype.api + + +class ValidateVDBInputNode(pyblish.api.InstancePlugin): + """Validate that the node connected to the output node is of type VDB. + + Regardless of the amount of VDBs create the output will need to have an + equal amount of VDBs, points, primitives and vertices + + A VDB is an inherited type of Prim, holds the following data: + - Primitives: 1 + - Points: 1 + - Vertices: 1 + - VDBs: 1 + + """ + + order = openpype.api.ValidateContentsOrder + 0.1 + families = ["vdbcache"] + hosts = ["houdini"] + label = "Validate Input Node (VDB)" + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Node connected to the output node is not" "of type VDB!" + ) + + @classmethod + def get_invalid(cls, instance): + + node = instance.data["output_node"] + + prims = node.geometry().prims() + nr_of_prims = len(prims) + + nr_of_points = len(node.geometry().points()) + if nr_of_points != nr_of_prims: + cls.log.error("The number of primitives and points do not match") + return [instance] + + for prim in prims: + if prim.numVertices() != 1: + cls.log.error("Found primitive with more than 1 vertex!") + return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py new file mode 100644 index 0000000000..5eb8f93d03 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/validate_animation_settings.py @@ -0,0 +1,51 @@ +import pyblish.api + +from openpype.hosts.houdini.api import lib + + +class ValidateAnimationSettings(pyblish.api.InstancePlugin): + """Validate if the unexpanded string contains the frame ('$F') token + + This validator will only check the output parameter of the node if + the Valid Frame Range is not set to 'Render Current Frame' + + Rules: + If you render out a frame range it is mandatory to have the + frame token - '$F4' or similar - to ensure that each frame gets + written. If this is not the case you will override the same file + every time a frame is written out. + + Examples: + Good: 'my_vbd_cache.$F4.vdb' + Bad: 'my_vbd_cache.vdb' + + """ + + order = pyblish.api.ValidatorOrder + label = "Validate Frame Settings" + families = ["vdbcache"] + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Output settings do no match for '%s'" % instance + ) + + @classmethod + def get_invalid(cls, instance): + + node = instance[0] + + # Check trange parm, 0 means Render Current Frame + frame_range = node.evalParm("trange") + if frame_range == 0: + return [] + + output_parm = lib.get_output_parameter(node) + unexpanded_str = output_parm.unexpandedString() + + if "$F" not in unexpanded_str: + cls.log.error("No frame token found in '%s'" % node.path()) + return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py index f66238f159..76b5910576 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_frame_token.py +++ b/openpype/hosts/houdini/plugins/publish/validate_frame_token.py @@ -1,12 +1,12 @@ import pyblish.api from openpype.hosts.houdini.api import lib -from openpype.pipeline import PublishXmlValidationError + class ValidateFrameToken(pyblish.api.InstancePlugin): - """Validate if the unexpanded string contains the frame ('$F') token + """Validate if the unexpanded string contains the frame ('$F') token. - This validator will only check the output parameter of the node if + This validator will *only* check the output parameter of the node if the Valid Frame Range is not set to 'Render Current Frame' Rules: @@ -28,14 +28,9 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): def process(self, instance): invalid = self.get_invalid(instance) - data = { - "nodepath": instance - } if invalid: - raise PublishXmlValidationError( - self, - "Output path for '%s' is missing $F4 token" % instance, - formatting_data=data + raise RuntimeError( + "Output settings do no match for '%s'" % instance ) @classmethod @@ -52,5 +47,5 @@ class ValidateFrameToken(pyblish.api.InstancePlugin): unexpanded_str = output_parm.unexpandedString() if "$F" not in unexpanded_str: - # cls.log.info("No frame token found in '%s'" % node.path()) + cls.log.error("No frame token found in '%s'" % node.path()) return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_output_node.py new file mode 100644 index 0000000000..0b60ab5c48 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/validate_output_node.py @@ -0,0 +1,77 @@ +import pyblish.api + + +class ValidateOutputNode(pyblish.api.InstancePlugin): + """Validate the instance SOP Output Node. + + This will ensure: + - The SOP Path is set. + - The SOP Path refers to an existing object. + - The SOP Path node is a SOP node. + - The SOP Path node has at least one input connection (has an input) + - The SOP Path has geometry data. + + """ + + order = pyblish.api.ValidatorOrder + families = ["pointcache", "vdbcache"] + hosts = ["houdini"] + label = "Validate Output Node" + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Output node(s) `%s` are incorrect. " + "See plug-in log for details." % invalid + ) + + @classmethod + def get_invalid(cls, instance): + + import hou + + output_node = instance.data["output_node"] + + if output_node is None: + node = instance[0] + cls.log.error( + "SOP Output node in '%s' does not exist. " + "Ensure a valid SOP output path is set." % node.path() + ) + + return [node.path()] + + # Output node must be a Sop node. + if not isinstance(output_node, hou.SopNode): + cls.log.error( + "Output node %s is not a SOP node. " + "SOP Path must point to a SOP node, " + "instead found category type: %s" + % (output_node.path(), output_node.type().category().name()) + ) + return [output_node.path()] + + # For the sake of completeness also assert the category type + # is Sop to avoid potential edge case scenarios even though + # the isinstance check above should be stricter than this category + assert output_node.type().category().name() == "Sop", ( + "Output node %s is not of category Sop. This is a bug.." + % output_node.path() + ) + + # Check if output node has incoming connections + if not output_node.inputConnections(): + cls.log.error( + "Output node `%s` has no incoming connections" + % output_node.path() + ) + return [output_node.path()] + + # Ensure the output node has at least Geometry data + if not output_node.geometry(): + cls.log.error( + "Output node `%s` has no geometry data." % output_node.path() + ) + return [output_node.path()] diff --git a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py index a37d376919..a5a07b1b1a 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_sop_output_node.py @@ -14,7 +14,7 @@ class ValidateSopOutputNode(pyblish.api.InstancePlugin): """ order = pyblish.api.ValidatorOrder - families = ["pointcache"] + families = ["pointcache", "vdbcache"] hosts = ["houdini"] label = "Validate Output Node" diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py new file mode 100644 index 0000000000..0ae1bc94eb --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_input_node.py @@ -0,0 +1,47 @@ +import pyblish.api +import openpype.api + + +class ValidateVDBInputNode(pyblish.api.InstancePlugin): + """Validate that the node connected to the output node is of type VDB. + + Regardless of the amount of VDBs create the output will need to have an + equal amount of VDBs, points, primitives and vertices + + A VDB is an inherited type of Prim, holds the following data: + - Primitives: 1 + - Points: 1 + - Vertices: 1 + - VDBs: 1 + + """ + + order = openpype.api.ValidateContentsOrder + 0.1 + families = ["vdbcache"] + hosts = ["houdini"] + label = "Validate Input Node (VDB)" + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError( + "Node connected to the output node is not" "of type VDB!" + ) + + @classmethod + def get_invalid(cls, instance): + + node = instance.data["output_node"] + + prims = node.geometry().prims() + nr_of_prims = len(prims) + + nr_of_points = len(node.geometry().points()) + if nr_of_points != nr_of_prims: + cls.log.error("The number of primitives and points do not match") + return [instance] + + for prim in prims: + if prim.numVertices() != 1: + cls.log.error("Found primitive with more than 1 vertex!") + return [instance] diff --git a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py index 0345f27d72..1ba840b71d 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py +++ b/openpype/hosts/houdini/plugins/publish/validate_vdb_output_node.py @@ -1,6 +1,5 @@ import pyblish.api import openpype.api -from openpype.pipeline import PublishXmlValidationError import hou @@ -24,61 +23,32 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): label = "Validate Output Node (VDB)" def process(self, instance): - - data = { - "node": instance - } - - output_node = instance.data["output_node"] - if output_node is None: - raise PublishXmlValidationError( - self, - "SOP Output node in '{node}' does not exist. Ensure a valid " - "SOP output path is set.".format(**data), - key="noSOP", - formatting_data=data - ) - - # Output node must be a Sop node. - if not isinstance(output_node, hou.SopNode): - data = { - "nodepath": output_node.path(), - "categoryname": output_node.type().category().name() - } - raise PublishXmlValidationError( - self, - "Output node {nodepath} is not a SOP node. SOP Path must" - "point to a SOP node, instead found category" - "type: {categoryname}".format(**data), - key="wrongSOP", - formatting_data=data - ) - invalid = self.get_invalid(instance) - if invalid: - raise PublishXmlValidationError( - self, - "Output node(s) `{}` are incorrect. See plug-in" - "log for details.".format(invalid), - formatting_data=data + raise RuntimeError( + "Node connected to the output node is not" " of type VDB!" ) @classmethod def get_invalid(cls, instance): - output_node = instance.data["output_node"] + node = instance.data["output_node"] + if node is None: + cls.log.error( + "SOP path is not correctly set on " + "ROP node '%s'." % instance[0].path() + ) + return [instance] frame = instance.data.get("frameStart", 0) - geometry = output_node.geometryAtFrame(frame) + geometry = node.geometryAtFrame(frame) if geometry is None: - # No geometry data on this output_node - # - maybe the node hasn't cooked? - cls.log.debug( + # No geometry data on this node, maybe the node hasn't cooked? + cls.log.error( "SOP node has no geometry data. " - "Is it cooked? %s" % output_node.path() + "Is it cooked? %s" % node.path() ) - return [output_node] + return [node] prims = geometry.prims() nr_of_prims = len(prims) @@ -87,17 +57,17 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin): invalid_prim = False for prim in prims: if not isinstance(prim, hou.VDB): - cls.log.debug("Found non-VDB primitive: %s" % prim) + cls.log.error("Found non-VDB primitive: %s" % prim) invalid_prim = True if invalid_prim: return [instance] nr_of_points = len(geometry.points()) if nr_of_points != nr_of_prims: - cls.log.debug("The number of primitives and points do not match") + cls.log.error("The number of primitives and points do not match") return [instance] for prim in prims: if prim.numVertices() != 1: - cls.log.debug("Found primitive with more than 1 vertex!") + cls.log.error("Found primitive with more than 1 vertex!") return [instance] diff --git a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py index 20fb47513e..46e996a569 100644 --- a/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py +++ b/openpype/hosts/testhost/plugins/publish/validate_context_with_error.py @@ -2,7 +2,6 @@ import pyblish.api from openpype.pipeline import PublishValidationError - class ValidateInstanceAssetRepair(pyblish.api.Action): """Repair the instance asset.""" From 4dd520bea922ea540da1aa5ea42005665ae775ee Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 4 Mar 2022 15:28:19 +0100 Subject: [PATCH 095/302] remove extra validator --- .../plugins/publish/validate_output_node.py | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 openpype/hosts/houdini/plugins/publish/validate_output_node.py diff --git a/openpype/hosts/houdini/plugins/publish/validate_output_node.py b/openpype/hosts/houdini/plugins/publish/validate_output_node.py deleted file mode 100644 index 0b60ab5c48..0000000000 --- a/openpype/hosts/houdini/plugins/publish/validate_output_node.py +++ /dev/null @@ -1,77 +0,0 @@ -import pyblish.api - - -class ValidateOutputNode(pyblish.api.InstancePlugin): - """Validate the instance SOP Output Node. - - This will ensure: - - The SOP Path is set. - - The SOP Path refers to an existing object. - - The SOP Path node is a SOP node. - - The SOP Path node has at least one input connection (has an input) - - The SOP Path has geometry data. - - """ - - order = pyblish.api.ValidatorOrder - families = ["pointcache", "vdbcache"] - hosts = ["houdini"] - label = "Validate Output Node" - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError( - "Output node(s) `%s` are incorrect. " - "See plug-in log for details." % invalid - ) - - @classmethod - def get_invalid(cls, instance): - - import hou - - output_node = instance.data["output_node"] - - if output_node is None: - node = instance[0] - cls.log.error( - "SOP Output node in '%s' does not exist. " - "Ensure a valid SOP output path is set." % node.path() - ) - - return [node.path()] - - # Output node must be a Sop node. - if not isinstance(output_node, hou.SopNode): - cls.log.error( - "Output node %s is not a SOP node. " - "SOP Path must point to a SOP node, " - "instead found category type: %s" - % (output_node.path(), output_node.type().category().name()) - ) - return [output_node.path()] - - # For the sake of completeness also assert the category type - # is Sop to avoid potential edge case scenarios even though - # the isinstance check above should be stricter than this category - assert output_node.type().category().name() == "Sop", ( - "Output node %s is not of category Sop. This is a bug.." - % output_node.path() - ) - - # Check if output node has incoming connections - if not output_node.inputConnections(): - cls.log.error( - "Output node `%s` has no incoming connections" - % output_node.path() - ) - return [output_node.path()] - - # Ensure the output node has at least Geometry data - if not output_node.geometry(): - cls.log.error( - "Output node `%s` has no geometry data." % output_node.path() - ) - return [output_node.path()] From ee53add80fb8af7147508c1864f7fab7aae232db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 17:13:03 +0100 Subject: [PATCH 096/302] don't set version if is not available --- openpype/pipeline/create/context.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index e11d32091f..706279fd72 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -399,15 +399,6 @@ class CreatedInstance: self._data["active"] = data.get("active", True) self._data["creator_identifier"] = creator.identifier - # QUESTION handle version of instance here or in creator? - version = None - if not new: - version = data.get("version") - - if version is None: - version = 1 - self._data["version"] = version - # Pop from source data all keys that are defined in `_data` before # this moment and through their values away # - they should be the same and if are not then should not change From 93956497fcdac75e83162700b705dcfcc30e9854 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 17:55:43 +0100 Subject: [PATCH 097/302] removed deprecated and unused assetcreator tool --- openpype/tools/assetcreator/__init__.py | 10 - openpype/tools/assetcreator/__main__.py | 5 - openpype/tools/assetcreator/app.py | 654 ------------------------ openpype/tools/assetcreator/model.py | 310 ----------- openpype/tools/assetcreator/widget.py | 448 ---------------- 5 files changed, 1427 deletions(-) delete mode 100644 openpype/tools/assetcreator/__init__.py delete mode 100644 openpype/tools/assetcreator/__main__.py delete mode 100644 openpype/tools/assetcreator/app.py delete mode 100644 openpype/tools/assetcreator/model.py delete mode 100644 openpype/tools/assetcreator/widget.py diff --git a/openpype/tools/assetcreator/__init__.py b/openpype/tools/assetcreator/__init__.py deleted file mode 100644 index 3b88ebe984..0000000000 --- a/openpype/tools/assetcreator/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ - -from .app import ( - show, - cli -) - -__all__ = [ - "show", - "cli", -] diff --git a/openpype/tools/assetcreator/__main__.py b/openpype/tools/assetcreator/__main__.py deleted file mode 100644 index d77bc585c5..0000000000 --- a/openpype/tools/assetcreator/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from . import cli - -if __name__ == '__main__': - import sys - sys.exit(cli(sys.argv[1:])) diff --git a/openpype/tools/assetcreator/app.py b/openpype/tools/assetcreator/app.py deleted file mode 100644 index 60ef31e859..0000000000 --- a/openpype/tools/assetcreator/app.py +++ /dev/null @@ -1,654 +0,0 @@ -import os -import sys -from subprocess import Popen - -import ftrack_api -from Qt import QtWidgets, QtCore -from openpype import style -from openpype.api import get_current_project_settings -from openpype.lib.avalon_context import update_current_task -from openpype.tools.utils.lib import qt_app_context -from avalon import io, api, schema -from . import widget, model - -module = sys.modules[__name__] -module.window = None - - -class Window(QtWidgets.QDialog): - """Asset creator interface - - """ - - def __init__(self, parent=None, context=None): - super(Window, self).__init__(parent) - self.context = context - project_name = io.active_project() - self.setWindowTitle("Asset creator ({0})".format(project_name)) - self.setFocusPolicy(QtCore.Qt.StrongFocus) - self.setAttribute(QtCore.Qt.WA_DeleteOnClose) - - # Validators - self.valid_parent = False - - self.session = None - - # assets widget - assets_widget = QtWidgets.QWidget() - assets_widget.setContentsMargins(0, 0, 0, 0) - assets_layout = QtWidgets.QVBoxLayout(assets_widget) - assets = widget.AssetWidget() - assets.view.setSelectionMode(assets.view.ExtendedSelection) - assets_layout.addWidget(assets) - - # Outlink - label_outlink = QtWidgets.QLabel("Outlink:") - input_outlink = QtWidgets.QLineEdit() - input_outlink.setReadOnly(True) - input_outlink.setStyleSheet("background-color: #333333;") - checkbox_outlink = QtWidgets.QCheckBox("Use outlink") - # Parent - label_parent = QtWidgets.QLabel("*Parent:") - input_parent = QtWidgets.QLineEdit() - input_parent.setReadOnly(True) - input_parent.setStyleSheet("background-color: #333333;") - - # Name - label_name = QtWidgets.QLabel("*Name:") - input_name = QtWidgets.QLineEdit() - input_name.setPlaceholderText("") - - # Asset Build - label_assetbuild = QtWidgets.QLabel("Asset Build:") - combo_assetbuilt = QtWidgets.QComboBox() - - # Task template - label_task_template = QtWidgets.QLabel("Task template:") - combo_task_template = QtWidgets.QComboBox() - - # Info widget - info_widget = QtWidgets.QWidget() - info_widget.setContentsMargins(10, 10, 10, 10) - info_layout = QtWidgets.QVBoxLayout(info_widget) - - # Inputs widget - inputs_widget = QtWidgets.QWidget() - inputs_widget.setContentsMargins(0, 0, 0, 0) - - inputs_layout = QtWidgets.QFormLayout(inputs_widget) - inputs_layout.addRow(label_outlink, input_outlink) - inputs_layout.addRow(None, checkbox_outlink) - inputs_layout.addRow(label_parent, input_parent) - inputs_layout.addRow(label_name, input_name) - inputs_layout.addRow(label_assetbuild, combo_assetbuilt) - inputs_layout.addRow(label_task_template, combo_task_template) - - # Add button - btns_widget = QtWidgets.QWidget() - btns_widget.setContentsMargins(0, 0, 0, 0) - btn_layout = QtWidgets.QHBoxLayout(btns_widget) - btn_create_asset = QtWidgets.QPushButton("Create asset") - btn_create_asset.setToolTip( - "Creates all necessary components for asset" - ) - checkbox_app = None - if self.context is not None: - checkbox_app = QtWidgets.QCheckBox("Open {}".format( - self.context.capitalize()) - ) - btn_layout.addWidget(checkbox_app) - btn_layout.addWidget(btn_create_asset) - - task_view = QtWidgets.QTreeView() - task_view.setIndentation(0) - task_model = model.TasksModel() - task_view.setModel(task_model) - - info_layout.addWidget(inputs_widget) - info_layout.addWidget(task_view) - info_layout.addWidget(btns_widget) - - # Body - body = QtWidgets.QSplitter() - body.setContentsMargins(0, 0, 0, 0) - body.setSizePolicy(QtWidgets.QSizePolicy.Expanding, - QtWidgets.QSizePolicy.Expanding) - body.setOrientation(QtCore.Qt.Horizontal) - body.addWidget(assets_widget) - body.addWidget(info_widget) - body.setStretchFactor(0, 100) - body.setStretchFactor(1, 150) - - # statusbar - message = QtWidgets.QLabel() - message.setFixedHeight(20) - - statusbar = QtWidgets.QWidget() - layout = QtWidgets.QHBoxLayout(statusbar) - layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(message) - - layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(body) - layout.addWidget(statusbar) - - self.data = { - "label": { - "message": message, - }, - "view": { - "tasks": task_view - }, - "model": { - "assets": assets, - "tasks": task_model - }, - "inputs": { - "outlink": input_outlink, - "outlink_cb": checkbox_outlink, - "parent": input_parent, - "name": input_name, - "assetbuild": combo_assetbuilt, - "tasktemplate": combo_task_template, - "open_app": checkbox_app - }, - "buttons": { - "create_asset": btn_create_asset - } - } - - # signals - btn_create_asset.clicked.connect(self.create_asset) - assets.selection_changed.connect(self.on_asset_changed) - input_name.textChanged.connect(self.on_asset_name_change) - checkbox_outlink.toggled.connect(self.on_outlink_checkbox_change) - combo_task_template.currentTextChanged.connect( - self.on_task_template_changed - ) - if self.context is not None: - checkbox_app.toggled.connect(self.on_app_checkbox_change) - # on start - self.on_start() - - self.resize(600, 500) - - self.echo("Connected to project: {0}".format(project_name)) - - def open_app(self): - if self.context == 'maya': - Popen("maya") - else: - message = QtWidgets.QMessageBox(self) - message.setWindowTitle("App is not set") - message.setIcon(QtWidgets.QMessageBox.Critical) - message.show() - - def on_start(self): - project_name = io.Session['AVALON_PROJECT'] - project_query = 'Project where full_name is "{}"'.format(project_name) - if self.session is None: - session = ftrack_api.Session() - self.session = session - else: - session = self.session - ft_project = session.query(project_query).one() - schema_name = ft_project['project_schema']['name'] - # Load config - schemas_items = get_current_project_settings().get('ftrack', {}).get( - 'project_schemas', {} - ) - # Get info if it is silo project - self.silos = io.distinct("silo") - if self.silos and None in self.silos: - self.silos = None - - key = "default" - if schema_name in schemas_items: - key = schema_name - - self.config_data = schemas_items[key] - - # set outlink - input_outlink = self.data['inputs']['outlink'] - checkbox_outlink = self.data['inputs']['outlink_cb'] - outlink_text = io.Session.get('AVALON_ASSET', '') - checkbox_outlink.setChecked(True) - if outlink_text == '': - outlink_text = '< No context >' - checkbox_outlink.setChecked(False) - checkbox_outlink.hide() - input_outlink.setText(outlink_text) - - # load asset build types - self.load_assetbuild_types() - - # Load task templates - self.load_task_templates() - self.data["model"]["assets"].refresh() - self.on_asset_changed() - - def create_asset(self): - name_input = self.data['inputs']['name'] - name = name_input.text() - test_name = name.replace(' ', '') - error_message = None - message = QtWidgets.QMessageBox(self) - message.setWindowTitle("Some errors have occurred") - message.setIcon(QtWidgets.QMessageBox.Critical) - # TODO: show error messages on any error - if self.valid_parent is not True and test_name == '': - error_message = "Name is not set and Parent is not selected" - elif self.valid_parent is not True: - error_message = "Parent is not selected" - elif test_name == '': - error_message = "Name is not set" - - if error_message is not None: - message.setText(error_message) - message.show() - return - - test_name_exists = io.find({ - 'type': 'asset', - 'name': name - }) - existing_assets = [x for x in test_name_exists] - if len(existing_assets) > 0: - message.setText("Entered Asset name is occupied") - message.show() - return - - checkbox_app = self.data['inputs']['open_app'] - if checkbox_app is not None and checkbox_app.isChecked() is True: - task_view = self.data["view"]["tasks"] - task_model = self.data["model"]["tasks"] - try: - index = task_view.selectedIndexes()[0] - task_name = task_model.itemData(index)[0] - except Exception: - message.setText("Please select task") - message.show() - return - - # Get ftrack session - if self.session is None: - session = ftrack_api.Session() - self.session = session - else: - session = self.session - - # Get Ftrack project entity - project_name = io.Session['AVALON_PROJECT'] - project_query = 'Project where full_name is "{}"'.format(project_name) - try: - ft_project = session.query(project_query).one() - except Exception: - message.setText("Ftrack project was not found") - message.show() - return - - # Get Ftrack entity of parent - ft_parent = None - assets_model = self.data["model"]["assets"] - selected = assets_model.get_selected_assets() - parent = io.find_one({"_id": selected[0], "type": "asset"}) - asset_id = parent.get('data', {}).get('ftrackId', None) - asset_entity_type = parent.get('data', {}).get('entityType', None) - asset_query = '{} where id is "{}"' - if asset_id is not None and asset_entity_type is not None: - try: - ft_parent = session.query(asset_query.format( - asset_entity_type, asset_id) - ).one() - except Exception: - ft_parent = None - - if ft_parent is None: - ft_parent = self.get_ftrack_asset(parent, ft_project) - - if ft_parent is None: - message.setText("Parent's Ftrack entity was not found") - message.show() - return - - asset_build_combo = self.data['inputs']['assetbuild'] - asset_type_name = asset_build_combo.currentText() - asset_type_query = 'Type where name is "{}"'.format(asset_type_name) - try: - asset_type = session.query(asset_type_query).one() - except Exception: - message.setText("Selected Asset Build type does not exists") - message.show() - return - - for children in ft_parent['children']: - if children['name'] == name: - message.setText("Entered Asset name is occupied") - message.show() - return - - task_template_combo = self.data['inputs']['tasktemplate'] - task_template = task_template_combo.currentText() - tasks = [] - for template in self.config_data['task_templates']: - if template['name'] == task_template: - tasks = template['task_types'] - break - - available_task_types = [] - task_types = ft_project['project_schema']['_task_type_schema'] - for task_type in task_types['types']: - available_task_types.append(task_type['name']) - - not_possible_tasks = [] - for task in tasks: - if task not in available_task_types: - not_possible_tasks.append(task) - - if len(not_possible_tasks) != 0: - message.setText(( - "These Task types weren't found" - " in Ftrack project schema:\n{}").format( - ', '.join(not_possible_tasks)) - ) - message.show() - return - - # Create asset build - asset_build_data = { - 'name': name, - 'project_id': ft_project['id'], - 'parent_id': ft_parent['id'], - 'type': asset_type - } - - new_entity = session.create('AssetBuild', asset_build_data) - - task_data = { - 'project_id': ft_project['id'], - 'parent_id': new_entity['id'] - } - - for task in tasks: - type = session.query('Type where name is "{}"'.format(task)).one() - - task_data['type_id'] = type['id'] - task_data['name'] = task - session.create('Task', task_data) - - av_project = io.find_one({'type': 'project'}) - - hiearchy_items = [] - hiearchy_items.extend(self.get_avalon_parent(parent)) - hiearchy_items.append(parent['name']) - - hierarchy = os.path.sep.join(hiearchy_items) - new_asset_data = { - 'ftrackId': new_entity['id'], - 'entityType': new_entity.entity_type, - 'visualParent': parent['_id'], - 'tasks': tasks, - 'parents': hiearchy_items, - 'hierarchy': hierarchy - } - new_asset_info = { - 'parent': av_project['_id'], - 'name': name, - 'schema': "openpype:asset-3.0", - 'type': 'asset', - 'data': new_asset_data - } - - # Backwards compatibility (add silo from parent if is silo project) - if self.silos: - new_asset_info["silo"] = parent["silo"] - - try: - schema.validate(new_asset_info) - except Exception: - message.setText(( - 'Asset information are not valid' - ' to create asset in avalon database' - )) - message.show() - session.rollback() - return - io.insert_one(new_asset_info) - session.commit() - - outlink_cb = self.data['inputs']['outlink_cb'] - if outlink_cb.isChecked() is True: - outlink_input = self.data['inputs']['outlink'] - outlink_name = outlink_input.text() - outlink_asset = io.find_one({ - 'type': 'asset', - 'name': outlink_name - }) - outlink_ft_id = outlink_asset.get('data', {}).get('ftrackId', None) - outlink_entity_type = outlink_asset.get( - 'data', {} - ).get('entityType', None) - if outlink_ft_id is not None and outlink_entity_type is not None: - try: - outlink_entity = session.query(asset_query.format()).one() - except Exception: - outlink_entity = None - - if outlink_entity is None: - outlink_entity = self.get_ftrack_asset( - outlink_asset, ft_project - ) - - if outlink_entity is None: - message.setText("Outlink's Ftrack entity was not found") - message.show() - return - - link_data = { - 'from_id': new_entity['id'], - 'to_id': outlink_entity['id'] - } - session.create('TypedContextLink', link_data) - session.commit() - - if checkbox_app is not None and checkbox_app.isChecked() is True: - origin_asset = api.Session.get('AVALON_ASSET', None) - origin_task = api.Session.get('AVALON_TASK', None) - asset_name = name - task_view = self.data["view"]["tasks"] - task_model = self.data["model"]["tasks"] - try: - index = task_view.selectedIndexes()[0] - except Exception: - message.setText("No task is selected. App won't be launched") - message.show() - return - task_name = task_model.itemData(index)[0] - try: - update_current_task(task=task_name, asset=asset_name) - self.open_app() - - finally: - if origin_task is not None and origin_asset is not None: - update_current_task( - task=origin_task, asset=origin_asset - ) - - message.setWindowTitle("Asset Created") - message.setText("Asset Created successfully") - message.setIcon(QtWidgets.QMessageBox.Information) - message.show() - - def get_ftrack_asset(self, asset, ft_project): - parenthood = [] - parenthood.extend(self.get_avalon_parent(asset)) - parenthood.append(asset['name']) - parenthood = list(reversed(parenthood)) - output_entity = None - ft_entity = ft_project - index = len(parenthood) - 1 - while True: - name = parenthood[index] - found = False - for children in ft_entity['children']: - if children['name'] == name: - ft_entity = children - found = True - break - if found is False: - return None - if index == 0: - output_entity = ft_entity - break - index -= 1 - - return output_entity - - def get_avalon_parent(self, entity): - parent_id = entity['data']['visualParent'] - parents = [] - if parent_id is not None: - parent = io.find_one({'_id': parent_id}) - parents.extend(self.get_avalon_parent(parent)) - parents.append(parent['name']) - return parents - - def echo(self, message): - widget = self.data["label"]["message"] - widget.setText(str(message)) - - QtCore.QTimer.singleShot(5000, lambda: widget.setText("")) - - print(message) - - def load_task_templates(self): - templates = self.config_data.get('task_templates', []) - all_names = [] - for template in templates: - all_names.append(template['name']) - - tt_combobox = self.data['inputs']['tasktemplate'] - tt_combobox.clear() - tt_combobox.addItems(all_names) - - def load_assetbuild_types(self): - types = [] - schemas = self.config_data.get('schemas', []) - for _schema in schemas: - if _schema['object_type'] == 'Asset Build': - types = _schema['task_types'] - break - ab_combobox = self.data['inputs']['assetbuild'] - ab_combobox.clear() - ab_combobox.addItems(types) - - def on_app_checkbox_change(self): - task_model = self.data['model']['tasks'] - app_checkbox = self.data['inputs']['open_app'] - if app_checkbox.isChecked() is True: - task_model.selectable = True - else: - task_model.selectable = False - - def on_outlink_checkbox_change(self): - checkbox_outlink = self.data['inputs']['outlink_cb'] - outlink_input = self.data['inputs']['outlink'] - if checkbox_outlink.isChecked() is True: - outlink_text = io.Session['AVALON_ASSET'] - else: - outlink_text = '< Outlinks won\'t be set >' - - outlink_input.setText(outlink_text) - - def on_task_template_changed(self): - combobox = self.data['inputs']['tasktemplate'] - task_model = self.data['model']['tasks'] - name = combobox.currentText() - tasks = [] - for template in self.config_data['task_templates']: - if template['name'] == name: - tasks = template['task_types'] - break - task_model.set_tasks(tasks) - - def on_asset_changed(self): - """Callback on asset selection changed - - This updates the task view. - - """ - assets_model = self.data["model"]["assets"] - parent_input = self.data['inputs']['parent'] - selected = assets_model.get_selected_assets() - - self.valid_parent = False - if len(selected) > 1: - parent_input.setText('< Please select only one asset! >') - elif len(selected) == 1: - if isinstance(selected[0], io.ObjectId): - self.valid_parent = True - asset = io.find_one({"_id": selected[0], "type": "asset"}) - parent_input.setText(asset['name']) - else: - parent_input.setText('< Selected invalid parent(silo) >') - else: - parent_input.setText('< Nothing is selected >') - - self.creatability_check() - - def on_asset_name_change(self): - self.creatability_check() - - def creatability_check(self): - name_input = self.data['inputs']['name'] - name = str(name_input.text()).strip() - creatable = False - if name and self.valid_parent: - creatable = True - - self.data["buttons"]["create_asset"].setEnabled(creatable) - - - -def show(parent=None, debug=False, context=None): - """Display Loader GUI - - Arguments: - debug (bool, optional): Run loader in debug-mode, - defaults to False - - """ - - try: - module.window.close() - del module.window - except (RuntimeError, AttributeError): - pass - - if debug is True: - io.install() - - with qt_app_context(): - window = Window(parent, context) - window.setStyleSheet(style.load_stylesheet()) - window.show() - - module.window = window - - -def cli(args): - import argparse - parser = argparse.ArgumentParser() - parser.add_argument("project") - parser.add_argument("asset") - - args = parser.parse_args(args) - project = args.project - asset = args.asset - io.install() - - api.Session["AVALON_PROJECT"] = project - if asset != '': - api.Session["AVALON_ASSET"] = asset - - show() diff --git a/openpype/tools/assetcreator/model.py b/openpype/tools/assetcreator/model.py deleted file mode 100644 index f84541ca2a..0000000000 --- a/openpype/tools/assetcreator/model.py +++ /dev/null @@ -1,310 +0,0 @@ -import re -import logging - -from Qt import QtCore, QtWidgets -from avalon.vendor import qtawesome -from avalon import io -from avalon import style - -log = logging.getLogger(__name__) - - -class Item(dict): - """An item that can be represented in a tree view using `TreeModel`. - - The item can store data just like a regular dictionary. - - >>> data = {"name": "John", "score": 10} - >>> item = Item(data) - >>> assert item["name"] == "John" - - """ - - def __init__(self, data=None): - super(Item, self).__init__() - - self._children = list() - self._parent = None - - if data is not None: - assert isinstance(data, dict) - self.update(data) - - def childCount(self): - return len(self._children) - - def child(self, row): - - if row >= len(self._children): - log.warning("Invalid row as child: {0}".format(row)) - return - - return self._children[row] - - def children(self): - return self._children - - def parent(self): - return self._parent - - def row(self): - """ - Returns: - int: Index of this item under parent""" - if self._parent is not None: - siblings = self.parent().children() - return siblings.index(self) - - def add_child(self, child): - """Add a child to this item""" - child._parent = self - self._children.append(child) - - -class TreeModel(QtCore.QAbstractItemModel): - - Columns = list() - ItemRole = QtCore.Qt.UserRole + 1 - - def __init__(self, parent=None): - super(TreeModel, self).__init__(parent) - self._root_item = Item() - - def rowCount(self, parent): - if parent.isValid(): - item = parent.internalPointer() - else: - item = self._root_item - - return item.childCount() - - def columnCount(self, parent): - return len(self.Columns) - - def data(self, index, role): - - if not index.isValid(): - return None - - if role == QtCore.Qt.DisplayRole or role == QtCore.Qt.EditRole: - - item = index.internalPointer() - column = index.column() - - key = self.Columns[column] - return item.get(key, None) - - if role == self.ItemRole: - return index.internalPointer() - - def setData(self, index, value, role=QtCore.Qt.EditRole): - """Change the data on the items. - - Returns: - bool: Whether the edit was successful - """ - - if index.isValid(): - if role == QtCore.Qt.EditRole: - - item = index.internalPointer() - column = index.column() - key = self.Columns[column] - item[key] = value - - # passing `list()` for PyQt5 (see PYSIDE-462) - self.dataChanged.emit(index, index, list()) - - # must return true if successful - return True - - return False - - def setColumns(self, keys): - assert isinstance(keys, (list, tuple)) - self.Columns = keys - - def headerData(self, section, orientation, role): - - if role == QtCore.Qt.DisplayRole: - if section < len(self.Columns): - return self.Columns[section] - - super(TreeModel, self).headerData(section, orientation, role) - - def flags(self, index): - flags = QtCore.Qt.ItemIsEnabled - - item = index.internalPointer() - if item.get("enabled", True): - flags |= QtCore.Qt.ItemIsSelectable - - return flags - - def parent(self, index): - - item = index.internalPointer() - parent_item = item.parent() - - # If it has no parents we return invalid - if parent_item == self._root_item or not parent_item: - return QtCore.QModelIndex() - - return self.createIndex(parent_item.row(), 0, parent_item) - - def index(self, row, column, parent): - """Return index for row/column under parent""" - - if not parent.isValid(): - parent_item = self._root_item - else: - parent_item = parent.internalPointer() - - child_item = parent_item.child(row) - if child_item: - return self.createIndex(row, column, child_item) - else: - return QtCore.QModelIndex() - - def add_child(self, item, parent=None): - if parent is None: - parent = self._root_item - - parent.add_child(item) - - def column_name(self, column): - """Return column key by index""" - - if column < len(self.Columns): - return self.Columns[column] - - def clear(self): - self.beginResetModel() - self._root_item = Item() - self.endResetModel() - - -class TasksModel(TreeModel): - """A model listing the tasks combined for a list of assets""" - - Columns = ["Tasks"] - - def __init__(self): - super(TasksModel, self).__init__() - self._num_assets = 0 - self._icons = { - "__default__": qtawesome.icon("fa.male", - color=style.colors.default), - "__no_task__": qtawesome.icon("fa.exclamation-circle", - color=style.colors.mid) - } - - self._get_task_icons() - - def _get_task_icons(self): - # Get the project configured icons from database - project = io.find_one({"type": "project"}) - tasks = project["config"].get("tasks", []) - for task in tasks: - icon_name = task.get("icon", None) - if icon_name: - icon = qtawesome.icon("fa.{}".format(icon_name), - color=style.colors.default) - self._icons[task["name"]] = icon - - def set_tasks(self, tasks): - """Set assets to track by their database id - - Arguments: - asset_ids (list): List of asset ids. - - """ - - self.clear() - - # let cleared task view if no tasks are available - if len(tasks) == 0: - return - - self.beginResetModel() - - icon = self._icons["__default__"] - for task in tasks: - item = Item({ - "Tasks": task, - "icon": icon - }) - - self.add_child(item) - - self.endResetModel() - - def flags(self, index): - return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable - - def headerData(self, section, orientation, role): - - # Override header for count column to show amount of assets - # it is listing the tasks for - if role == QtCore.Qt.DisplayRole: - if orientation == QtCore.Qt.Horizontal: - if section == 1: # count column - return "count ({0})".format(self._num_assets) - - return super(TasksModel, self).headerData(section, orientation, role) - - def data(self, index, role): - - if not index.isValid(): - return - - # Add icon to the first column - if role == QtCore.Qt.DecorationRole: - if index.column() == 0: - return index.internalPointer()["icon"] - - return super(TasksModel, self).data(index, role) - - -class DeselectableTreeView(QtWidgets.QTreeView): - """A tree view that deselects on clicking on an empty area in the view""" - - def mousePressEvent(self, event): - - index = self.indexAt(event.pos()) - if not index.isValid(): - # clear the selection - self.clearSelection() - # clear the current index - self.setCurrentIndex(QtCore.QModelIndex()) - - QtWidgets.QTreeView.mousePressEvent(self, event) - - -class RecursiveSortFilterProxyModel(QtCore.QSortFilterProxyModel): - """Filters to the regex if any of the children matches allow parent""" - def filterAcceptsRow(self, row, parent): - - regex = self.filterRegExp() - if not regex.isEmpty(): - pattern = regex.pattern() - model = self.sourceModel() - source_index = model.index(row, self.filterKeyColumn(), parent) - if source_index.isValid(): - - # Check current index itself - key = model.data(source_index, self.filterRole()) - if re.search(pattern, key, re.IGNORECASE): - return True - - # Check children - rows = model.rowCount(source_index) - for i in range(rows): - if self.filterAcceptsRow(i, source_index): - return True - - # Otherwise filter it - return False - - return super(RecursiveSortFilterProxyModel, - self).filterAcceptsRow(row, parent) diff --git a/openpype/tools/assetcreator/widget.py b/openpype/tools/assetcreator/widget.py deleted file mode 100644 index fd0f438e68..0000000000 --- a/openpype/tools/assetcreator/widget.py +++ /dev/null @@ -1,448 +0,0 @@ -import logging -import contextlib -import collections - -from avalon.vendor import qtawesome -from Qt import QtWidgets, QtCore, QtGui -from avalon import style, io - -from .model import ( - TreeModel, - Item, - RecursiveSortFilterProxyModel, - DeselectableTreeView -) - -log = logging.getLogger(__name__) - - -def _iter_model_rows(model, - column, - include_root=False): - """Iterate over all row indices in a model""" - indices = [QtCore.QModelIndex()] # start iteration at root - - for index in indices: - - # Add children to the iterations - child_rows = model.rowCount(index) - for child_row in range(child_rows): - child_index = model.index(child_row, column, index) - indices.append(child_index) - - if not include_root and not index.isValid(): - continue - - yield index - - -@contextlib.contextmanager -def preserve_expanded_rows(tree_view, - column=0, - role=QtCore.Qt.DisplayRole): - """Preserves expanded row in QTreeView by column's data role. - - This function is created to maintain the expand vs collapse status of - the model items. When refresh is triggered the items which are expanded - will stay expanded and vice versa. - - Arguments: - tree_view (QWidgets.QTreeView): the tree view which is - nested in the application - column (int): the column to retrieve the data from - role (int): the role which dictates what will be returned - - Returns: - None - - """ - - model = tree_view.model() - - expanded = set() - - for index in _iter_model_rows(model, - column=column, - include_root=False): - if tree_view.isExpanded(index): - value = index.data(role) - expanded.add(value) - - try: - yield - finally: - if not expanded: - return - - for index in _iter_model_rows(model, - column=column, - include_root=False): - value = index.data(role) - state = value in expanded - if state: - tree_view.expand(index) - else: - tree_view.collapse(index) - - -@contextlib.contextmanager -def preserve_selection(tree_view, - column=0, - role=QtCore.Qt.DisplayRole, - current_index=True): - """Preserves row selection in QTreeView by column's data role. - - This function is created to maintain the selection status of - the model items. When refresh is triggered the items which are expanded - will stay expanded and vice versa. - - tree_view (QWidgets.QTreeView): the tree view nested in the application - column (int): the column to retrieve the data from - role (int): the role which dictates what will be returned - - Returns: - None - - """ - - model = tree_view.model() - selection_model = tree_view.selectionModel() - flags = selection_model.Select | selection_model.Rows - - if current_index: - current_index_value = tree_view.currentIndex().data(role) - else: - current_index_value = None - - selected_rows = selection_model.selectedRows() - if not selected_rows: - yield - return - - selected = set(row.data(role) for row in selected_rows) - try: - yield - finally: - if not selected: - return - - # Go through all indices, select the ones with similar data - for index in _iter_model_rows(model, - column=column, - include_root=False): - - value = index.data(role) - state = value in selected - if state: - tree_view.scrollTo(index) # Ensure item is visible - selection_model.select(index, flags) - - if current_index_value and value == current_index_value: - tree_view.setCurrentIndex(index) - - -class AssetModel(TreeModel): - """A model listing assets in the silo in the active project. - - The assets are displayed in a treeview, they are visually parented by - a `visualParent` field in the database containing an `_id` to a parent - asset. - - """ - - Columns = ["label"] - Name = 0 - Deprecated = 2 - ObjectId = 3 - - DocumentRole = QtCore.Qt.UserRole + 2 - ObjectIdRole = QtCore.Qt.UserRole + 3 - - def __init__(self, parent=None): - super(AssetModel, self).__init__(parent=parent) - self.refresh() - - def _add_hierarchy(self, assets, parent=None, silos=None): - """Add the assets that are related to the parent as children items. - - This method does *not* query the database. These instead are queried - in a single batch upfront as an optimization to reduce database - queries. Resulting in up to 10x speed increase. - - Args: - assets (dict): All assets in the currently active silo stored - by key/value - - Returns: - None - - """ - if silos: - # WARNING: Silo item "_id" is set to silo value - # mainly because GUI issue with preserve selection and expanded row - # and because of easier hierarchy parenting (in "assets") - for silo in silos: - item = Item({ - "_id": silo, - "name": silo, - "label": silo, - "type": "silo" - }) - self.add_child(item, parent=parent) - self._add_hierarchy(assets, parent=item) - - parent_id = parent["_id"] if parent else None - current_assets = assets.get(parent_id, list()) - - for asset in current_assets: - # get label from data, otherwise use name - data = asset.get("data", {}) - label = data.get("label", asset["name"]) - tags = data.get("tags", []) - - # store for the asset for optimization - deprecated = "deprecated" in tags - - item = Item({ - "_id": asset["_id"], - "name": asset["name"], - "label": label, - "type": asset["type"], - "tags": ", ".join(tags), - "deprecated": deprecated, - "_document": asset - }) - self.add_child(item, parent=parent) - - # Add asset's children recursively if it has children - if asset["_id"] in assets: - self._add_hierarchy(assets, parent=item) - - def refresh(self): - """Refresh the data for the model.""" - - self.clear() - self.beginResetModel() - - # Get all assets in current silo sorted by name - db_assets = io.find({"type": "asset"}).sort("name", 1) - silos = db_assets.distinct("silo") or None - # if any silo is set to None then it's expected it should not be used - if silos and None in silos: - silos = None - - # Group the assets by their visual parent's id - assets_by_parent = collections.defaultdict(list) - for asset in db_assets: - parent_id = ( - asset.get("data", {}).get("visualParent") or - asset.get("silo") - ) - assets_by_parent[parent_id].append(asset) - - # Build the hierarchical tree items recursively - self._add_hierarchy( - assets_by_parent, - parent=None, - silos=silos - ) - - self.endResetModel() - - def flags(self, index): - return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable - - def data(self, index, role): - - if not index.isValid(): - return - - item = index.internalPointer() - if role == QtCore.Qt.DecorationRole: # icon - - column = index.column() - if column == self.Name: - - # Allow a custom icon and custom icon color to be defined - data = item.get("_document", {}).get("data", {}) - icon = data.get("icon", None) - if icon is None and item.get("type") == "silo": - icon = "database" - color = data.get("color", style.colors.default) - - if icon is None: - # Use default icons if no custom one is specified. - # If it has children show a full folder, otherwise - # show an open folder - has_children = self.rowCount(index) > 0 - icon = "folder" if has_children else "folder-o" - - # Make the color darker when the asset is deprecated - if item.get("deprecated", False): - color = QtGui.QColor(color).darker(250) - - try: - key = "fa.{0}".format(icon) # font-awesome key - icon = qtawesome.icon(key, color=color) - return icon - except Exception as exception: - # Log an error message instead of erroring out completely - # when the icon couldn't be created (e.g. invalid name) - log.error(exception) - - return - - if role == QtCore.Qt.ForegroundRole: # font color - if "deprecated" in item.get("tags", []): - return QtGui.QColor(style.colors.light).darker(250) - - if role == self.ObjectIdRole: - return item.get("_id", None) - - if role == self.DocumentRole: - return item.get("_document", None) - - return super(AssetModel, self).data(index, role) - - -class AssetWidget(QtWidgets.QWidget): - """A Widget to display a tree of assets with filter - - To list the assets of the active project: - >>> # widget = AssetWidget() - >>> # widget.refresh() - >>> # widget.show() - - """ - - assets_refreshed = QtCore.Signal() # on model refresh - selection_changed = QtCore.Signal() # on view selection change - current_changed = QtCore.Signal() # on view current index change - - def __init__(self, parent=None): - super(AssetWidget, self).__init__(parent=parent) - self.setContentsMargins(0, 0, 0, 0) - - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(4) - - # Tree View - model = AssetModel(self) - proxy = RecursiveSortFilterProxyModel() - proxy.setSourceModel(model) - proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) - - view = DeselectableTreeView() - view.setIndentation(15) - view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) - view.setHeaderHidden(True) - view.setModel(proxy) - - # Header - header = QtWidgets.QHBoxLayout() - - icon = qtawesome.icon("fa.refresh", color=style.colors.light) - refresh = QtWidgets.QPushButton(icon, "") - refresh.setToolTip("Refresh items") - - filter = QtWidgets.QLineEdit() - filter.textChanged.connect(proxy.setFilterFixedString) - filter.setPlaceholderText("Filter assets..") - - header.addWidget(filter) - header.addWidget(refresh) - - # Layout - layout.addLayout(header) - layout.addWidget(view) - - # Signals/Slots - selection = view.selectionModel() - selection.selectionChanged.connect(self.selection_changed) - selection.currentChanged.connect(self.current_changed) - refresh.clicked.connect(self.refresh) - - self.refreshButton = refresh - self.model = model - self.proxy = proxy - self.view = view - - def _refresh_model(self): - with preserve_expanded_rows( - self.view, column=0, role=self.model.ObjectIdRole - ): - with preserve_selection( - self.view, column=0, role=self.model.ObjectIdRole - ): - self.model.refresh() - - self.assets_refreshed.emit() - - def refresh(self): - self._refresh_model() - - def get_active_asset(self): - """Return the asset id the current asset.""" - current = self.view.currentIndex() - return current.data(self.model.ItemRole) - - def get_active_index(self): - return self.view.currentIndex() - - def get_selected_assets(self): - """Return the assets' ids that are selected.""" - selection = self.view.selectionModel() - rows = selection.selectedRows() - return [row.data(self.model.ObjectIdRole) for row in rows] - - def select_assets(self, assets, expand=True, key="name"): - """Select assets by name. - - Args: - assets (list): List of asset names - expand (bool): Whether to also expand to the asset in the view - - Returns: - None - - """ - # TODO: Instead of individual selection optimize for many assets - - if not isinstance(assets, (tuple, list)): - assets = [assets] - assert isinstance( - assets, (tuple, list) - ), "Assets must be list or tuple" - - # convert to list - tuple cant be modified - assets = list(assets) - - # Clear selection - selection_model = self.view.selectionModel() - selection_model.clearSelection() - - # Select - mode = selection_model.Select | selection_model.Rows - for index in iter_model_rows( - self.proxy, column=0, include_root=False - ): - # stop iteration if there are no assets to process - if not assets: - break - - value = index.data(self.model.ItemRole).get(key) - if value not in assets: - continue - - # Remove processed asset - assets.pop(assets.index(value)) - - selection_model.select(index, mode) - - if expand: - # Expand parent index - self.view.expand(self.proxy.parent(index)) - - # Set the currently active index - self.view.setCurrentIndex(index) From e629e40b4f95718e94de5c63180c38251c3c8e54 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 18:58:33 +0100 Subject: [PATCH 098/302] created base of event system --- openpype/pipeline/__init__.py | 8 ++ openpype/pipeline/events.py | 221 ++++++++++++++++++++++++++++++ openpype/pipeline/lib/__init__.py | 8 -- openpype/pipeline/lib/events.py | 51 ------- 4 files changed, 229 insertions(+), 59 deletions(-) create mode 100644 openpype/pipeline/events.py delete mode 100644 openpype/pipeline/lib/events.py diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e968df4011..673608bded 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -1,5 +1,10 @@ from .lib import attribute_definitions +from .events import ( + emit_event, + register_event_callback +) + from .create import ( BaseCreator, Creator, @@ -17,6 +22,9 @@ from .publish import ( __all__ = ( "attribute_definitions", + "emit_event", + "register_event_callback", + "BaseCreator", "Creator", "AutoCreator", diff --git a/openpype/pipeline/events.py b/openpype/pipeline/events.py new file mode 100644 index 0000000000..cae8b250f7 --- /dev/null +++ b/openpype/pipeline/events.py @@ -0,0 +1,221 @@ +"""Events holding data about specific event.""" +import os +import re +import inspect +import logging +import weakref +from uuid import uuid4 +try: + from weakref import WeakMethod +except Exception: + from .python_2_comp import WeakMethod + + +class EventCallback(object): + def __init__(self, topic, func_ref, func_name, func_path): + self._topic = topic + # Replace '*' with any character regex and escape rest of text + # - when callback is registered for '*' topic it will receive all + # events + # - it is possible to register to a partial topis 'my.event.*' + # - it will receive all matching event topics + # e.g. 'my.event.start' and 'my.event.end' + topic_regex_str = "^{}$".format( + ".+".join( + re.escape(part) + for part in topic.split("*") + ) + ) + topic_regex = re.compile(topic_regex_str) + self._topic_regex = topic_regex + self._func_ref = func_ref + self._func_name = func_name + self._func_path = func_path + self._ref_valid = True + self._enabled = True + + self._log = None + + def __repr__(self): + return "< {} - {} > {}".format( + self.__class__.__name__, self._func_name, self._func_path + ) + + @property + def log(self): + if self._log is None: + self._log = logging.getLogger(self.__class__.__name__) + return self._log + + @property + def is_ref_valid(self): + return self._ref_valid + + def validate_ref(self): + if not self._ref_valid: + return + + callback = self._func_ref() + if not callback: + self._ref_valid = False + + @property + def enabled(self): + """Is callback enabled.""" + return self._enabled + + def set_enabled(self, enabled): + """Change if callback is enabled.""" + self._enabled = enabled + + def deregister(self): + """Calling this funcion will cause that callback will be removed.""" + # Fake reference + self._ref_valid = False + + def topic_matches(self, topic): + """Check if event topic matches callback's topic.""" + return self._topic_regex.match(topic) + + def process_event(self, event): + """Process event. + + Args: + event(Event): Event that was triggered. + """ + # Skip if callback is not enabled or has invalid reference + if not self._ref_valid or not self._enabled: + return + + # Get reference + callback = self._func_ref() + # Check if reference is valid or callback's topic matches the event + if not callback: + # Change state if is invalid so the callback is removed + self._ref_valid = False + + elif self.topic_matches(event.topic): + # Try execute callback + sig = inspect.signature(callback) + try: + if len(sig.parameters) == 0: + callback() + else: + callback(event) + except Exception: + self.log.warning( + "Failed to execute event callback {}".format( + str(repr(self)) + ), + exc_info=True + ) + + +# Inherit from 'object' for Python 2 hosts +class Event(object): + """Base event object. + + Can be used to anything because data are not much specific. Only required + argument is topic which defines why event is happening and may be used for + filtering. + + Arg: + topic (str): Identifier of event. + data (Any): Data specific for event. Dictionary is recommended. + """ + _data = {} + + def __init__(self, topic, data=None, source=None): + self._id = str(uuid4()) + self._topic = topic + if data is None: + data = {} + self._data = data + self._source = source + + def __getitem__(self, key): + return self._data[key] + + def get(self, key, *args, **kwargs): + return self._data.get(key, *args, **kwargs) + + @property + def id(self): + return self._id + + @property + def source(self): + return self._source + + @property + def data(self): + return self._data + + @property + def topic(self): + return self._topic + + def emit(self): + """Emit event and trigger callbacks.""" + StoredCallbacks.emit_event(self) + + +class StoredCallbacks: + _registered_callbacks = [] + + @classmethod + def add_callback(cls, topic, callback): + # Convert callback into references + # - deleted functions won't cause crashes + if inspect.ismethod(callback): + ref = WeakMethod(callback) + elif callable(callback): + ref = weakref.ref(callback) + else: + # TODO add logs + return + + function_name = callback.__name__ + function_path = os.path.abspath(inspect.getfile(callback)) + callback = EventCallback(topic, ref, function_name, function_path) + cls._registered_callbacks.append(callback) + return callback + + @classmethod + def validate(cls): + invalid_callbacks = [] + for callbacks in cls._registered_callbacks: + for callback in tuple(callbacks): + callback.validate_ref() + if not callback.is_ref_valid: + invalid_callbacks.append(callback) + + for callback in invalid_callbacks: + cls._registered_callbacks.remove(callback) + + @classmethod + def emit_event(cls, event): + invalid_callbacks = [] + for callback in cls._registered_callbacks: + callback.process_event() + if not callback.is_ref_valid: + invalid_callbacks.append(callback) + + for callback in invalid_callbacks: + cls._registered_callbacks.remove(callback) + + +def register_event_callback(topic, callback): + """Add callback that will be executed on specific topic.""" + return StoredCallbacks.add_callback(topic, callback) + + +def emit_event(topic, data=None, source=None): + """Emit event with topic and data. + + Returns: + Event: Object of event that was emitted. + """ + event = Event(topic, data, source) + event.emit() + return event diff --git a/openpype/pipeline/lib/__init__.py b/openpype/pipeline/lib/__init__.py index ed38889c66..f762c4205d 100644 --- a/openpype/pipeline/lib/__init__.py +++ b/openpype/pipeline/lib/__init__.py @@ -1,8 +1,3 @@ -from .events import ( - BaseEvent, - BeforeWorkfileSave -) - from .attribute_definitions import ( AbtractAttrDef, @@ -20,9 +15,6 @@ from .attribute_definitions import ( __all__ = ( - "BaseEvent", - "BeforeWorkfileSave", - "AbtractAttrDef", "UIDef", diff --git a/openpype/pipeline/lib/events.py b/openpype/pipeline/lib/events.py deleted file mode 100644 index 05dea20e8c..0000000000 --- a/openpype/pipeline/lib/events.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Events holding data about specific event.""" - - -# Inherit from 'object' for Python 2 hosts -class BaseEvent(object): - """Base event object. - - Can be used to anything because data are not much specific. Only required - argument is topic which defines why event is happening and may be used for - filtering. - - Arg: - topic (str): Identifier of event. - data (Any): Data specific for event. Dictionary is recommended. - """ - _data = {} - - def __init__(self, topic, data=None): - self._topic = topic - if data is None: - data = {} - self._data = data - - @property - def data(self): - return self._data - - @property - def topic(self): - return self._topic - - @classmethod - def emit(cls, *args, **kwargs): - """Create object of event and emit. - - Args: - Same args as '__init__' expects which may be class specific. - """ - from avalon import pipeline - - obj = cls(*args, **kwargs) - pipeline.emit(obj.topic, [obj]) - return obj - - -class BeforeWorkfileSave(BaseEvent): - """Before workfile changes event data.""" - def __init__(self, filename, workdir): - super(BeforeWorkfileSave, self).__init__("before.workfile.save") - self.filename = filename - self.workdir_path = workdir From 9c111fa9d448b9371807f7812c03d21c4aa5b6f8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 4 Mar 2022 19:01:31 +0100 Subject: [PATCH 099/302] use new event system in openpype --- openpype/__init__.py | 7 +-- openpype/hosts/aftereffects/api/pipeline.py | 3 +- openpype/hosts/blender/api/pipeline.py | 25 ++++++---- openpype/hosts/harmony/api/pipeline.py | 5 +- openpype/hosts/hiero/api/events.py | 4 +- openpype/hosts/hiero/api/menu.py | 2 +- openpype/hosts/houdini/api/pipeline.py | 30 +++++++----- openpype/hosts/maya/api/pipeline.py | 49 ++++++++++--------- openpype/hosts/nuke/api/pipeline.py | 7 +-- openpype/hosts/photoshop/api/pipeline.py | 4 +- .../hosts/tvpaint/api/communication_server.py | 6 +-- openpype/hosts/tvpaint/api/pipeline.py | 5 +- openpype/hosts/webpublisher/api/__init__.py | 5 -- openpype/tools/loader/app.py | 5 +- openpype/tools/workfiles/app.py | 16 ++++-- 15 files changed, 97 insertions(+), 76 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 11b563ebfe..9175727a54 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -10,8 +10,9 @@ from .lib import ( Anatomy, filter_pyblish_plugins, set_plugin_attributes_from_settings, - change_timer_to_current_context + change_timer_to_current_context, ) +from .pipeline import register_event_callback pyblish = avalon = _original_discover = None @@ -122,10 +123,10 @@ def install(): avalon.discover = patched_discover pipeline.discover = patched_discover - avalon.on("taskChanged", _on_task_change) + register_event_callback("taskChanged", _on_task_change) -def _on_task_change(*args): +def _on_task_change(): change_timer_to_current_context() diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 94f1e3d105..6f7cd8c46d 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -10,6 +10,7 @@ from avalon import io, pipeline from openpype import lib from openpype.api import Logger import openpype.hosts.aftereffects +from openpype.pipeline import register_event_callback from .launch_logic import get_stub @@ -73,7 +74,7 @@ def install(): "instanceToggled", on_pyblish_instance_toggled ) - avalon.api.on("application.launched", application_launch) + register_event_callback("application.launched", application_launch) def uninstall(): diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 6da0ba3dcb..38312316cc 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -15,6 +15,10 @@ from avalon import io, schema from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import Logger +from openpype.pipeline import ( + register_event_callback, + emit_event +) import openpype.hosts.blender HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.blender.__file__)) @@ -50,8 +54,9 @@ def install(): lib.append_user_scripts() - avalon.api.on("new", on_new) - avalon.api.on("open", on_open) + register_event_callback("new", on_new) + register_event_callback("open", on_open) + _register_callbacks() _register_events() @@ -113,22 +118,22 @@ def set_start_end_frames(): scene.render.resolution_y = resolution_y -def on_new(arg1, arg2): +def on_new(): set_start_end_frames() -def on_open(arg1, arg2): +def on_open(): set_start_end_frames() @bpy.app.handlers.persistent def _on_save_pre(*args): - avalon.api.emit("before_save", args) + emit_event("before.save") @bpy.app.handlers.persistent def _on_save_post(*args): - avalon.api.emit("save", args) + emit_event("save") @bpy.app.handlers.persistent @@ -136,9 +141,9 @@ def _on_load_post(*args): # Detect new file or opening an existing file if bpy.data.filepath: # Likely this was an open operation since it has a filepath - avalon.api.emit("open", args) + emit_event("open") else: - avalon.api.emit("new", args) + emit_event("new") ops.OpenFileCacher.post_load() @@ -169,7 +174,7 @@ def _register_callbacks(): log.info("Installed event handler _on_load_post...") -def _on_task_changed(*args): +def _on_task_changed(): """Callback for when the task in the context is changed.""" # TODO (jasper): Blender has no concept of projects or workspace. @@ -186,7 +191,7 @@ def _on_task_changed(*args): def _register_events(): """Install callbacks for specific events.""" - avalon.api.on("taskChanged", _on_task_changed) + register_event_callback("taskChanged", _on_task_changed) log.info("Installed event callback for 'taskChanged'...") diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index 17d2870876..a94d30210e 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -9,6 +9,7 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype import lib +from openpype.pipeline import register_event_callback import openpype.hosts.harmony import openpype.hosts.harmony.api as harmony @@ -129,7 +130,7 @@ def check_inventory(): harmony.send({"function": "PypeHarmony.message", "args": msg}) -def application_launch(): +def application_launch(event): """Event that is executed after Harmony is launched.""" # FIXME: This is breaking server <-> client communication. # It is now moved so it it manually called. @@ -187,7 +188,7 @@ def install(): "instanceToggled", on_pyblish_instance_toggled ) - avalon.api.on("application.launched", application_launch) + register_event_callback("application.launched", application_launch) def uninstall(): diff --git a/openpype/hosts/hiero/api/events.py b/openpype/hosts/hiero/api/events.py index 7563503593..6e2580ed8c 100644 --- a/openpype/hosts/hiero/api/events.py +++ b/openpype/hosts/hiero/api/events.py @@ -1,7 +1,7 @@ import os import hiero.core.events -import avalon.api as avalon from openpype.api import Logger +from openpype.pipeline import register_event_callback from .lib import ( sync_avalon_data_to_workfile, launch_workfiles_app, @@ -126,5 +126,5 @@ def register_events(): """ # if task changed then change notext of hiero - avalon.on("taskChanged", update_menu_task_label) + register_event_callback("taskChanged", update_menu_task_label) log.info("Installed event callback for 'taskChanged'..") diff --git a/openpype/hosts/hiero/api/menu.py b/openpype/hosts/hiero/api/menu.py index 306bef87ca..de20b86f30 100644 --- a/openpype/hosts/hiero/api/menu.py +++ b/openpype/hosts/hiero/api/menu.py @@ -14,7 +14,7 @@ self = sys.modules[__name__] self._change_context_menu = None -def update_menu_task_label(*args): +def update_menu_task_label(): """Update the task label in Avalon menu to current session""" object_name = self._change_context_menu diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 1c08e72d65..86c85ad3a1 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -11,6 +11,10 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon.lib import find_submodule +from openpype.pipeline import ( + register_event_callback, + emit_event +) import openpype.hosts.houdini from openpype.hosts.houdini.api import lib @@ -51,11 +55,11 @@ def install(): avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) log.info("Installing callbacks ... ") - # avalon.on("init", on_init) - avalon.api.before("save", before_save) - avalon.api.on("save", on_save) - avalon.api.on("open", on_open) - avalon.api.on("new", on_new) + # register_event_callback("init", on_init) + register_event_callback("before.save", before_save) + register_event_callback("save", on_save) + register_event_callback("open", on_open) + register_event_callback("new", on_new) pyblish.api.register_callback( "instanceToggled", on_pyblish_instance_toggled @@ -101,13 +105,13 @@ def _register_callbacks(): def on_file_event_callback(event): if event == hou.hipFileEventType.AfterLoad: - avalon.api.emit("open", [event]) + emit_event("open") elif event == hou.hipFileEventType.AfterSave: - avalon.api.emit("save", [event]) + emit_event("save") elif event == hou.hipFileEventType.BeforeSave: - avalon.api.emit("before_save", [event]) + emit_event("before_save") elif event == hou.hipFileEventType.AfterClear: - avalon.api.emit("new", [event]) + emit_event("new") def get_main_window(): @@ -229,11 +233,11 @@ def ls(): yield data -def before_save(*args): +def before_save(): return lib.validate_fps() -def on_save(*args): +def on_save(): log.info("Running callback on save..") @@ -242,7 +246,7 @@ def on_save(*args): lib.set_id(node, new_id, overwrite=False) -def on_open(*args): +def on_open(): if not hou.isUIAvailable(): log.debug("Batch mode detected, ignoring `on_open` callbacks..") @@ -279,7 +283,7 @@ def on_open(*args): dialog.show() -def on_new(_): +def on_new(): """Set project resolution and fps when create a new file""" if hou.hipFile.isLoadingHipFile(): diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 1b3bb9feb3..05db1b7b26 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -2,7 +2,6 @@ import os import sys import errno import logging -import contextlib from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om @@ -16,6 +15,10 @@ from avalon.pipeline import AVALON_CONTAINER_ID import openpype.hosts.maya from openpype.tools.utils import host_tools from openpype.lib import any_outdated +from openpype.pipeline import ( + register_event_callback, + emit_event +) from openpype.lib.path_tools import HostDirmap from openpype.hosts.maya.lib import copy_workspace_mel from . import menu, lib @@ -55,7 +58,7 @@ def install(): log.info(PUBLISH_PATH) log.info("Installing callbacks ... ") - avalon.api.on("init", on_init) + register_event_callback("init", on_init) # Callbacks below are not required for headless mode, the `init` however # is important to load referenced Alembics correctly at rendertime. @@ -69,12 +72,12 @@ def install(): menu.install() - avalon.api.on("save", on_save) - avalon.api.on("open", on_open) - avalon.api.on("new", on_new) - avalon.api.before("save", on_before_save) - avalon.api.on("taskChanged", on_task_changed) - avalon.api.on("before.workfile.save", before_workfile_save) + register_event_callback("save", on_save) + register_event_callback("open", on_open) + register_event_callback("new", on_new) + register_event_callback("before.save", on_before_save) + register_event_callback("taskChanged", on_task_changed) + register_event_callback("before.workfile.save", before_workfile_save) def _set_project(): @@ -137,7 +140,7 @@ def _register_callbacks(): def _on_maya_initialized(*args): - avalon.api.emit("init", args) + emit_event("init") if cmds.about(batch=True): log.warning("Running batch mode ...") @@ -147,16 +150,16 @@ def _on_maya_initialized(*args): lib.get_main_window() -def _on_scene_new(*args): - avalon.api.emit("new", args) +def _on_scene_new(): + emit_event("new") -def _on_scene_save(*args): - avalon.api.emit("save", args) +def _on_scene_save(): + emit_event("save") -def _on_scene_open(*args): - avalon.api.emit("open", args) +def _on_scene_open(): + emit_event("open") def _before_scene_save(return_code, client_data): @@ -166,7 +169,7 @@ def _before_scene_save(return_code, client_data): # in order to block the operation. OpenMaya.MScriptUtil.setBool(return_code, True) - avalon.api.emit("before_save", [return_code, client_data]) + emit_event("before.save") def uninstall(): @@ -343,7 +346,7 @@ def containerise(name, return container -def on_init(_): +def on_init(): log.info("Running callback on init..") def safe_deferred(fn): @@ -384,12 +387,12 @@ def on_init(_): safe_deferred(override_toolbox_ui) -def on_before_save(return_code, _): +def on_before_save(): """Run validation for scene's FPS prior to saving""" return lib.validate_fps() -def on_save(_): +def on_save(): """Automatically add IDs to new nodes Any transform of a mesh, without an existing ID, is given one @@ -407,7 +410,7 @@ def on_save(_): lib.set_id(node, new_id, overwrite=False) -def on_open(_): +def on_open(): """On scene open let's assume the containers have changed.""" from Qt import QtWidgets @@ -455,7 +458,7 @@ def on_open(_): dialog.show() -def on_new(_): +def on_new(): """Set project resolution and fps when create a new file""" log.info("Running callback on new..") with lib.suspended_refresh(): @@ -471,7 +474,7 @@ def on_new(_): lib.set_context_settings() -def on_task_changed(*args): +def on_task_changed(): """Wrapped function of app initialize and maya's on task changed""" # Run menu.update_menu_task_label() @@ -509,7 +512,7 @@ def on_task_changed(*args): def before_workfile_save(event): - workdir_path = event.workdir_path + workdir_path = event["workdir_path"] if workdir_path: copy_workspace_mel(workdir_path) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 8c6c9ca55b..1a5116c9ea 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -14,6 +14,7 @@ from openpype.api import ( BuildWorkfile, get_current_project_settings ) +from openpype.pipeline import register_event_callback from openpype.tools.utils import host_tools from .command import viewer_update_and_undo_stop @@ -102,8 +103,8 @@ def install(): avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) # Register Avalon event for workfiles loading. - avalon.api.on("workio.open_file", check_inventory_versions) - avalon.api.on("taskChanged", change_context_label) + register_event_callback("workio.open_file", check_inventory_versions) + register_event_callback("taskChanged", change_context_label) pyblish.api.register_callback( "instanceToggled", on_pyblish_instance_toggled) @@ -226,7 +227,7 @@ def _uninstall_menu(): menu.removeItem(item.name()) -def change_context_label(*args): +def change_context_label(): menubar = nuke.menu("Nuke") menu = menubar.findItem(MENU_LABEL) diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 25983f2471..2aade59812 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -1,5 +1,4 @@ import os -import sys from Qt import QtWidgets import pyblish.api @@ -7,6 +6,7 @@ import avalon.api from avalon import pipeline, io from openpype.api import Logger +from openpype.pipeline import register_event_callback import openpype.hosts.photoshop from . import lib @@ -75,7 +75,7 @@ def install(): "instanceToggled", on_pyblish_instance_toggled ) - avalon.api.on("application.launched", on_application_launch) + register_event_callback("application.launched", on_application_launch) def uninstall(): diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py index e9c5f4c73e..b001b84203 100644 --- a/openpype/hosts/tvpaint/api/communication_server.py +++ b/openpype/hosts/tvpaint/api/communication_server.py @@ -21,7 +21,7 @@ from aiohttp_json_rpc.protocol import ( ) from aiohttp_json_rpc.exceptions import RpcError -from avalon import api +from openpype.pipeline import emit_event from openpype.hosts.tvpaint.tvpaint_plugin import get_plugin_files_path log = logging.getLogger(__name__) @@ -754,7 +754,7 @@ class BaseCommunicator: self._on_client_connect() - api.emit("application.launched") + emit_event("application.launched") def _on_client_connect(self): self._initial_textfile_write() @@ -938,5 +938,5 @@ class QtCommunicator(BaseCommunicator): def _exit(self, *args, **kwargs): super()._exit(*args, **kwargs) - api.emit("application.exit") + emit_event("application.exit") self.qt_app.exit(self.exit_code) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 74eb41892c..b999478fb1 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -14,6 +14,7 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.hosts import tvpaint from openpype.api import get_current_project_settings +from openpype.pipeline import register_event_callback from .lib import ( execute_george, @@ -84,8 +85,8 @@ def install(): if on_instance_toggle not in registered_callbacks: pyblish.api.register_callback("instanceToggled", on_instance_toggle) - avalon.api.on("application.launched", initial_launch) - avalon.api.on("application.exit", application_exit) + register_event_callback("application.launched", initial_launch) + register_event_callback("application.exit", application_exit) def uninstall(): diff --git a/openpype/hosts/webpublisher/api/__init__.py b/openpype/hosts/webpublisher/api/__init__.py index e40d46d662..f338c92a5e 100644 --- a/openpype/hosts/webpublisher/api/__init__.py +++ b/openpype/hosts/webpublisher/api/__init__.py @@ -16,10 +16,6 @@ LOAD_PATH = os.path.join(PLUGINS_DIR, "load") CREATE_PATH = os.path.join(PLUGINS_DIR, "create") -def application_launch(): - pass - - def install(): print("Installing Pype config...") @@ -29,7 +25,6 @@ def install(): log.info(PUBLISH_PATH) io.install() - avalon.on("application.launched", application_launch) def uninstall(): diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index aa743b05fe..afb94bf8fc 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -1,9 +1,10 @@ import sys from Qt import QtWidgets, QtCore -from avalon import api, io, pipeline +from avalon import api, io from openpype import style +from openpype.pipeline import register_event_callback from openpype.tools.utils import ( lib, PlaceholderLineEdit @@ -33,7 +34,7 @@ def on_context_task_change(*args, **kwargs): module.window.on_context_task_change(*args, **kwargs) -pipeline.on("taskChanged", on_context_task_change) +register_event_callback("taskChanged", on_context_task_change) class LoaderWindow(QtWidgets.QDialog): diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index aece7bfb4f..280fe2d8a2 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -9,10 +9,10 @@ import datetime import Qt from Qt import QtWidgets, QtCore -from avalon import io, api, pipeline +from avalon import io, api from openpype import style -from openpype.pipeline.lib import BeforeWorkfileSave +from openpype.pipeline import emit_event from openpype.tools.utils.lib import ( qt_app_context ) @@ -823,7 +823,11 @@ class FilesWidget(QtWidgets.QWidget): return # Trigger before save event - BeforeWorkfileSave.emit(work_filename, self._workdir_path) + emit_event( + "before.workfile.save", + {"filename": work_filename, "workdir_path": self._workdir_path}, + source="workfiles.tool" + ) # Make sure workfiles root is updated # - this triggers 'workio.work_root(...)' which may change value of @@ -853,7 +857,11 @@ class FilesWidget(QtWidgets.QWidget): api.Session["AVALON_PROJECT"] ) # Trigger after save events - pipeline.emit("after.workfile.save", [filepath]) + emit_event( + "after.workfile.save", + {"filepath": filepath}, + source="workfiles.tool" + ) self.workfile_created.emit(filepath) # Refresh files model From bb33d63526b342377b6c5228e36c72d140d6421e Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 5 Mar 2022 03:36:36 +0000 Subject: [PATCH 100/302] [Automated] Bump version --- CHANGELOG.md | 44 ++++++++++++++++++++++---------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348f7dc1b8..711517e6c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.9.0-nightly.5](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0-nightly.6](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) @@ -12,56 +12,56 @@ - Documentation: fixed broken links [\#2799](https://github.com/pypeclub/OpenPype/pull/2799) - Documentation: broken link fix [\#2785](https://github.com/pypeclub/OpenPype/pull/2785) -- Documentation: link fixes [\#2772](https://github.com/pypeclub/OpenPype/pull/2772) -- Update docusaurus to latest version [\#2760](https://github.com/pypeclub/OpenPype/pull/2760) +- Various testing updates [\#2726](https://github.com/pypeclub/OpenPype/pull/2726) **🚀 Enhancements** +- Ftrack: Can sync fps as string [\#2836](https://github.com/pypeclub/OpenPype/pull/2836) - General: Color dialog UI fixes [\#2817](https://github.com/pypeclub/OpenPype/pull/2817) +- Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811) +- Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805) - General: Set context environments for non host applications [\#2803](https://github.com/pypeclub/OpenPype/pull/2803) +- Houdini: Remove duplicate ValidateOutputNode plug-in [\#2780](https://github.com/pypeclub/OpenPype/pull/2780) - Tray publisher: New Tray Publisher host \(beta\) [\#2778](https://github.com/pypeclub/OpenPype/pull/2778) +- Slack: Added regex for filtering on subset names [\#2775](https://github.com/pypeclub/OpenPype/pull/2775) - Houdini: Implement Reset Frame Range [\#2770](https://github.com/pypeclub/OpenPype/pull/2770) -- Pyblish Pype: Remove redundant new line in installed fonts printing [\#2758](https://github.com/pypeclub/OpenPype/pull/2758) - Flame: use Shot Name on segment for asset name [\#2751](https://github.com/pypeclub/OpenPype/pull/2751) -- Flame: adding validator source clip [\#2746](https://github.com/pypeclub/OpenPype/pull/2746) -- Ftrack: Disable ftrack module by default [\#2732](https://github.com/pypeclub/OpenPype/pull/2732) +- Houdini: Move Houdini Save Current File to beginning of ExtractorOrder [\#2747](https://github.com/pypeclub/OpenPype/pull/2747) - RoyalRender: Minor enhancements [\#2700](https://github.com/pypeclub/OpenPype/pull/2700) **🐛 Bug fixes** +- Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832) +- Deadline: Remove recreated event [\#2828](https://github.com/pypeclub/OpenPype/pull/2828) +- Deadline: Added missing events folder [\#2827](https://github.com/pypeclub/OpenPype/pull/2827) - Settings: Missing document with OP versions may break start of OpenPype [\#2825](https://github.com/pypeclub/OpenPype/pull/2825) +- Deadline: more detailed temp file name for environment json [\#2824](https://github.com/pypeclub/OpenPype/pull/2824) +- General: Host name was formed from obsolete code [\#2821](https://github.com/pypeclub/OpenPype/pull/2821) - Settings UI: Fix "Apply from" action [\#2820](https://github.com/pypeclub/OpenPype/pull/2820) +- Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) +- StandalonePublisher: use dynamic groups in subset names [\#2816](https://github.com/pypeclub/OpenPype/pull/2816) - Settings UI: Search case sensitivity [\#2810](https://github.com/pypeclub/OpenPype/pull/2810) - Flame Babypublisher optimalization [\#2806](https://github.com/pypeclub/OpenPype/pull/2806) - resolve: fixing fusion module loading [\#2802](https://github.com/pypeclub/OpenPype/pull/2802) +- Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) +- Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) - Flame: Fix version string in default settings [\#2783](https://github.com/pypeclub/OpenPype/pull/2783) -- After Effects: Fix typo in name `afftereffects` -\> `aftereffects` [\#2768](https://github.com/pypeclub/OpenPype/pull/2768) -- Avoid renaming udim indexes [\#2765](https://github.com/pypeclub/OpenPype/pull/2765) +- Houdini: Fix open last workfile [\#2767](https://github.com/pypeclub/OpenPype/pull/2767) - Maya: Fix `unique\_namespace` when in an namespace that is empty [\#2759](https://github.com/pypeclub/OpenPype/pull/2759) -- Loader UI: Fix right click in representation widget [\#2757](https://github.com/pypeclub/OpenPype/pull/2757) -- Aftereffects 2022 and Deadline [\#2748](https://github.com/pypeclub/OpenPype/pull/2748) -- Flame: bunch of bugs [\#2745](https://github.com/pypeclub/OpenPype/pull/2745) -- Maya: Save current scene on workfile publish [\#2744](https://github.com/pypeclub/OpenPype/pull/2744) -- Version Up: Preserve parts of filename after version number \(like subversion\) on version\_up [\#2741](https://github.com/pypeclub/OpenPype/pull/2741) - Maya: Remove some unused code [\#2709](https://github.com/pypeclub/OpenPype/pull/2709) +- Multiple hosts: unify menu style across hosts [\#2693](https://github.com/pypeclub/OpenPype/pull/2693) **Merged pull requests:** +- General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839) +- Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829) - Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823) -- Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) -- Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) -- Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) +- Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818) - Ftrack: Moved module one hierarchy level higher [\#2792](https://github.com/pypeclub/OpenPype/pull/2792) - SyncServer: Moved module one hierarchy level higher [\#2791](https://github.com/pypeclub/OpenPype/pull/2791) - Royal render: Move module one hierarchy level higher [\#2790](https://github.com/pypeclub/OpenPype/pull/2790) - Deadline: Move module one hierarchy level higher [\#2789](https://github.com/pypeclub/OpenPype/pull/2789) -- Houdini: Remove duplicate ValidateOutputNode plug-in [\#2780](https://github.com/pypeclub/OpenPype/pull/2780) -- Slack: Added regex for filtering on subset names [\#2775](https://github.com/pypeclub/OpenPype/pull/2775) -- Houdini: Fix open last workfile [\#2767](https://github.com/pypeclub/OpenPype/pull/2767) - General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766) -- Harmony: Rendering in Deadline didn't work in other machines than submitter [\#2754](https://github.com/pypeclub/OpenPype/pull/2754) -- Houdini: Move Houdini Save Current File to beginning of ExtractorOrder [\#2747](https://github.com/pypeclub/OpenPype/pull/2747) -- Maya: set Deadline job/batch name to original source workfile name instead of published workfile [\#2733](https://github.com/pypeclub/OpenPype/pull/2733) ## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07) diff --git a/openpype/version.py b/openpype/version.py index b41951a34c..d977e87243 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.5" +__version__ = "3.9.0-nightly.6" diff --git a/pyproject.toml b/pyproject.toml index 851bf3f735..2469cb76a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.5" # OpenPype +version = "3.9.0-nightly.6" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 502785021e5fcf364bb1b01217c1dc522114d17e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Sat, 5 Mar 2022 08:30:29 +0100 Subject: [PATCH 101/302] moved events to openpype.lib --- openpype/__init__.py | 2 +- openpype/hosts/aftereffects/api/pipeline.py | 2 +- openpype/hosts/blender/api/pipeline.py | 2 +- openpype/hosts/harmony/api/pipeline.py | 2 +- openpype/hosts/hiero/api/events.py | 4 ++-- openpype/hosts/houdini/api/pipeline.py | 8 +++----- openpype/hosts/maya/api/pipeline.py | 10 +++++----- openpype/hosts/nuke/api/pipeline.py | 2 +- openpype/hosts/photoshop/api/pipeline.py | 2 +- openpype/hosts/tvpaint/api/communication_server.py | 2 +- openpype/hosts/tvpaint/api/pipeline.py | 2 +- openpype/lib/__init__.py | 7 +++++++ openpype/{pipeline => lib}/events.py | 11 ++++++++--- openpype/pipeline/__init__.py | 8 -------- openpype/tools/loader/app.py | 2 +- openpype/tools/workfiles/app.py | 2 +- 16 files changed, 35 insertions(+), 33 deletions(-) rename openpype/{pipeline => lib}/events.py (94%) diff --git a/openpype/__init__.py b/openpype/__init__.py index 9175727a54..63ad81d3ca 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -11,8 +11,8 @@ from .lib import ( filter_pyblish_plugins, set_plugin_attributes_from_settings, change_timer_to_current_context, + register_event_callback, ) -from .pipeline import register_event_callback pyblish = avalon = _original_discover = None diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 6f7cd8c46d..96d04aad14 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -10,7 +10,7 @@ from avalon import io, pipeline from openpype import lib from openpype.api import Logger import openpype.hosts.aftereffects -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback from .launch_logic import get_stub diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 38312316cc..d1e7df3a93 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -15,7 +15,7 @@ from avalon import io, schema from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import Logger -from openpype.pipeline import ( +from openpype.lib import ( register_event_callback, emit_event ) diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index a94d30210e..8d7ac19eb9 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -9,7 +9,7 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype import lib -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback import openpype.hosts.harmony import openpype.hosts.harmony.api as harmony diff --git a/openpype/hosts/hiero/api/events.py b/openpype/hosts/hiero/api/events.py index 6e2580ed8c..9439199933 100644 --- a/openpype/hosts/hiero/api/events.py +++ b/openpype/hosts/hiero/api/events.py @@ -1,12 +1,12 @@ import os import hiero.core.events from openpype.api import Logger -from openpype.pipeline import register_event_callback from .lib import ( sync_avalon_data_to_workfile, launch_workfiles_app, selection_changed_timeline, - before_project_save + before_project_save, + register_event_callback ) from .tags import add_tags_to_workfile from .menu import update_menu_task_label diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 86c85ad3a1..bbb7a7c512 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -11,15 +11,13 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon.lib import find_submodule -from openpype.pipeline import ( - register_event_callback, - emit_event -) import openpype.hosts.houdini from openpype.hosts.houdini.api import lib from openpype.lib import ( - any_outdated + register_event_callback, + emit_event, + any_outdated, ) from .lib import get_asset_fps diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 05db1b7b26..4945a9ba56 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -14,8 +14,8 @@ from avalon.pipeline import AVALON_CONTAINER_ID import openpype.hosts.maya from openpype.tools.utils import host_tools -from openpype.lib import any_outdated -from openpype.pipeline import ( +from openpype.lib import ( + any_outdated, register_event_callback, emit_event ) @@ -150,15 +150,15 @@ def _on_maya_initialized(*args): lib.get_main_window() -def _on_scene_new(): +def _on_scene_new(*args): emit_event("new") -def _on_scene_save(): +def _on_scene_save(*args): emit_event("save") -def _on_scene_open(): +def _on_scene_open(*args): emit_event("open") diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 1a5116c9ea..419cfb1ac2 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -14,7 +14,7 @@ from openpype.api import ( BuildWorkfile, get_current_project_settings ) -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback from openpype.tools.utils import host_tools from .command import viewer_update_and_undo_stop diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 2aade59812..e424400465 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -6,7 +6,7 @@ import avalon.api from avalon import pipeline, io from openpype.api import Logger -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback import openpype.hosts.photoshop from . import lib diff --git a/openpype/hosts/tvpaint/api/communication_server.py b/openpype/hosts/tvpaint/api/communication_server.py index b001b84203..65cb9aa2f3 100644 --- a/openpype/hosts/tvpaint/api/communication_server.py +++ b/openpype/hosts/tvpaint/api/communication_server.py @@ -21,7 +21,7 @@ from aiohttp_json_rpc.protocol import ( ) from aiohttp_json_rpc.exceptions import RpcError -from openpype.pipeline import emit_event +from openpype.lib import emit_event from openpype.hosts.tvpaint.tvpaint_plugin import get_plugin_files_path log = logging.getLogger(__name__) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index b999478fb1..381c2c62f0 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -14,7 +14,7 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.hosts import tvpaint from openpype.api import get_current_project_settings -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback from .lib import ( execute_george, diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 6a24f30455..1ee9129fa7 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -16,6 +16,10 @@ sys.path.insert(0, python_version_dir) site.addsitedir(python_version_dir) +from .events import ( + emit_event, + register_event_callback +) from .env_tools import ( env_value_to_bool, get_paths_from_environ, @@ -193,6 +197,9 @@ from .openpype_version import ( terminal = Terminal __all__ = [ + "emit_event", + "register_event_callback", + "get_openpype_execute_args", "get_pype_execute_args", "get_linux_launcher_args", diff --git a/openpype/pipeline/events.py b/openpype/lib/events.py similarity index 94% rename from openpype/pipeline/events.py rename to openpype/lib/events.py index cae8b250f7..62c480d8e0 100644 --- a/openpype/pipeline/events.py +++ b/openpype/lib/events.py @@ -8,7 +8,7 @@ from uuid import uuid4 try: from weakref import WeakMethod except Exception: - from .python_2_comp import WeakMethod + from openpype.lib.python_2_comp import WeakMethod class EventCallback(object): @@ -83,6 +83,7 @@ class EventCallback(object): Args: event(Event): Event that was triggered. """ + self.log.info("Processing event {}".format(event.topic)) # Skip if callback is not enabled or has invalid reference if not self._ref_valid or not self._enabled: return @@ -93,9 +94,11 @@ class EventCallback(object): if not callback: # Change state if is invalid so the callback is removed self._ref_valid = False + self.log.info("Invalid reference") elif self.topic_matches(event.topic): # Try execute callback + self.log.info("Triggering callback") sig = inspect.signature(callback) try: if len(sig.parameters) == 0: @@ -109,6 +112,8 @@ class EventCallback(object): ), exc_info=True ) + else: + self.log.info("Not matchin callback") # Inherit from 'object' for Python 2 hosts @@ -172,7 +177,7 @@ class StoredCallbacks: elif callable(callback): ref = weakref.ref(callback) else: - # TODO add logs + print("Invalid callback") return function_name = callback.__name__ @@ -197,7 +202,7 @@ class StoredCallbacks: def emit_event(cls, event): invalid_callbacks = [] for callback in cls._registered_callbacks: - callback.process_event() + callback.process_event(event) if not callback.is_ref_valid: invalid_callbacks.append(callback) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index 673608bded..e968df4011 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -1,10 +1,5 @@ from .lib import attribute_definitions -from .events import ( - emit_event, - register_event_callback -) - from .create import ( BaseCreator, Creator, @@ -22,9 +17,6 @@ from .publish import ( __all__ = ( "attribute_definitions", - "emit_event", - "register_event_callback", - "BaseCreator", "Creator", "AutoCreator", diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index afb94bf8fc..ec8f56e74b 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -4,7 +4,7 @@ from Qt import QtWidgets, QtCore from avalon import api, io from openpype import style -from openpype.pipeline import register_event_callback +from openpype.lib import register_event_callback from openpype.tools.utils import ( lib, PlaceholderLineEdit diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 280fe2d8a2..87e1492a20 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -12,7 +12,6 @@ from Qt import QtWidgets, QtCore from avalon import io, api from openpype import style -from openpype.pipeline import emit_event from openpype.tools.utils.lib import ( qt_app_context ) @@ -21,6 +20,7 @@ from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget from openpype.tools.utils.tasks_widget import TasksWidget from openpype.tools.utils.delegates import PrettyTimeDelegate from openpype.lib import ( + emit_event, Anatomy, get_workfile_doc, create_workfile_doc, From 70cf30041aa581f65fd5227cae508e42b7aeac88 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Sat, 5 Mar 2022 09:45:36 +0100 Subject: [PATCH 102/302] fix python 2 compatibility --- openpype/lib/events.py | 89 ++++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/openpype/lib/events.py b/openpype/lib/events.py index 62c480d8e0..f71cb74c10 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -12,7 +12,32 @@ except Exception: class EventCallback(object): - def __init__(self, topic, func_ref, func_name, func_path): + """Callback registered to a topic. + + The callback function is registered to a topic. Topic is a string which + may contain '*' that will be handled as "any characters". + + # Examples: + - "workfile.save" Callback will be triggered if the event topic is exactly + "workfile.save" . + - "workfile.*" Callback will be triggered an event topic starts with + "workfile." so "workfile.save" and "workfile.open" + will trigger the callback. + - "*" Callback will listen to all events. + + Callback can be function or method. In both cases it should expect one + or none arguments. When 1 argument is expected then the processed 'Event' + object is passed in. + + The registered callbacks don't keep function in memory so it is not + possible to store lambda function as callback. + + Args: + topic(str): Topic which will be listened. + func(func): Callback to a topic. + """ + def __init__(self, topic, func): + self._log = None self._topic = topic # Replace '*' with any character regex and escape rest of text # - when callback is registered for '*' topic it will receive all @@ -28,14 +53,42 @@ class EventCallback(object): ) topic_regex = re.compile(topic_regex_str) self._topic_regex = topic_regex + + # Convert callback into references + # - deleted functions won't cause crashes + if inspect.ismethod(func): + func_ref = WeakMethod(func) + elif callable(func): + func_ref = weakref.ref(func) + else: + func_ref = None + self.log.warning(( + "Registered callback is not callable. \"{}\"" + ).format(str(func))) + + func_name = None + func_path = None + expect_args = False + # Collect additional data about function + # - name + # - path + # - if expect argument or not + if func_ref is not None: + func_name = func.__name__ + func_path = os.path.abspath(inspect.getfile(func)) + if hasattr(inspect, "signature"): + sig = inspect.signature(func) + expect_args = len(sig.parameters) > 0 + else: + expect_args = len(inspect.getargspec(func)[0]) > 0 + self._func_ref = func_ref self._func_name = func_name self._func_path = func_path - self._ref_valid = True + self._expect_args = expect_args + self._ref_valid = func_ref is not None self._enabled = True - self._log = None - def __repr__(self): return "< {} - {} > {}".format( self.__class__.__name__, self._func_name, self._func_path @@ -83,7 +136,7 @@ class EventCallback(object): Args: event(Event): Event that was triggered. """ - self.log.info("Processing event {}".format(event.topic)) + # Skip if callback is not enabled or has invalid reference if not self._ref_valid or not self._enabled: return @@ -94,17 +147,15 @@ class EventCallback(object): if not callback: # Change state if is invalid so the callback is removed self._ref_valid = False - self.log.info("Invalid reference") elif self.topic_matches(event.topic): # Try execute callback - self.log.info("Triggering callback") - sig = inspect.signature(callback) try: - if len(sig.parameters) == 0: - callback() - else: + if self._expect_args: callback(event) + else: + callback() + except Exception: self.log.warning( "Failed to execute event callback {}".format( @@ -112,8 +163,6 @@ class EventCallback(object): ), exc_info=True ) - else: - self.log.info("Not matchin callback") # Inherit from 'object' for Python 2 hosts @@ -170,19 +219,7 @@ class StoredCallbacks: @classmethod def add_callback(cls, topic, callback): - # Convert callback into references - # - deleted functions won't cause crashes - if inspect.ismethod(callback): - ref = WeakMethod(callback) - elif callable(callback): - ref = weakref.ref(callback) - else: - print("Invalid callback") - return - - function_name = callback.__name__ - function_path = os.path.abspath(inspect.getfile(callback)) - callback = EventCallback(topic, ref, function_name, function_path) + callback = EventCallback(topic, callback) cls._registered_callbacks.append(callback) return callback From 17c88141c154003736e59e93f75bc405fc604845 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 12:20:50 +0100 Subject: [PATCH 103/302] fix emit of taskChanged topic --- openpype/lib/avalon_context.py | 4 ++-- openpype/lib/events.py | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 0bfd3f6de0..67a5515100 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -15,6 +15,7 @@ from openpype.settings import ( ) from .anatomy import Anatomy from .profiles_filtering import filter_profiles +from .events import emit_event # avalon module is not imported at the top # - may not be in path at the time of pype.lib initialization @@ -779,7 +780,6 @@ def update_current_task(task=None, asset=None, app=None, template_key=None): """ import avalon.api - from avalon.pipeline import emit changes = compute_session_changes( avalon.api.Session, @@ -799,7 +799,7 @@ def update_current_task(task=None, asset=None, app=None, template_key=None): os.environ[key] = value # Emit session change - emit("taskChanged", changes.copy()) + emit_event("taskChanged", changes.copy()) return changes diff --git a/openpype/lib/events.py b/openpype/lib/events.py index f71cb74c10..9496d71ca8 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -248,13 +248,30 @@ class StoredCallbacks: def register_event_callback(topic, callback): - """Add callback that will be executed on specific topic.""" + """Add callback that will be executed on specific topic. + + Args: + topic(str): Topic on which will callback be triggered. + callback(function): Callback that will be triggered when a topic + is triggered. Callback should expect none or 1 argument where + `Event` object is passed. + + Returns: + EventCallback: Object wrapping the callback. It can be used to + enable/disable listening to a topic or remove the callback from + the topic completely. + """ return StoredCallbacks.add_callback(topic, callback) def emit_event(topic, data=None, source=None): """Emit event with topic and data. + Arg: + topic(str): Event's topic. + data(dict): Event's additional data. Optional. + source(str): Who emitted the topic. Optional. + Returns: Event: Object of event that was emitted. """ From 38b6ad8042647c490c18fc8624e435b9218b83d4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 12:21:53 +0100 Subject: [PATCH 104/302] Loader UI is using registering callback from init --- openpype/tools/loader/app.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/openpype/tools/loader/app.py b/openpype/tools/loader/app.py index ec8f56e74b..d73a977ac6 100644 --- a/openpype/tools/loader/app.py +++ b/openpype/tools/loader/app.py @@ -26,17 +26,6 @@ module = sys.modules[__name__] module.window = None -# Register callback on task change -# - callback can't be defined in Window as it is weak reference callback -# so `WeakSet` will remove it immediately -def on_context_task_change(*args, **kwargs): - if module.window: - module.window.on_context_task_change(*args, **kwargs) - - -register_event_callback("taskChanged", on_context_task_change) - - class LoaderWindow(QtWidgets.QDialog): """Asset loader interface""" @@ -195,6 +184,8 @@ class LoaderWindow(QtWidgets.QDialog): self._first_show = True + register_event_callback("taskChanged", self.on_context_task_change) + def resizeEvent(self, event): super(LoaderWindow, self).resizeEvent(event) self._overlay_frame.resize(self.size()) From e0395fd0afadb39cd789e46cd5ae12eed7863ddc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 12:42:43 +0100 Subject: [PATCH 105/302] hound fix --- openpype/lib/events.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/events.py b/openpype/lib/events.py index 9496d71ca8..9cb80b2a6e 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -18,8 +18,8 @@ class EventCallback(object): may contain '*' that will be handled as "any characters". # Examples: - - "workfile.save" Callback will be triggered if the event topic is exactly - "workfile.save" . + - "workfile.save" Callback will be triggered if the event topic is + exactly "workfile.save" . - "workfile.*" Callback will be triggered an event topic starts with "workfile." so "workfile.save" and "workfile.open" will trigger the callback. From 8088534caa55bacecdf0edd5c9825cd9b80b1908 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 14:49:55 +0100 Subject: [PATCH 106/302] implemented 'create_hard_link' function in openpype lib --- openpype/lib/__init__.py | 2 ++ openpype/lib/path_tools.py | 1 - openpype/lib/vendor_bin_utils.py | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 6a24f30455..e1006303db 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -58,6 +58,7 @@ from .anatomy import ( from .config import get_datetime_data from .vendor_bin_utils import ( + create_hard_link, get_vendor_bin_path, get_oiio_tools_path, get_ffmpeg_tool_path, @@ -208,6 +209,7 @@ __all__ = [ "get_paths_from_environ", "get_global_environments", + "create_hard_link", "get_vendor_bin_path", "get_oiio_tools_path", "get_ffmpeg_tool_path", diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index d6c32ad9e8..71fc0fe25c 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -6,7 +6,6 @@ import logging import six from openpype.settings import get_project_settings -from openpype.settings.lib import get_site_local_overrides from .anatomy import Anatomy from .profiles_filtering import filter_profiles diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 4c2cf93dfa..fcc15a31f0 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -8,6 +8,41 @@ import distutils log = logging.getLogger("FFmpeg utils") +def create_hard_link(src_path, dst_path): + """Create hardlink of file. + + Args: + src_path(str): Full path to a file which is used as source for + hardlink. + dst_path(str): Full path to a file where a link of source will be + added. + """ + # Use `os.link` if is available + # - should be for all platforms with newer python versions + if hasattr(os, "link"): + os.link(src_path, dst_path) + return + + # Windows implementation of hardlinks + # - used in Python 2 + if platform.system().lower() == "windows": + import ctypes + from ctypes.wintypes import BOOL + CreateHardLink = ctypes.windll.kernel32.CreateHardLinkW + CreateHardLink.argtypes = [ + ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_void_p + ] + CreateHardLink.restype = BOOL + + res = CreateHardLink(dst_path, src_path, None) + if res == 0: + raise ctypes.WinError() + # Raises not implemented error if gets here + raise NotImplementedError( + "Implementation of hardlink for current environment is missing." + ) + + def get_vendor_bin_path(bin_app): """Path to OpenPype vendorized binaries. From c57fc0391677b0b48a06ba4c723936cf46071c5f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 14:50:14 +0100 Subject: [PATCH 107/302] use create_hard_link instead of filelink --- openpype/lib/delivery.py | 7 +++---- openpype/plugins/publish/integrate_hero_version.py | 4 ++-- openpype/plugins/publish/integrate_new.py | 8 +++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index a61603fa05..9fc65aae8e 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -71,15 +71,14 @@ def path_from_representation(representation, anatomy): def copy_file(src_path, dst_path): """Hardlink file if possible(to save space), copy if not""" - from avalon.vendor import filelink # safer importing + from openpype.lib import create_hard_link # safer importing if os.path.exists(dst_path): return try: - filelink.create( + create_hard_link( src_path, - dst_path, - filelink.HARDLINK + dst_path ) except OSError: shutil.copyfile(src_path, dst_path) diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py index ec836954e8..60245314f4 100644 --- a/openpype/plugins/publish/integrate_hero_version.py +++ b/openpype/plugins/publish/integrate_hero_version.py @@ -7,7 +7,7 @@ import shutil from pymongo import InsertOne, ReplaceOne import pyblish.api from avalon import api, io, schema -from avalon.vendor import filelink +from openpype.lib import create_hard_link class IntegrateHeroVersion(pyblish.api.InstancePlugin): @@ -518,7 +518,7 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin): # First try hardlink and copy if paths are cross drive try: - filelink.create(src_path, dst_path, filelink.HARDLINK) + create_hard_link(src_path, dst_path) # Return when successful return diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 6e0940d459..f9ab46b6fd 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -13,12 +13,14 @@ from pymongo import DeleteOne, InsertOne import pyblish.api from avalon import io from avalon.api import format_template_with_optional_keys -from avalon.vendor import filelink import openpype.api from datetime import datetime # from pype.modules import ModulesManager from openpype.lib.profiles_filtering import filter_profiles -from openpype.lib import prepare_template_data +from openpype.lib import ( + prepare_template_data, + create_hard_link +) # this is needed until speedcopy for linux is fixed if sys.platform == "win32": @@ -730,7 +732,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): self.log.critical("An unexpected error occurred.") six.reraise(*sys.exc_info()) - filelink.create(src, dst, filelink.HARDLINK) + create_hard_link(src, dst) def get_subset(self, asset, instance): subset_name = instance.data["subset"] From 4c305b16772e56c4d3e7e4dce6e22a73040f0435 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 7 Mar 2022 16:04:21 +0100 Subject: [PATCH 108/302] global: settings removing pillar/letter box enumerator --- .../settings/defaults/project_settings/global.json | 1 - .../schemas/schema_global_publish.json | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index f08bee8b2d..9c44d9bc86 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -107,7 +107,6 @@ "letter_box": { "enabled": false, "ratio": 0.0, - "state": "letterbox", "fill_color": [ 0, 0, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index e608e9ff63..3eea7ccb30 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -366,19 +366,6 @@ "minimum": 0, "maximum": 10000 }, - { - "key": "state", - "label": "Type", - "type": "enum", - "enum_items": [ - { - "letterbox": "Letterbox" - }, - { - "pillar": "Pillar" - } - ] - }, { "type": "color", "label": "Fill Color", From e1dd41274344533eee44759672a17175f9fadd1b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 7 Mar 2022 16:05:16 +0100 Subject: [PATCH 109/302] global: extract review with dynamic letter/pillar box switch --- openpype/plugins/publish/extract_review.py | 48 +++++++++++----------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index a76c0fa450..b70b81e18d 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -978,7 +978,6 @@ class ExtractReview(pyblish.api.InstancePlugin): output = [] ratio = letter_box_def["ratio"] - state = letter_box_def["state"] fill_color = letter_box_def["fill_color"] f_red, f_green, f_blue, f_alpha = fill_color fill_color_hex = "{0:0>2X}{1:0>2X}{2:0>2X}".format( @@ -993,13 +992,13 @@ class ExtractReview(pyblish.api.InstancePlugin): l_red, l_green, l_blue ) line_color_alpha = float(l_alpha) / 255 - test_ratio_width = int( - (output_height - (output_width * (1 / ratio))) / 2 - ) - test_ratio_height = int( - (output_width - (output_height * ratio)) / 2 - ) - if state == "letterbox" and test_ratio_width: + + # test ratios and define if pillar or letter boxes + output_ratio = output_width / output_height + pillar = output_ratio > ratio + need_mask = format(output_ratio, ".3f") != format(ratio, ".3f") + + if need_mask and not pillar: if fill_color_alpha > 0: top_box = ( "drawbox=0:0:{widht}:round(" @@ -1055,7 +1054,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ) output.extend([top_line, bottom_line]) - elif state == "pillar" and test_ratio_height: + elif need_mask and pillar: if fill_color_alpha > 0: left_box = ( "drawbox=0:0:round(({widht}-({height}" @@ -1308,21 +1307,6 @@ class ExtractReview(pyblish.api.InstancePlugin): "scale_factor_by_height: `{}`".format(scale_factor_by_height) ) - # letter_box - if letter_box_enabled: - filters.extend([ - "scale={}x{}:flags=lanczos".format( - output_width, output_height - ), - "setsar=1" - ]) - filters.extend( - self.get_letterbox_filters( - letter_box_def, - output_width, - output_height - ) - ) # scaling none square pixels and 1920 width if ( @@ -1362,6 +1346,22 @@ class ExtractReview(pyblish.api.InstancePlugin): "setsar=1" ]) + # letter_box + if letter_box_enabled: + filters.extend([ + "scale={}x{}:flags=lanczos".format( + output_width, output_height + ), + "setsar=1" + ]) + filters.extend( + self.get_letterbox_filters( + letter_box_def, + output_width, + output_height + ) + ) + new_repre["resolutionWidth"] = output_width new_repre["resolutionHeight"] = output_height From f566779531e83df670f474fcc2652f84408f0234 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 7 Mar 2022 16:30:01 +0100 Subject: [PATCH 110/302] global: shifting order for `reformated` tag processing --- openpype/plugins/publish/extract_review.py | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index b70b81e18d..fedeee6f08 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1124,6 +1124,9 @@ class ExtractReview(pyblish.api.InstancePlugin): """ filters = [] + # Get instance data + pixel_aspect = temp_data["pixel_aspect"] + # NOTE Skipped using instance's resolution full_input_path_single_file = temp_data["full_input_path_single_file"] try: @@ -1158,6 +1161,19 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = output_def.get("width") or None output_height = output_def.get("height") or None + # if nuke baking profile was having set reformat node + reformat_in_baking = bool("reformated" in new_repre["tags"]) + self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) + + if reformat_in_baking: + self.log.debug(( + "Using resolution from input. It is already " + "reformated from baking process" + )) + output_width = output_width or input_width + output_height = output_height or input_height + pixel_aspect = 1 + # Overscal color overscan_color_value = "black" overscan_color = output_def.get("overscan_color") @@ -1189,9 +1205,6 @@ class ExtractReview(pyblish.api.InstancePlugin): letter_box_def = output_def["letter_box"] letter_box_enabled = letter_box_def["enabled"] - # Get instance data - pixel_aspect = temp_data["pixel_aspect"] - # Make sure input width and height is not an odd number input_width_is_odd = bool(input_width % 2 != 0) input_height_is_odd = bool(input_height % 2 != 0) @@ -1216,9 +1229,6 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("input_width: `{}`".format(input_width)) self.log.debug("input_height: `{}`".format(input_height)) - reformat_in_baking = bool("reformated" in new_repre["tags"]) - self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) - # Use instance resolution if output definition has not set it. if output_width is None or output_height is None: output_width = temp_data["resolution_width"] @@ -1230,17 +1240,6 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = input_width output_height = input_height - if reformat_in_baking: - self.log.debug(( - "Using resolution from input. It is already " - "reformated from baking process" - )) - output_width = input_width - output_height = input_height - pixel_aspect = 1 - new_repre["resolutionWidth"] = input_width - new_repre["resolutionHeight"] = input_height - output_width = int(output_width) output_height = int(output_height) From bde7d988a21fdf1df8ac95ff863089f25dc31612 Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Mon, 7 Mar 2022 16:59:50 +0100 Subject: [PATCH 111/302] Fix family test in validate_write_legacy to work with stillImage --- openpype/hosts/nuke/plugins/publish/validate_write_legacy.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py b/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py index a73bed8edd..91e7dacc6e 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py +++ b/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py @@ -34,9 +34,8 @@ class ValidateWriteLegacy(pyblish.api.InstancePlugin): # test if render in family test knob # and only one item should be available assert len(family_test) == 1, msg + " > More avalon attributes" - assert "render" in node[family_test[0]].value(), msg + \ + assert "render" in node[family_test[0]].value() or "still" in node[family_test[0]].value(), msg + \ " > Not correct family" - # test if `file` knob in node, this way old # non-group-node write could be detected assert "file" not in node.knobs(), msg + \ @@ -74,6 +73,8 @@ class ValidateWriteLegacy(pyblish.api.InstancePlugin): Create_name = "CreateWriteRender" elif family == "prerender": Create_name = "CreateWritePrerender" + elif family == "still": + Create_name = "CreateWriteStill" # get appropriate plugin class creator_plugin = None From 9f39bba2d3bdc97f0005ed3793134f66ecad834b Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Mon, 7 Mar 2022 17:06:23 +0100 Subject: [PATCH 112/302] fix hound --- openpype/hosts/nuke/plugins/publish/validate_write_legacy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py b/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py index 91e7dacc6e..08f09f8097 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py +++ b/openpype/hosts/nuke/plugins/publish/validate_write_legacy.py @@ -34,7 +34,8 @@ class ValidateWriteLegacy(pyblish.api.InstancePlugin): # test if render in family test knob # and only one item should be available assert len(family_test) == 1, msg + " > More avalon attributes" - assert "render" in node[family_test[0]].value() or "still" in node[family_test[0]].value(), msg + \ + assert "render" in node[family_test[0]].value() \ + or "still" in node[family_test[0]].value(), msg + \ " > Not correct family" # test if `file` knob in node, this way old # non-group-node write could be detected From e48af4585734f2354ea0133d7fafc9c0922aefca Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 17:22:51 +0100 Subject: [PATCH 113/302] moved qargparse into openpype vendor --- openpype/hosts/flame/api/plugin.py | 2 +- openpype/hosts/hiero/api/plugin.py | 2 +- openpype/hosts/maya/api/plugin.py | 3 +- openpype/hosts/nuke/plugins/load/load_clip.py | 2 +- .../hosts/nuke/plugins/load/load_image.py | 3 +- .../plugins/load/load_image_from_sequence.py | 3 +- openpype/hosts/resolve/api/plugin.py | 6 +- .../hosts/tvpaint/plugins/load/load_image.py | 2 +- .../plugins/load/load_reference_image.py | 2 +- openpype/plugins/load/delete_old_versions.py | 2 +- openpype/tools/utils/widgets.py | 4 +- openpype/vendor/python/common/qargparse.py | 817 ++++++++++++++++++ 12 files changed, 833 insertions(+), 15 deletions(-) create mode 100644 openpype/vendor/python/common/qargparse.py diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index ec49db1601..0850faf98d 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -2,9 +2,9 @@ import os import re import shutil import sys -from avalon.vendor import qargparse from xml.etree import ElementTree as ET import six +import qargparse from Qt import QtWidgets, QtCore import openpype.api as openpype from openpype import style diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py index 3506af2d6a..3d7bdeab68 100644 --- a/openpype/hosts/hiero/api/plugin.py +++ b/openpype/hosts/hiero/api/plugin.py @@ -2,7 +2,7 @@ import re import os import hiero from Qt import QtWidgets, QtCore -from avalon.vendor import qargparse +import qargparse import avalon.api as avalon import openpype.api as openpype from . import lib diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index bdb8fcf13a..547b125eb4 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -2,8 +2,9 @@ import os from maya import cmds +import qargparse + from avalon import api -from avalon.vendor import qargparse from openpype.api import PypeCreatorMixin from .pipeline import containerise diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 21b7a6a816..a253ba4a9d 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -1,5 +1,5 @@ import nuke -from avalon.vendor import qargparse +import qargparse from avalon import api, io from openpype.hosts.nuke.api.lib import ( diff --git a/openpype/hosts/nuke/plugins/load/load_image.py b/openpype/hosts/nuke/plugins/load/load_image.py index d36226b139..27c634ec57 100644 --- a/openpype/hosts/nuke/plugins/load/load_image.py +++ b/openpype/hosts/nuke/plugins/load/load_image.py @@ -1,7 +1,6 @@ -import re import nuke -from avalon.vendor import qargparse +import qargparse from avalon import api, io from openpype.hosts.nuke.api.lib import ( diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 6627aded51..12e0503dfc 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -1,7 +1,7 @@ import os +import qargparse from avalon.pipeline import get_representation_path_from_context -from avalon.vendor import qargparse from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name @@ -92,4 +92,3 @@ class ImageFromSequenceLoader(photoshop.PhotoshopLoader): def remove(self, container): """No update possible, not containerized.""" pass - diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 8612cf82ec..3f4476e18e 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -1,12 +1,14 @@ import re import uuid + +import qargparse +from Qt import QtWidgets, QtCore + from avalon import api import openpype.api as pype from openpype.hosts import resolve -from avalon.vendor import qargparse from . import lib -from Qt import QtWidgets, QtCore class CreatorWidget(QtWidgets.QDialog): diff --git a/openpype/hosts/tvpaint/plugins/load/load_image.py b/openpype/hosts/tvpaint/plugins/load/load_image.py index 7dba1e3619..f861d0119e 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_image.py +++ b/openpype/hosts/tvpaint/plugins/load/load_image.py @@ -1,4 +1,4 @@ -from avalon.vendor import qargparse +import qargparse from openpype.hosts.tvpaint.api import lib, plugin diff --git a/openpype/hosts/tvpaint/plugins/load/load_reference_image.py b/openpype/hosts/tvpaint/plugins/load/load_reference_image.py index 0a85e5dc76..5e4e3965d2 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_reference_image.py +++ b/openpype/hosts/tvpaint/plugins/load/load_reference_image.py @@ -1,6 +1,6 @@ import collections +import qargparse from avalon.pipeline import get_representation_context -from avalon.vendor import qargparse from openpype.hosts.tvpaint.api import lib, pipeline, plugin diff --git a/openpype/plugins/load/delete_old_versions.py b/openpype/plugins/load/delete_old_versions.py index b2f2c88975..e8612745fb 100644 --- a/openpype/plugins/load/delete_old_versions.py +++ b/openpype/plugins/load/delete_old_versions.py @@ -5,10 +5,10 @@ import uuid import clique from pymongo import UpdateOne import ftrack_api +import qargparse from Qt import QtWidgets, QtCore from avalon import api, style -from avalon.vendor import qargparse from avalon.api import AvalonMongoDB import avalon.pipeline from openpype.api import Anatomy diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index a4e172ea5c..783736a9ca 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -1,8 +1,8 @@ import logging from Qt import QtWidgets, QtCore, QtGui - -from avalon.vendor import qtawesome, qargparse +import qargparse +from avalon.vendor import qtawesome from openpype.style import ( get_objected_colors, get_style_image_path diff --git a/openpype/vendor/python/common/qargparse.py b/openpype/vendor/python/common/qargparse.py new file mode 100644 index 0000000000..ebde9ae76d --- /dev/null +++ b/openpype/vendor/python/common/qargparse.py @@ -0,0 +1,817 @@ +""" +NOTE: The required `Qt` module has changed to use the one that vendorized. + Remember to change to relative import when updating this. +""" + +import re +import logging + +from collections import OrderedDict as odict +from Qt import QtCore, QtWidgets, QtGui +import qtawesome + +__version__ = "0.5.2" +_log = logging.getLogger(__name__) +_type = type # used as argument + +try: + # Python 2 + _basestring = basestring +except NameError: + _basestring = str + + +class QArgumentParser(QtWidgets.QWidget): + """User interface arguments + + Arguments: + arguments (list, optional): Instances of QArgument + description (str, optional): Long-form text of what this parser is for + storage (QSettings, optional): Persistence to disk, providing + value() and setValue() methods + + """ + + changed = QtCore.Signal(QtCore.QObject) # A QArgument + + def __init__(self, + arguments=None, + description=None, + storage=None, + parent=None): + super(QArgumentParser, self).__init__(parent) + self.setAttribute(QtCore.Qt.WA_StyledBackground) + + # Create internal settings + if storage is True: + storage = QtCore.QSettings( + QtCore.QSettings.IniFormat, + QtCore.QSettings.UserScope, + __name__, "QArgparse", + ) + + if storage is not None: + _log.info("Storing settings @ %s" % storage.fileName()) + + arguments = arguments or [] + + assert hasattr(arguments, "__iter__"), "arguments must be iterable" + assert isinstance(storage, (type(None), QtCore.QSettings)), ( + "storage must be of type QSettings" + ) + + layout = QtWidgets.QGridLayout(self) + layout.setRowStretch(999, 1) + + if description: + layout.addWidget(QtWidgets.QLabel(description), 0, 0, 1, 2) + + self._row = 1 + self._storage = storage + self._arguments = odict() + self._desciption = description + + for arg in arguments or []: + self._addArgument(arg) + + self.setStyleSheet(style) + + def setDescription(self, text): + self._desciption.setText(text) + + def addArgument(self, name, type=None, default=None, **kwargs): + # Infer type from default + if type is None and default is not None: + type = _type(default) + + # Default to string + type = type or str + + Argument = { + None: String, + int: Integer, + float: Float, + bool: Boolean, + str: String, + list: Enum, + tuple: Enum, + }.get(type, type) + + arg = Argument(name, default=default, **kwargs) + self._addArgument(arg) + return arg + + def _addArgument(self, arg): + if arg["name"] in self._arguments: + raise ValueError("Duplicate argument '%s'" % arg["name"]) + + if self._storage is not None: + default = self._storage.value(arg["name"]) + + if default: + if isinstance(arg, Boolean): + default = bool({ + None: QtCore.Qt.Unchecked, + + 0: QtCore.Qt.Unchecked, + 1: QtCore.Qt.Checked, + 2: QtCore.Qt.Checked, + + "0": QtCore.Qt.Unchecked, + "1": QtCore.Qt.Checked, + "2": QtCore.Qt.Checked, + + # May be stored as string, if used with IniFormat + "false": QtCore.Qt.Unchecked, + "true": QtCore.Qt.Checked, + }.get(default)) + + arg["default"] = default + + arg.changed.connect(lambda: self.on_changed(arg)) + + label = ( + QtWidgets.QLabel(arg["label"]) + if arg.label + else QtWidgets.QLabel() + ) + widget = arg.create() + icon = qtawesome.icon("fa.refresh", color="white") + reset = QtWidgets.QPushButton(icon, "") # default + reset.setToolTip("Reset") + reset.setProperty("type", "reset") + reset.clicked.connect(lambda: self.on_reset(arg)) + + # Shown on edit + reset.hide() + + for widget in (label, widget): + widget.setToolTip(arg["help"]) + widget.setObjectName(arg["name"]) # useful in CSS + widget.setProperty("type", type(arg).__name__) + widget.setAttribute(QtCore.Qt.WA_StyledBackground) + widget.setEnabled(arg["enabled"]) + + # Align label on top of row if widget is over two times heiger + height = (lambda w: w.sizeHint().height()) + label_on_top = height(label) * 2 < height(widget) + alignment = (QtCore.Qt.AlignTop,) if label_on_top else () + + layout = self.layout() + layout.addWidget(label, self._row, 0, *alignment) + layout.addWidget(widget, self._row, 1) + layout.addWidget(reset, self._row, 2, *alignment) + layout.setColumnStretch(1, 1) + + def on_changed(*_): + reset.setVisible(arg["edited"]) + + arg.changed.connect(on_changed) + + self._row += 1 + self._arguments[arg["name"]] = arg + + def clear(self): + assert self._storage, "Cannot clear without persistent storage" + self._storage.clear() + _log.info("Clearing settings @ %s" % self._storage.fileName()) + + def find(self, name): + return self._arguments[name] + + def on_reset(self, arg): + arg.write(arg["default"]) + + def on_changed(self, arg): + arg["edited"] = arg.read() != arg["default"] + self.changed.emit(arg) + + # Optional PEP08 syntax + add_argument = addArgument + + +class QArgument(QtCore.QObject): + """Base class of argument user interface + """ + changed = QtCore.Signal() + + # Provide a left-hand side label for this argument + label = True + # For defining default value for each argument type + default = None + + def __init__(self, name, default=None, **kwargs): + super(QArgument, self).__init__(kwargs.pop("parent", None)) + + kwargs["name"] = name + kwargs["label"] = kwargs.get("label", camel_to_title(name)) + kwargs["default"] = self.default if default is None else default + kwargs["help"] = kwargs.get("help", "") + kwargs["read"] = kwargs.get("read") + kwargs["write"] = kwargs.get("write") + kwargs["enabled"] = bool(kwargs.get("enabled", True)) + kwargs["edited"] = False + + self._data = kwargs + + def __str__(self): + return self["name"] + + def __repr__(self): + return "%s(\"%s\")" % (type(self).__name__, self["name"]) + + def __getitem__(self, key): + return self._data[key] + + def __setitem__(self, key, value): + self._data[key] = value + + def __eq__(self, other): + if isinstance(other, _basestring): + return self["name"] == other + return super(QArgument, self).__eq__(other) + + def __ne__(self, other): + return not self.__eq__(other) + + def create(self): + return QtWidgets.QWidget() + + def read(self): + return self._read() + + def write(self, value): + self._write(value) + self.changed.emit() + + +class Boolean(QArgument): + """Boolean type user interface + + Presented by `QtWidgets.QCheckBox`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (bool, optional): Argument's default value, default None + enabled (bool, optional): Whether to enable this widget, default True + + """ + def create(self): + widget = QtWidgets.QCheckBox() + widget.clicked.connect(self.changed.emit) + + if isinstance(self, Tristate): + self._read = lambda: widget.checkState() + state = { + 0: QtCore.Qt.Unchecked, + 1: QtCore.Qt.PartiallyChecked, + 2: QtCore.Qt.Checked, + "1": QtCore.Qt.PartiallyChecked, + "0": QtCore.Qt.Unchecked, + "2": QtCore.Qt.Checked, + } + else: + self._read = lambda: bool(widget.checkState()) + state = { + None: QtCore.Qt.Unchecked, + + 0: QtCore.Qt.Unchecked, + 1: QtCore.Qt.Checked, + 2: QtCore.Qt.Checked, + + "0": QtCore.Qt.Unchecked, + "1": QtCore.Qt.Checked, + "2": QtCore.Qt.Checked, + + # May be stored as string, if used with QSettings(..IniFormat) + "false": QtCore.Qt.Unchecked, + "true": QtCore.Qt.Checked, + } + + self._write = lambda value: widget.setCheckState(state[value]) + widget.clicked.connect(self.changed.emit) + + if self["default"] is not None: + self._write(self["default"]) + + return widget + + def read(self): + return self._read() + + +class Tristate(QArgument): + """Not implemented""" + + +class Number(QArgument): + """Base class of numeric type user interface""" + default = 0 + + def create(self): + if isinstance(self, Float): + widget = QtWidgets.QDoubleSpinBox() + widget.setMinimum(self._data.get("min", 0.0)) + widget.setMaximum(self._data.get("max", 99.99)) + else: + widget = QtWidgets.QSpinBox() + widget.setMinimum(self._data.get("min", 0)) + widget.setMaximum(self._data.get("max", 99)) + + widget.editingFinished.connect(self.changed.emit) + self._read = lambda: widget.value() + self._write = lambda value: widget.setValue(value) + + if self["default"] != self.default: + self._write(self["default"]) + + return widget + + +class Integer(Number): + """Integer type user interface + + A subclass of `qargparse.Number`, presented by `QtWidgets.QSpinBox`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (int, optional): Argument's default value, default 0 + min (int, optional): Argument's minimum value, default 0 + max (int, optional): Argument's maximum value, default 99 + enabled (bool, optional): Whether to enable this widget, default True + + """ + + +class Float(Number): + """Float type user interface + + A subclass of `qargparse.Number`, presented by `QtWidgets.QDoubleSpinBox`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (float, optional): Argument's default value, default 0.0 + min (float, optional): Argument's minimum value, default 0.0 + max (float, optional): Argument's maximum value, default 99.99 + enabled (bool, optional): Whether to enable this widget, default True + + """ + + +class Range(Number): + """Range type user interface + + A subclass of `qargparse.Number`, not production ready. + + """ + + +class Double3(QArgument): + """Double3 type user interface + + Presented by three `QtWidgets.QLineEdit` widget with `QDoubleValidator` + installed. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (tuple or list, optional): Default (0, 0, 0). + enabled (bool, optional): Whether to enable this widget, default True + + """ + default = (0, 0, 0) + + def create(self): + widget = QtWidgets.QWidget() + layout = QtWidgets.QHBoxLayout(widget) + layout.setContentsMargins(0, 0, 0, 0) + x, y, z = (self.child_arg(layout, i) for i in range(3)) + + self._read = lambda: ( + float(x.text()), float(y.text()), float(z.text())) + self._write = lambda value: [ + w.setText(str(float(v))) for w, v in zip([x, y, z], value)] + + if self["default"] != self.default: + self._write(self["default"]) + + return widget + + def child_arg(self, layout, index): + widget = QtWidgets.QLineEdit() + widget.setValidator(QtGui.QDoubleValidator()) + + default = str(float(self["default"][index])) + widget.setText(default) + + def focusOutEvent(event): + if not widget.text(): + widget.setText(default) # Ensure value exists for `_read` + QtWidgets.QLineEdit.focusOutEvent(widget, event) + widget.focusOutEvent = focusOutEvent + + widget.editingFinished.connect(self.changed.emit) + widget.returnPressed.connect(widget.editingFinished.emit) + + layout.addWidget(widget) + + return widget + + +class String(QArgument): + """String type user interface + + Presented by `QtWidgets.QLineEdit`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (str, optional): Argument's default value, default None + placeholder (str, optional): Placeholder message for the widget + enabled (bool, optional): Whether to enable this widget, default True + + """ + def __init__(self, *args, **kwargs): + super(String, self).__init__(*args, **kwargs) + self._previous = None + + def create(self): + widget = QtWidgets.QLineEdit() + widget.editingFinished.connect(self.onEditingFinished) + widget.returnPressed.connect(widget.editingFinished.emit) + self._read = lambda: widget.text() + self._write = lambda value: widget.setText(value) + + if isinstance(self, Info): + widget.setReadOnly(True) + widget.setPlaceholderText(self._data.get("placeholder", "")) + + if self["default"] is not None: + self._write(self["default"]) + self._previous = self["default"] + + return widget + + def onEditingFinished(self): + current = self._read() + + if current != self._previous: + self.changed.emit() + self._previous = current + + +class Info(String): + """String type user interface but read-only + + A subclass of `qargparse.String`, presented by `QtWidgets.QLineEdit`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (str, optional): Argument's default value, default None + enabled (bool, optional): Whether to enable this widget, default True + + """ + + +class Color(String): + """Color type user interface + + A subclass of `qargparse.String`, not production ready. + + """ + + +class Button(QArgument): + """Button type user interface + + Presented by `QtWidgets.QPushButton`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (bool, optional): Argument's default value, default None + enabled (bool, optional): Whether to enable this widget, default True + + """ + label = False + + def create(self): + widget = QtWidgets.QPushButton(self["label"]) + widget.clicked.connect(self.changed.emit) + + state = [ + QtCore.Qt.Unchecked, + QtCore.Qt.Checked, + ] + + if isinstance(self, Toggle): + widget.setCheckable(True) + if hasattr(widget, "isChecked"): + self._read = lambda: state[int(widget.isChecked())] + self._write = ( + lambda value: widget.setChecked(value) + ) + else: + self._read = lambda: widget.checkState() + self._write = ( + lambda value: widget.setCheckState(state[int(value)]) + ) + else: + self._read = lambda: "clicked" + self._write = lambda value: None + + if self["default"] is not None: + self._write(self["default"]) + + return widget + + +class Toggle(Button): + """Checkable `Button` type user interface + + Presented by `QtWidgets.QPushButton`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + default (bool, optional): Argument's default value, default None + enabled (bool, optional): Whether to enable this widget, default True + + """ + + +class InfoList(QArgument): + """String list type user interface + + Presented by `QtWidgets.QListView`, not production ready. + + """ + def __init__(self, name, **kwargs): + kwargs["default"] = kwargs.pop("default", ["Empty"]) + super(InfoList, self).__init__(name, **kwargs) + + def create(self): + class Model(QtCore.QStringListModel): + def data(self, index, role): + return super(Model, self).data(index, role) + + model = QtCore.QStringListModel(self["default"]) + widget = QtWidgets.QListView() + widget.setModel(model) + widget.setEditTriggers(widget.NoEditTriggers) + + self._read = lambda: model.stringList() + self._write = lambda value: model.setStringList(value) + + return widget + + +class Choice(QArgument): + """Argument user interface for selecting one from list + + Presented by `QtWidgets.QListView`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + items (list, optional): List of strings for select, default `["Empty"]` + default (str, optional): Default item in `items`, use first of `items` + if not given. + enabled (bool, optional): Whether to enable this widget, default True + + """ + def __init__(self, name, **kwargs): + kwargs["items"] = kwargs.get("items", ["Empty"]) + kwargs["default"] = kwargs.pop("default", kwargs["items"][0]) + super(Choice, self).__init__(name, **kwargs) + + def index(self, value): + """Return numerical equivalent to self.read()""" + return self["items"].index(value) + + def create(self): + def on_changed(selected, deselected): + try: + selected = selected.indexes()[0] + except IndexError: + # At least one item must be selected at all times + selected = deselected.indexes()[0] + + value = selected.data(QtCore.Qt.DisplayRole) + set_current(value) + self.changed.emit() + + def set_current(current): + options = model.stringList() + + if current == "Empty": + index = 0 + else: + for index, member in enumerate(options): + if member == current: + break + else: + raise ValueError( + "%s not a member of %s" % (current, options) + ) + + qindex = model.index(index, 0, QtCore.QModelIndex()) + smodel.setCurrentIndex(qindex, smodel.ClearAndSelect) + self["current"] = options[index] + + def reset(items, default=None): + items = items or ["Empty"] + model.setStringList(items) + set_current(default or items[0]) + + model = QtCore.QStringListModel() + widget = QtWidgets.QListView() + widget.setModel(model) + widget.setEditTriggers(widget.NoEditTriggers) + widget.setSelectionMode(widget.SingleSelection) + smodel = widget.selectionModel() + smodel.selectionChanged.connect(on_changed) + + self._read = lambda: self["current"] + self._write = lambda value: set_current(value) + self.reset = reset + + reset(self["items"], self["default"]) + + return widget + + +class Separator(QArgument): + """Visual separator + + Example: + + item1 + item2 + ------------ + item3 + item4 + + """ + + def create(self): + widget = QtWidgets.QWidget() + + self._read = lambda: None + self._write = lambda value: None + + return widget + + +class Enum(QArgument): + """Argument user interface for selecting one from dropdown list + + Presented by `QtWidgets.QComboBox`. + + Arguments: + name (str): The name of argument + label (str, optional): Display name, convert from `name` if not given + help (str, optional): Tool tip message of this argument + items (list, optional): List of strings for select, default `[]` + default (int, optional): Index of default item, use first of `items` + if not given. + enabled (bool, optional): Whether to enable this widget, default True + + """ + def __init__(self, name, **kwargs): + kwargs["default"] = kwargs.pop("default", 0) + kwargs["items"] = kwargs.get("items", []) + + assert isinstance(kwargs["items"], (tuple, list)), ( + "items must be list" + ) + + super(Enum, self).__init__(name, **kwargs) + + def create(self): + widget = QtWidgets.QComboBox() + widget.addItems(self["items"]) + widget.currentIndexChanged.connect( + lambda index: self.changed.emit()) + + self._read = lambda: widget.currentText() + self._write = lambda value: widget.setCurrentIndex(value) + + if self["default"] is not None: + self._write(self["default"]) + + return widget + + +style = """\ +QWidget { + /* Explicitly specify a size, to account for automatic HDPi */ + font-size: 11px; +} + +*[type="Button"] { + text-align:left; +} + +*[type="Info"] { + background: transparent; + border: none; +} + +QLabel[type="Separator"] { + min-height: 20px; + text-decoration: underline; +} + +QPushButton[type="reset"] { + max-width: 11px; + max-height: 11px; +} + +""" + + +def camelToTitle(text): + """Convert camelCase `text` to Title Case + + Example: + >>> camelToTitle("mixedCase") + "Mixed Case" + >>> camelToTitle("myName") + "My Name" + >>> camelToTitle("you") + "You" + >>> camelToTitle("You") + "You" + >>> camelToTitle("This is That") + "This Is That" + + """ + + return re.sub( + r"((?<=[a-z])[A-Z]|(? Date: Mon, 7 Mar 2022 17:42:34 +0100 Subject: [PATCH 114/302] added qtawesome and qtpy into poetry lock --- .../hosts/fusion/scripts/set_rendermode.py | 2 +- .../hosts/fusion/utility_scripts/switch_ui.py | 2 +- openpype/tools/assetcreator/widget.py | 2 +- openpype/tools/creator/widgets.py | 3 +- openpype/tools/launcher/lib.py | 2 +- openpype/tools/launcher/models.py | 2 +- openpype/tools/launcher/widgets.py | 2 +- openpype/tools/launcher/window.py | 2 +- openpype/tools/loader/lib.py | 2 +- openpype/tools/loader/model.py | 2 +- openpype/tools/mayalookassigner/models.py | 2 +- .../project_manager/project_manager/style.py | 4 +-- openpype/tools/publisher/widgets/widgets.py | 3 +- openpype/tools/pyblish_pype/model.py | 3 +- openpype/tools/sceneinventory/model.py | 4 +-- .../tools/sceneinventory/switch_dialog.py | 2 +- openpype/tools/sceneinventory/view.py | 2 +- openpype/tools/sceneinventory/window.py | 2 +- .../tools/settings/settings/categories.py | 3 +- openpype/tools/settings/settings/widgets.py | 2 +- .../standalonepublish/widgets/model_asset.py | 2 +- .../widgets/model_tasks_template.py | 2 +- .../standalonepublish/widgets/widget_asset.py | 2 +- .../widgets/widget_family_desc.py | 6 ++-- openpype/tools/subsetmanager/window.py | 2 +- openpype/tools/utils/assets_widget.py | 2 +- openpype/tools/utils/lib.py | 2 +- openpype/tools/utils/tasks_widget.py | 3 +- openpype/tools/utils/widgets.py | 2 +- openpype/tools/workfiles/model.py | 2 +- poetry.lock | 28 +++++++++++++++++++ pyproject.toml | 2 ++ 32 files changed, 65 insertions(+), 38 deletions(-) diff --git a/openpype/hosts/fusion/scripts/set_rendermode.py b/openpype/hosts/fusion/scripts/set_rendermode.py index 77a2d8e945..f0638e4fe3 100644 --- a/openpype/hosts/fusion/scripts/set_rendermode.py +++ b/openpype/hosts/fusion/scripts/set_rendermode.py @@ -1,5 +1,5 @@ from Qt import QtWidgets -from avalon.vendor import qtawesome +import qtawesome from openpype.hosts.fusion.api import get_current_comp diff --git a/openpype/hosts/fusion/utility_scripts/switch_ui.py b/openpype/hosts/fusion/utility_scripts/switch_ui.py index afb39f7041..d9eeae25ea 100644 --- a/openpype/hosts/fusion/utility_scripts/switch_ui.py +++ b/openpype/hosts/fusion/utility_scripts/switch_ui.py @@ -6,7 +6,7 @@ from Qt import QtWidgets, QtCore import avalon.api from avalon import io -from avalon.vendor import qtawesome as qta +import qtawesome as qta from openpype import style from openpype.hosts.fusion import api diff --git a/openpype/tools/assetcreator/widget.py b/openpype/tools/assetcreator/widget.py index fd0f438e68..9ad7e19692 100644 --- a/openpype/tools/assetcreator/widget.py +++ b/openpype/tools/assetcreator/widget.py @@ -2,7 +2,7 @@ import logging import contextlib import collections -from avalon.vendor import qtawesome +import qtawesome from Qt import QtWidgets, QtCore, QtGui from avalon import style, io diff --git a/openpype/tools/creator/widgets.py b/openpype/tools/creator/widgets.py index 9dd435c1cc..43df08496b 100644 --- a/openpype/tools/creator/widgets.py +++ b/openpype/tools/creator/widgets.py @@ -3,9 +3,8 @@ import inspect from Qt import QtWidgets, QtCore, QtGui -from avalon.vendor import qtawesome +import qtawesome -from openpype import style from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS from openpype.tools.utils import ErrorMessageBox diff --git a/openpype/tools/launcher/lib.py b/openpype/tools/launcher/lib.py index b4e6a0c3e9..68c759f295 100644 --- a/openpype/tools/launcher/lib.py +++ b/openpype/tools/launcher/lib.py @@ -16,7 +16,7 @@ provides a bridge between the file-based project inventory and configuration. import os from Qt import QtGui -from avalon.vendor import qtawesome +import qtawesome from openpype.api import resources ICON_CACHE = {} diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index effa283318..9036c9cbd5 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -7,7 +7,7 @@ import time import appdirs from Qt import QtCore, QtGui -from avalon.vendor import qtawesome +import qtawesome from avalon import api from openpype.lib import JSONSettingRegistry from openpype.lib.applications import ( diff --git a/openpype/tools/launcher/widgets.py b/openpype/tools/launcher/widgets.py index 30e6531843..62599664fe 100644 --- a/openpype/tools/launcher/widgets.py +++ b/openpype/tools/launcher/widgets.py @@ -2,7 +2,7 @@ import copy import time import collections from Qt import QtWidgets, QtCore, QtGui -from avalon.vendor import qtawesome +import qtawesome from openpype.tools.flickcharm import FlickCharm from openpype.tools.utils.assets_widget import SingleSelectAssetsWidget diff --git a/openpype/tools/launcher/window.py b/openpype/tools/launcher/window.py index b5b6368865..d80b3eabf0 100644 --- a/openpype/tools/launcher/window.py +++ b/openpype/tools/launcher/window.py @@ -8,7 +8,7 @@ from avalon.api import AvalonMongoDB from openpype import style from openpype.api import resources -from avalon.vendor import qtawesome +import qtawesome from .models import ( LauncherModel, ProjectModel diff --git a/openpype/tools/loader/lib.py b/openpype/tools/loader/lib.py index 180dee3eb5..28e94237ec 100644 --- a/openpype/tools/loader/lib.py +++ b/openpype/tools/loader/lib.py @@ -1,7 +1,7 @@ import inspect from Qt import QtGui +import qtawesome -from avalon.vendor import qtawesome from openpype.tools.utils.widgets import ( OptionalAction, OptionDialog diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index 10b22d0e17..baee569239 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -8,8 +8,8 @@ from avalon import ( schema ) from Qt import QtCore, QtGui +import qtawesome -from avalon.vendor import qtawesome from avalon.lib import HeroVersionType from openpype.tools.utils.models import TreeModel, Item diff --git a/openpype/tools/mayalookassigner/models.py b/openpype/tools/mayalookassigner/models.py index 39cab83c61..386b7d7e1e 100644 --- a/openpype/tools/mayalookassigner/models.py +++ b/openpype/tools/mayalookassigner/models.py @@ -1,8 +1,8 @@ from collections import defaultdict from Qt import QtCore +import qtawesome -from avalon.vendor import qtawesome from avalon.style import colors from openpype.tools.utils import models diff --git a/openpype/tools/project_manager/project_manager/style.py b/openpype/tools/project_manager/project_manager/style.py index d24fc7102f..4405d05960 100644 --- a/openpype/tools/project_manager/project_manager/style.py +++ b/openpype/tools/project_manager/project_manager/style.py @@ -1,7 +1,7 @@ import os -from Qt import QtCore, QtGui +from Qt import QtGui -from avalon.vendor import qtawesome +import qtawesome from openpype.tools.utils import paint_image_with_color diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index fb1f0e54aa..9a9fe3193e 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -4,8 +4,7 @@ import re import copy import collections from Qt import QtWidgets, QtCore, QtGui - -from avalon.vendor import qtawesome +import qtawesome from openpype.widgets.attribute_defs import create_widget_for_attr_def from openpype.tools import resources diff --git a/openpype/tools/pyblish_pype/model.py b/openpype/tools/pyblish_pype/model.py index 0faadb5940..2931a379b3 100644 --- a/openpype/tools/pyblish_pype/model.py +++ b/openpype/tools/pyblish_pype/model.py @@ -29,10 +29,9 @@ import pyblish from . import settings, util from .awesome import tags as awesome -import Qt from Qt import QtCore, QtGui +import qtawesome from six import text_type -from .vendor import qtawesome from .constants import PluginStates, InstanceStates, GroupStates, Roles from openpype.api import get_system_settings diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index 6435e5c488..cba60be355 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -4,9 +4,9 @@ import logging from collections import defaultdict from Qt import QtCore, QtGui -from avalon import api, io, style, schema -from avalon.vendor import qtawesome +import qtawesome +from avalon import api, io, style, schema from avalon.lib import HeroVersionType from openpype.tools.utils.models import TreeModel, Item diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 4946c073d4..93ea68beb4 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -1,9 +1,9 @@ import collections import logging from Qt import QtWidgets, QtCore +import qtawesome from avalon import io, api, pipeline -from avalon.vendor import qtawesome from .widgets import ( ButtonWithMenu, diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index ec48b10e47..32c1883de6 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -3,9 +3,9 @@ import logging from functools import partial from Qt import QtWidgets, QtCore +import qtawesome from avalon import io, api, style -from avalon.vendor import qtawesome from avalon.lib import HeroVersionType from openpype.modules import ModulesManager diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 095d30cac0..83e4435015 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -2,7 +2,7 @@ import os import sys from Qt import QtWidgets, QtCore -from avalon.vendor import qtawesome +import qtawesome from avalon import io, api from openpype import style diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py index 663d497c36..a5b5cd40f0 100644 --- a/openpype/tools/settings/settings/categories.py +++ b/openpype/tools/settings/settings/categories.py @@ -1,9 +1,9 @@ -import os import sys import traceback import contextlib from enum import Enum from Qt import QtWidgets, QtCore +import qtawesome from openpype.lib import get_openpype_version from openpype.tools.utils import set_style_property @@ -63,7 +63,6 @@ from .item_widgets import ( PathInputWidget ) from .color_widget import ColorWidget -from avalon.vendor import qtawesome class CategoryState(Enum): diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py index f793aab057..577c2630ab 100644 --- a/openpype/tools/settings/settings/widgets.py +++ b/openpype/tools/settings/settings/widgets.py @@ -2,7 +2,7 @@ import os import copy import uuid from Qt import QtWidgets, QtCore, QtGui -from avalon.vendor import qtawesome +import qtawesome from avalon.mongodb import ( AvalonMongoConnection, AvalonMongoDB diff --git a/openpype/tools/standalonepublish/widgets/model_asset.py b/openpype/tools/standalonepublish/widgets/model_asset.py index 60afe8f96c..6d764eff9f 100644 --- a/openpype/tools/standalonepublish/widgets/model_asset.py +++ b/openpype/tools/standalonepublish/widgets/model_asset.py @@ -1,8 +1,8 @@ import logging import collections from Qt import QtCore, QtGui +import qtawesome from . import TreeModel, Node -from avalon.vendor import qtawesome from avalon import style diff --git a/openpype/tools/standalonepublish/widgets/model_tasks_template.py b/openpype/tools/standalonepublish/widgets/model_tasks_template.py index 476f45391d..1f36eaa39d 100644 --- a/openpype/tools/standalonepublish/widgets/model_tasks_template.py +++ b/openpype/tools/standalonepublish/widgets/model_tasks_template.py @@ -1,6 +1,6 @@ from Qt import QtCore +import qtawesome from . import Node, TreeModel -from avalon.vendor import qtawesome from avalon import style diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index 2886d600bf..d929f227f9 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -1,9 +1,9 @@ import contextlib from Qt import QtWidgets, QtCore +import qtawesome from openpype.tools.utils import PlaceholderLineEdit -from avalon.vendor import qtawesome from avalon import style from . import RecursiveSortFilterProxyModel, AssetModel diff --git a/openpype/tools/standalonepublish/widgets/widget_family_desc.py b/openpype/tools/standalonepublish/widgets/widget_family_desc.py index 8c95ddf2e4..79681615b9 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family_desc.py +++ b/openpype/tools/standalonepublish/widgets/widget_family_desc.py @@ -1,7 +1,7 @@ -from Qt import QtWidgets, QtCore, QtGui -from . import FamilyRole, PluginRole -from avalon.vendor import qtawesome import six +from Qt import QtWidgets, QtCore, QtGui +import qtawesome +from . import FamilyRole, PluginRole class FamilyDescriptionWidget(QtWidgets.QWidget): diff --git a/openpype/tools/subsetmanager/window.py b/openpype/tools/subsetmanager/window.py index b7430d0626..a53af52174 100644 --- a/openpype/tools/subsetmanager/window.py +++ b/openpype/tools/subsetmanager/window.py @@ -2,9 +2,9 @@ import os import sys from Qt import QtWidgets, QtCore +import qtawesome from avalon import api -from avalon.vendor import qtawesome from openpype import style from openpype.tools.utils import PlaceholderLineEdit diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 17164d9e0f..d410b0f1c3 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -3,9 +3,9 @@ import collections import Qt from Qt import QtWidgets, QtCore, QtGui +import qtawesome from avalon import style -from avalon.vendor import qtawesome from openpype.style import get_objected_colors from openpype.tools.flickcharm import FlickCharm diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 01b9e25ef3..1cbc632804 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -4,10 +4,10 @@ import contextlib import collections from Qt import QtWidgets, QtCore, QtGui +import qtawesome import avalon.api from avalon import style -from avalon.vendor import qtawesome from openpype.api import ( get_project_settings, diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 2a8a45626c..7619f59974 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,7 +1,7 @@ from Qt import QtWidgets, QtCore, QtGui +import qtawesome from avalon import style -from avalon.vendor import qtawesome from .views import DeselectableTreeView @@ -14,6 +14,7 @@ TASK_ASSIGNEE_ROLE = QtCore.Qt.UserRole + 4 class TasksModel(QtGui.QStandardItemModel): """A model listing the tasks combined for a list of assets""" + def __init__(self, dbcon, parent=None): super(TasksModel, self).__init__(parent=parent) self.dbcon = dbcon diff --git a/openpype/tools/utils/widgets.py b/openpype/tools/utils/widgets.py index 783736a9ca..d5ae909be8 100644 --- a/openpype/tools/utils/widgets.py +++ b/openpype/tools/utils/widgets.py @@ -2,7 +2,7 @@ import logging from Qt import QtWidgets, QtCore, QtGui import qargparse -from avalon.vendor import qtawesome +import qtawesome from openpype.style import ( get_objected_colors, get_style_image_path diff --git a/openpype/tools/workfiles/model.py b/openpype/tools/workfiles/model.py index 3425cc3df0..b3cf5063e7 100644 --- a/openpype/tools/workfiles/model.py +++ b/openpype/tools/workfiles/model.py @@ -2,9 +2,9 @@ import os import logging from Qt import QtCore +import qtawesome from avalon import style -from avalon.vendor import qtawesome from openpype.tools.utils.models import TreeModel, Item log = logging.getLogger(__name__) diff --git a/poetry.lock b/poetry.lock index b6eba33e0a..a6507bb358 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1219,6 +1219,26 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "qtawesome" +version = "0.7.3" +description = "FontAwesome icons in PyQt and PySide applications" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +qtpy = "*" +six = "*" + +[[package]] +name = "qtpy" +version = "1.11.3" +description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5, PyQt4 and PySide) and additional custom QWidgets." +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" + [[package]] name = "recommonmark" version = "0.7.1" @@ -2651,6 +2671,14 @@ pywin32-ctypes = [ {file = "Qt.py-1.3.6-py2.py3-none-any.whl", hash = "sha256:7edf6048d07a6924707506b5ba34a6e05d66dde9a3f4e3a62f9996ccab0b91c7"}, {file = "Qt.py-1.3.6.tar.gz", hash = "sha256:0d78656a2f814602eee304521c7bf5da0cec414818b3833712c77524294c404a"}, ] +qtawesome = [ + {file = "QtAwesome-0.7.3-py2.py3-none-any.whl", hash = "sha256:ddf4530b4af71cec13b24b88a4cdb56ec85b1e44c43c42d0698804c7137b09b0"}, + {file = "QtAwesome-0.7.3.tar.gz", hash = "sha256:b98b9038d19190e83ab26d91c4d8fc3a36591ee2bc7f5016d4438b8240d097bd"}, +] +qtpy = [ + {file = "QtPy-1.11.3-py2.py3-none-any.whl", hash = "sha256:e121fbee8e95645af29c5a4aceba8d657991551fc1aa3b6b6012faf4725a1d20"}, + {file = "QtPy-1.11.3.tar.gz", hash = "sha256:d427addd37386a8d786db81864a5536700861d95bf085cb31d1bea855d699557"}, +] recommonmark = [ {file = "recommonmark-0.7.1-py2.py3-none-any.whl", hash = "sha256:1b1db69af0231efce3fa21b94ff627ea33dee7079a01dd0a7f8482c3da148b3f"}, {file = "recommonmark-0.7.1.tar.gz", hash = "sha256:bdb4db649f2222dcd8d2d844f0006b958d627f732415d399791ee436a3686d67"}, diff --git a/pyproject.toml b/pyproject.toml index 2469cb76a9..106ae788f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,6 +50,8 @@ pyblish-base = "^1.8.8" pynput = "^1.7.2" # idle manager in tray pymongo = "^3.11.2" "Qt.py" = "^1.3.3" +qtpy = "^1.11.3" +qtawesome = "0.7.3" speedcopy = "^2.1" six = "^1.15" semver = "^2.13.0" # for version resolution From abe340130fb71844b1a7ffe6c6878f0c5b9cf563 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 17:49:15 +0100 Subject: [PATCH 115/302] fixed remaining imports from avalon.vendor --- .../celaction/plugins/publish/submit_celaction_deadline.py | 4 ++-- openpype/hosts/maya/plugins/load/load_vdb_to_vray.py | 2 +- openpype/modules/log_viewer/tray/widgets.py | 2 +- openpype/modules/sync_server/tray/models.py | 2 +- openpype/modules/sync_server/tray/widgets.py | 2 +- openpype/tools/assetcreator/model.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/celaction/plugins/publish/submit_celaction_deadline.py b/openpype/hosts/celaction/plugins/publish/submit_celaction_deadline.py index fd958d11a3..ea109e9445 100644 --- a/openpype/hosts/celaction/plugins/publish/submit_celaction_deadline.py +++ b/openpype/hosts/celaction/plugins/publish/submit_celaction_deadline.py @@ -1,9 +1,9 @@ import os +import re import json import getpass -from avalon.vendor import requests -import re +import requests import pyblish.api diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py index 099c020093..6d5544103d 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -174,7 +174,7 @@ class LoadVDBtoVRay(api.Loader): fname = files[0] else: # Sequence - from avalon.vendor import clique + import clique # todo: check support for negative frames as input collections, remainder = clique.assemble(files) assert len(collections) == 1, ( diff --git a/openpype/modules/log_viewer/tray/widgets.py b/openpype/modules/log_viewer/tray/widgets.py index 5a67780413..ff77405de5 100644 --- a/openpype/modules/log_viewer/tray/widgets.py +++ b/openpype/modules/log_viewer/tray/widgets.py @@ -1,5 +1,5 @@ from Qt import QtCore, QtWidgets -from avalon.vendor import qtawesome +import qtawesome from .models import LogModel, LogsFilterProxy diff --git a/openpype/modules/sync_server/tray/models.py b/openpype/modules/sync_server/tray/models.py index 80f41992cb..7241cc3472 100644 --- a/openpype/modules/sync_server/tray/models.py +++ b/openpype/modules/sync_server/tray/models.py @@ -4,9 +4,9 @@ from bson.objectid import ObjectId from Qt import QtCore from Qt.QtCore import Qt +import qtawesome from openpype.tools.utils.delegates import pretty_timestamp -from avalon.vendor import qtawesome from openpype.lib import PypeLogger from openpype.api import get_local_site_id diff --git a/openpype/modules/sync_server/tray/widgets.py b/openpype/modules/sync_server/tray/widgets.py index 18487b3d11..6aae9562cf 100644 --- a/openpype/modules/sync_server/tray/widgets.py +++ b/openpype/modules/sync_server/tray/widgets.py @@ -5,6 +5,7 @@ from functools import partial from Qt import QtWidgets, QtCore, QtGui from Qt.QtCore import Qt +import qtawesome from openpype.tools.settings import style @@ -12,7 +13,6 @@ from openpype.api import get_local_site_id from openpype.lib import PypeLogger from openpype.tools.utils.delegates import pretty_timestamp -from avalon.vendor import qtawesome from .models import ( SyncRepresentationSummaryModel, diff --git a/openpype/tools/assetcreator/model.py b/openpype/tools/assetcreator/model.py index f84541ca2a..ae9e0f673a 100644 --- a/openpype/tools/assetcreator/model.py +++ b/openpype/tools/assetcreator/model.py @@ -2,7 +2,7 @@ import re import logging from Qt import QtCore, QtWidgets -from avalon.vendor import qtawesome +import qtawesome from avalon import io from avalon import style From d81da109a799cd002b31d79b1408ef94ffdab92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 7 Mar 2022 17:59:50 +0100 Subject: [PATCH 116/302] add containers to hierarchy --- openpype/hosts/maya/plugins/load/load_reference.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 0a0ce4536f..3d18a48a93 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -128,9 +128,9 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes def load(self, context, name=None, namespace=None, options=None): - super(ReferenceLoader, self).load(context, name, namespace, options) + container = super(ReferenceLoader, self).load(context, name, namespace, options) # clean containers if present to AVALON_CONTAINERS - self._organize_containers(self[:]) + self._organize_containers(self[:], container[0]) def switch(self, container, representation): self.update(container, representation) @@ -167,11 +167,11 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): ) @staticmethod - def _organize_containers(nodes): - # type: (list) -> None + def _organize_containers(nodes, container): + # type: (list, str) -> None for node in nodes: id_attr = "{}.id".format(node) if not cmds.attributeQuery("id", node=node, exists=True): continue if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: - cmds.sets(node, forceElement=AVALON_CONTAINERS) + cmds.sets(node, forceElement=container) From b172a2763bd38fd2a191b817f2b53281a88e16bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Mon, 7 Mar 2022 18:02:44 +0100 Subject: [PATCH 117/302] =?UTF-8?q?fix=20=F0=9F=90=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openpype/hosts/maya/plugins/load/load_reference.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 3d18a48a93..017f89d08c 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -5,7 +5,6 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import get_project_settings from openpype.lib import get_creator_by_name import openpype.hosts.maya.api.plugin -from openpype.hosts.maya.api.pipeline import AVALON_CONTAINERS from openpype.hosts.maya.api.lib import maintained_selection @@ -128,7 +127,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): return new_nodes def load(self, context, name=None, namespace=None, options=None): - container = super(ReferenceLoader, self).load(context, name, namespace, options) + container = super(ReferenceLoader, self).load( + context, name, namespace, options) # clean containers if present to AVALON_CONTAINERS self._organize_containers(self[:], container[0]) From f021eaf389053394972d4babd2a076c7d8c5180e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 18:08:25 +0100 Subject: [PATCH 118/302] added python 2 dependency functools32 --- .../python/python_2/functools32/__init__.py | 1 + .../python_2/functools32/_dummy_thread32.py | 158 +++++++ .../python_2/functools32/functools32.py | 423 ++++++++++++++++++ .../python/python_2/functools32/reprlib32.py | 157 +++++++ 4 files changed, 739 insertions(+) create mode 100644 openpype/vendor/python/python_2/functools32/__init__.py create mode 100644 openpype/vendor/python/python_2/functools32/_dummy_thread32.py create mode 100644 openpype/vendor/python/python_2/functools32/functools32.py create mode 100644 openpype/vendor/python/python_2/functools32/reprlib32.py diff --git a/openpype/vendor/python/python_2/functools32/__init__.py b/openpype/vendor/python/python_2/functools32/__init__.py new file mode 100644 index 0000000000..837f7fb651 --- /dev/null +++ b/openpype/vendor/python/python_2/functools32/__init__.py @@ -0,0 +1 @@ +from .functools32 import * diff --git a/openpype/vendor/python/python_2/functools32/_dummy_thread32.py b/openpype/vendor/python/python_2/functools32/_dummy_thread32.py new file mode 100644 index 0000000000..8503b0e3dd --- /dev/null +++ b/openpype/vendor/python/python_2/functools32/_dummy_thread32.py @@ -0,0 +1,158 @@ +"""Drop-in replacement for the thread module. + +Meant to be used as a brain-dead substitute so that threaded code does +not need to be rewritten for when the thread module is not present. + +Suggested usage is:: + + try: + try: + import _thread # Python >= 3 + except: + import thread as _thread # Python < 3 + except ImportError: + import _dummy_thread as _thread + +""" +# Exports only things specified by thread documentation; +# skipping obsolete synonyms allocate(), start_new(), exit_thread(). +__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock', + 'interrupt_main', 'LockType'] + +# A dummy value +TIMEOUT_MAX = 2**31 + +# NOTE: this module can be imported early in the extension building process, +# and so top level imports of other modules should be avoided. Instead, all +# imports are done when needed on a function-by-function basis. Since threads +# are disabled, the import lock should not be an issue anyway (??). + +class error(Exception): + """Dummy implementation of _thread.error.""" + + def __init__(self, *args): + self.args = args + +def start_new_thread(function, args, kwargs={}): + """Dummy implementation of _thread.start_new_thread(). + + Compatibility is maintained by making sure that ``args`` is a + tuple and ``kwargs`` is a dictionary. If an exception is raised + and it is SystemExit (which can be done by _thread.exit()) it is + caught and nothing is done; all other exceptions are printed out + by using traceback.print_exc(). + + If the executed function calls interrupt_main the KeyboardInterrupt will be + raised when the function returns. + + """ + if type(args) != type(tuple()): + raise TypeError("2nd arg must be a tuple") + if type(kwargs) != type(dict()): + raise TypeError("3rd arg must be a dict") + global _main + _main = False + try: + function(*args, **kwargs) + except SystemExit: + pass + except: + import traceback + traceback.print_exc() + _main = True + global _interrupt + if _interrupt: + _interrupt = False + raise KeyboardInterrupt + +def exit(): + """Dummy implementation of _thread.exit().""" + raise SystemExit + +def get_ident(): + """Dummy implementation of _thread.get_ident(). + + Since this module should only be used when _threadmodule is not + available, it is safe to assume that the current process is the + only thread. Thus a constant can be safely returned. + """ + return -1 + +def allocate_lock(): + """Dummy implementation of _thread.allocate_lock().""" + return LockType() + +def stack_size(size=None): + """Dummy implementation of _thread.stack_size().""" + if size is not None: + raise error("setting thread stack size not supported") + return 0 + +class LockType(object): + """Class implementing dummy implementation of _thread.LockType. + + Compatibility is maintained by maintaining self.locked_status + which is a boolean that stores the state of the lock. Pickling of + the lock, though, should not be done since if the _thread module is + then used with an unpickled ``lock()`` from here problems could + occur from this class not having atomic methods. + + """ + + def __init__(self): + self.locked_status = False + + def acquire(self, waitflag=None, timeout=-1): + """Dummy implementation of acquire(). + + For blocking calls, self.locked_status is automatically set to + True and returned appropriately based on value of + ``waitflag``. If it is non-blocking, then the value is + actually checked and not set if it is already acquired. This + is all done so that threading.Condition's assert statements + aren't triggered and throw a little fit. + + """ + if waitflag is None or waitflag: + self.locked_status = True + return True + else: + if not self.locked_status: + self.locked_status = True + return True + else: + if timeout > 0: + import time + time.sleep(timeout) + return False + + __enter__ = acquire + + def __exit__(self, typ, val, tb): + self.release() + + def release(self): + """Release the dummy lock.""" + # XXX Perhaps shouldn't actually bother to test? Could lead + # to problems for complex, threaded code. + if not self.locked_status: + raise error + self.locked_status = False + return True + + def locked(self): + return self.locked_status + +# Used to signal that interrupt_main was called in a "thread" +_interrupt = False +# True when not executing in a "thread" +_main = True + +def interrupt_main(): + """Set _interrupt flag to True to have start_new_thread raise + KeyboardInterrupt upon exiting.""" + if _main: + raise KeyboardInterrupt + else: + global _interrupt + _interrupt = True diff --git a/openpype/vendor/python/python_2/functools32/functools32.py b/openpype/vendor/python/python_2/functools32/functools32.py new file mode 100644 index 0000000000..c44551fac0 --- /dev/null +++ b/openpype/vendor/python/python_2/functools32/functools32.py @@ -0,0 +1,423 @@ +"""functools.py - Tools for working with functions and callable objects +""" +# Python module wrapper for _functools C module +# to allow utilities written in Python to be added +# to the functools module. +# Written by Nick Coghlan +# and Raymond Hettinger +# Copyright (C) 2006-2010 Python Software Foundation. +# See C source code for _functools credits/copyright + +__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] + +from _functools import partial, reduce +from collections import MutableMapping, namedtuple +from .reprlib32 import recursive_repr as _recursive_repr +from weakref import proxy as _proxy +import sys as _sys +try: + from thread import allocate_lock as Lock +except ImportError: + from ._dummy_thread32 import allocate_lock as Lock + +################################################################################ +### OrderedDict +################################################################################ + +class _Link(object): + __slots__ = 'prev', 'next', 'key', '__weakref__' + +class OrderedDict(dict): + 'Dictionary that remembers insertion order' + # An inherited dict maps keys to values. + # The inherited dict provides __getitem__, __len__, __contains__, and get. + # The remaining methods are order-aware. + # Big-O running times for all methods are the same as regular dictionaries. + + # The internal self.__map dict maps keys to links in a doubly linked list. + # The circular doubly linked list starts and ends with a sentinel element. + # The sentinel element never gets deleted (this simplifies the algorithm). + # The sentinel is in self.__hardroot with a weakref proxy in self.__root. + # The prev links are weakref proxies (to prevent circular references). + # Individual links are kept alive by the hard reference in self.__map. + # Those hard references disappear when a key is deleted from an OrderedDict. + + def __init__(self, *args, **kwds): + '''Initialize an ordered dictionary. The signature is the same as + regular dictionaries, but keyword arguments are not recommended because + their insertion order is arbitrary. + + ''' + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__root + except AttributeError: + self.__hardroot = _Link() + self.__root = root = _proxy(self.__hardroot) + root.prev = root.next = root + self.__map = {} + self.__update(*args, **kwds) + + def __setitem__(self, key, value, + dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link): + 'od.__setitem__(i, y) <==> od[i]=y' + # Setting a new item creates a new link at the end of the linked list, + # and the inherited dictionary is updated with the new key/value pair. + if key not in self: + self.__map[key] = link = Link() + root = self.__root + last = root.prev + link.prev, link.next, link.key = last, root, key + last.next = link + root.prev = proxy(link) + dict_setitem(self, key, value) + + def __delitem__(self, key, dict_delitem=dict.__delitem__): + 'od.__delitem__(y) <==> del od[y]' + # Deleting an existing item uses self.__map to find the link which gets + # removed by updating the links in the predecessor and successor nodes. + dict_delitem(self, key) + link = self.__map.pop(key) + link_prev = link.prev + link_next = link.next + link_prev.next = link_next + link_next.prev = link_prev + + def __iter__(self): + 'od.__iter__() <==> iter(od)' + # Traverse the linked list in order. + root = self.__root + curr = root.next + while curr is not root: + yield curr.key + curr = curr.next + + def __reversed__(self): + 'od.__reversed__() <==> reversed(od)' + # Traverse the linked list in reverse order. + root = self.__root + curr = root.prev + while curr is not root: + yield curr.key + curr = curr.prev + + def clear(self): + 'od.clear() -> None. Remove all items from od.' + root = self.__root + root.prev = root.next = root + self.__map.clear() + dict.clear(self) + + def popitem(self, last=True): + '''od.popitem() -> (k, v), return and remove a (key, value) pair. + Pairs are returned in LIFO order if last is true or FIFO order if false. + + ''' + if not self: + raise KeyError('dictionary is empty') + root = self.__root + if last: + link = root.prev + link_prev = link.prev + link_prev.next = root + root.prev = link_prev + else: + link = root.next + link_next = link.next + root.next = link_next + link_next.prev = root + key = link.key + del self.__map[key] + value = dict.pop(self, key) + return key, value + + def move_to_end(self, key, last=True): + '''Move an existing element to the end (or beginning if last==False). + + Raises KeyError if the element does not exist. + When last=True, acts like a fast version of self[key]=self.pop(key). + + ''' + link = self.__map[key] + link_prev = link.prev + link_next = link.next + link_prev.next = link_next + link_next.prev = link_prev + root = self.__root + if last: + last = root.prev + link.prev = last + link.next = root + last.next = root.prev = link + else: + first = root.next + link.prev = root + link.next = first + root.next = first.prev = link + + def __sizeof__(self): + sizeof = _sys.getsizeof + n = len(self) + 1 # number of links including root + size = sizeof(self.__dict__) # instance dictionary + size += sizeof(self.__map) * 2 # internal dict and inherited dict + size += sizeof(self.__hardroot) * n # link objects + size += sizeof(self.__root) * n # proxy objects + return size + + update = __update = MutableMapping.update + keys = MutableMapping.keys + values = MutableMapping.values + items = MutableMapping.items + __ne__ = MutableMapping.__ne__ + + __marker = object() + + def pop(self, key, default=__marker): + '''od.pop(k[,d]) -> v, remove specified key and return the corresponding + value. If key is not found, d is returned if given, otherwise KeyError + is raised. + + ''' + if key in self: + result = self[key] + del self[key] + return result + if default is self.__marker: + raise KeyError(key) + return default + + def setdefault(self, key, default=None): + 'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od' + if key in self: + return self[key] + self[key] = default + return default + + @_recursive_repr() + def __repr__(self): + 'od.__repr__() <==> repr(od)' + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, list(self.items())) + + def __reduce__(self): + 'Return state information for pickling' + items = [[k, self[k]] for k in self] + inst_dict = vars(self).copy() + for k in vars(OrderedDict()): + inst_dict.pop(k, None) + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def copy(self): + 'od.copy() -> a shallow copy of od' + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S. + If not specified, the value defaults to None. + + ''' + self = cls() + for key in iterable: + self[key] = value + return self + + def __eq__(self, other): + '''od.__eq__(y) <==> od==y. Comparison to another OD is order-sensitive + while comparison to a regular mapping is order-insensitive. + + ''' + if isinstance(other, OrderedDict): + return len(self)==len(other) and \ + all(p==q for p, q in zip(self.items(), other.items())) + return dict.__eq__(self, other) + +# update_wrapper() and wraps() are tools to help write +# wrapper functions that can handle naive introspection + +WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') +WRAPPER_UPDATES = ('__dict__',) +def update_wrapper(wrapper, + wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Update a wrapper function to look like the wrapped function + + wrapper is the function to be updated + wrapped is the original function + assigned is a tuple naming the attributes assigned directly + from the wrapped function to the wrapper function (defaults to + functools.WRAPPER_ASSIGNMENTS) + updated is a tuple naming the attributes of the wrapper that + are updated with the corresponding attribute from the wrapped + function (defaults to functools.WRAPPER_UPDATES) + """ + wrapper.__wrapped__ = wrapped + for attr in assigned: + try: + value = getattr(wrapped, attr) + except AttributeError: + pass + else: + setattr(wrapper, attr, value) + for attr in updated: + getattr(wrapper, attr).update(getattr(wrapped, attr, {})) + # Return the wrapper so this can be used as a decorator via partial() + return wrapper + +def wraps(wrapped, + assigned = WRAPPER_ASSIGNMENTS, + updated = WRAPPER_UPDATES): + """Decorator factory to apply update_wrapper() to a wrapper function + + Returns a decorator that invokes update_wrapper() with the decorated + function as the wrapper argument and the arguments to wraps() as the + remaining arguments. Default arguments are as for update_wrapper(). + This is a convenience function to simplify applying partial() to + update_wrapper(). + """ + return partial(update_wrapper, wrapped=wrapped, + assigned=assigned, updated=updated) + +def total_ordering(cls): + """Class decorator that fills in missing ordering methods""" + convert = { + '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), + ('__le__', lambda self, other: self < other or self == other), + ('__ge__', lambda self, other: not self < other)], + '__le__': [('__ge__', lambda self, other: not self <= other or self == other), + ('__lt__', lambda self, other: self <= other and not self == other), + ('__gt__', lambda self, other: not self <= other)], + '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), + ('__ge__', lambda self, other: self > other or self == other), + ('__le__', lambda self, other: not self > other)], + '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), + ('__gt__', lambda self, other: self >= other and not self == other), + ('__lt__', lambda self, other: not self >= other)] + } + roots = set(dir(cls)) & set(convert) + if not roots: + raise ValueError('must define at least one ordering operation: < > <= >=') + root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ + for opname, opfunc in convert[root]: + if opname not in roots: + opfunc.__name__ = opname + opfunc.__doc__ = getattr(int, opname).__doc__ + setattr(cls, opname, opfunc) + return cls + +def cmp_to_key(mycmp): + """Convert a cmp= function into a key= function""" + class K(object): + __slots__ = ['obj'] + def __init__(self, obj): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + __hash__ = None + return K + +_CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize") + +def lru_cache(maxsize=100): + """Least-recently-used cache decorator. + + If *maxsize* is set to None, the LRU features are disabled and the cache + can grow without bound. + + Arguments to the cached function must be hashable. + + View the cache statistics named tuple (hits, misses, maxsize, currsize) with + f.cache_info(). Clear the cache and statistics with f.cache_clear(). + Access the underlying function with f.__wrapped__. + + See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used + + """ + # Users should only access the lru_cache through its public API: + # cache_info, cache_clear, and f.__wrapped__ + # The internals of the lru_cache are encapsulated for thread safety and + # to allow the implementation to change (including a possible C version). + + def decorating_function(user_function, + tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): + + hits, misses = [0], [0] + kwd_mark = (object(),) # separates positional and keyword args + lock = Lock() # needed because OrderedDict isn't threadsafe + + if maxsize is None: + cache = dict() # simple cache without ordering or size limit + + @wraps(user_function) + def wrapper(*args, **kwds): + key = args + if kwds: + key += kwd_mark + tuple(sorted(kwds.items())) + try: + result = cache[key] + hits[0] += 1 + return result + except KeyError: + pass + result = user_function(*args, **kwds) + cache[key] = result + misses[0] += 1 + return result + else: + cache = OrderedDict() # ordered least recent to most recent + cache_popitem = cache.popitem + cache_renew = cache.move_to_end + + @wraps(user_function) + def wrapper(*args, **kwds): + key = args + if kwds: + key += kwd_mark + tuple(sorted(kwds.items())) + with lock: + try: + result = cache[key] + cache_renew(key) # record recent use of this key + hits[0] += 1 + return result + except KeyError: + pass + result = user_function(*args, **kwds) + with lock: + cache[key] = result # record recent use of this key + misses[0] += 1 + if len(cache) > maxsize: + cache_popitem(0) # purge least recently used cache entry + return result + + def cache_info(): + """Report cache statistics""" + with lock: + return _CacheInfo(hits[0], misses[0], maxsize, len(cache)) + + def cache_clear(): + """Clear the cache and cache statistics""" + with lock: + cache.clear() + hits[0] = misses[0] = 0 + + wrapper.cache_info = cache_info + wrapper.cache_clear = cache_clear + return wrapper + + return decorating_function diff --git a/openpype/vendor/python/python_2/functools32/reprlib32.py b/openpype/vendor/python/python_2/functools32/reprlib32.py new file mode 100644 index 0000000000..af919758ca --- /dev/null +++ b/openpype/vendor/python/python_2/functools32/reprlib32.py @@ -0,0 +1,157 @@ +"""Redo the builtin repr() (representation) but with limits on most sizes.""" + +__all__ = ["Repr", "repr", "recursive_repr"] + +import __builtin__ as builtins +from itertools import islice +try: + from thread import get_ident +except ImportError: + from _dummy_thread32 import get_ident + +def recursive_repr(fillvalue='...'): + 'Decorator to make a repr function return fillvalue for a recursive call' + + def decorating_function(user_function): + repr_running = set() + + def wrapper(self): + key = id(self), get_ident() + if key in repr_running: + return fillvalue + repr_running.add(key) + try: + result = user_function(self) + finally: + repr_running.discard(key) + return result + + # Can't use functools.wraps() here because of bootstrap issues + wrapper.__module__ = getattr(user_function, '__module__') + wrapper.__doc__ = getattr(user_function, '__doc__') + wrapper.__name__ = getattr(user_function, '__name__') + wrapper.__annotations__ = getattr(user_function, '__annotations__', {}) + return wrapper + + return decorating_function + +class Repr: + + def __init__(self): + self.maxlevel = 6 + self.maxtuple = 6 + self.maxlist = 6 + self.maxarray = 5 + self.maxdict = 4 + self.maxset = 6 + self.maxfrozenset = 6 + self.maxdeque = 6 + self.maxstring = 30 + self.maxlong = 40 + self.maxother = 30 + + def repr(self, x): + return self.repr1(x, self.maxlevel) + + def repr1(self, x, level): + typename = type(x).__name__ + if ' ' in typename: + parts = typename.split() + typename = '_'.join(parts) + if hasattr(self, 'repr_' + typename): + return getattr(self, 'repr_' + typename)(x, level) + else: + return self.repr_instance(x, level) + + def _repr_iterable(self, x, level, left, right, maxiter, trail=''): + n = len(x) + if level <= 0 and n: + s = '...' + else: + newlevel = level - 1 + repr1 = self.repr1 + pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)] + if n > maxiter: pieces.append('...') + s = ', '.join(pieces) + if n == 1 and trail: right = trail + right + return '%s%s%s' % (left, s, right) + + def repr_tuple(self, x, level): + return self._repr_iterable(x, level, '(', ')', self.maxtuple, ',') + + def repr_list(self, x, level): + return self._repr_iterable(x, level, '[', ']', self.maxlist) + + def repr_array(self, x, level): + header = "array('%s', [" % x.typecode + return self._repr_iterable(x, level, header, '])', self.maxarray) + + def repr_set(self, x, level): + x = _possibly_sorted(x) + return self._repr_iterable(x, level, 'set([', '])', self.maxset) + + def repr_frozenset(self, x, level): + x = _possibly_sorted(x) + return self._repr_iterable(x, level, 'frozenset([', '])', + self.maxfrozenset) + + def repr_deque(self, x, level): + return self._repr_iterable(x, level, 'deque([', '])', self.maxdeque) + + def repr_dict(self, x, level): + n = len(x) + if n == 0: return '{}' + if level <= 0: return '{...}' + newlevel = level - 1 + repr1 = self.repr1 + pieces = [] + for key in islice(_possibly_sorted(x), self.maxdict): + keyrepr = repr1(key, newlevel) + valrepr = repr1(x[key], newlevel) + pieces.append('%s: %s' % (keyrepr, valrepr)) + if n > self.maxdict: pieces.append('...') + s = ', '.join(pieces) + return '{%s}' % (s,) + + def repr_str(self, x, level): + s = builtins.repr(x[:self.maxstring]) + if len(s) > self.maxstring: + i = max(0, (self.maxstring-3)//2) + j = max(0, self.maxstring-3-i) + s = builtins.repr(x[:i] + x[len(x)-j:]) + s = s[:i] + '...' + s[len(s)-j:] + return s + + def repr_int(self, x, level): + s = builtins.repr(x) # XXX Hope this isn't too slow... + if len(s) > self.maxlong: + i = max(0, (self.maxlong-3)//2) + j = max(0, self.maxlong-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + + def repr_instance(self, x, level): + try: + s = builtins.repr(x) + # Bugs in x.__repr__() can cause arbitrary + # exceptions -- then make up something + except Exception: + return '<%s instance at %x>' % (x.__class__.__name__, id(x)) + if len(s) > self.maxother: + i = max(0, (self.maxother-3)//2) + j = max(0, self.maxother-3-i) + s = s[:i] + '...' + s[len(s)-j:] + return s + + +def _possibly_sorted(x): + # Since not all sequences of items can be sorted and comparison + # functions may raise arbitrary exceptions, return an unsorted + # sequence in that case. + try: + return sorted(x) + except Exception: + return list(x) + +aRepr = Repr() +repr = aRepr.repr From 04ede4539d4896eb6c87ebf3f2a7b7caa977e8a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 7 Mar 2022 18:14:31 +0100 Subject: [PATCH 119/302] lower jsonschema module version --- poetry.lock | 6 +++--- pyproject.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/poetry.lock b/poetry.lock index a6507bb358..ee7b839b8d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -674,7 +674,7 @@ ansicon = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "jsonschema" -version = "3.2.0" +version = "2.6.0" description = "An implementation of JSON Schema validation for Python" category = "main" optional = false @@ -2121,8 +2121,8 @@ jinxed = [ {file = "jinxed-1.1.0.tar.gz", hash = "sha256:d8f1731f134e9e6b04d95095845ae6c10eb15cb223a5f0cabdea87d4a279c305"}, ] jsonschema = [ - {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, - {file = "jsonschema-3.2.0.tar.gz", hash = "sha256:c8a85b28d377cc7737e46e2d9f2b4f44ee3c0e1deac6bf46ddefc7187d30797a"}, + {file = "jsonschema-2.6.0-py2.py3-none-any.whl", hash = "sha256:000e68abd33c972a5248544925a0cae7d1125f9bf6c58280d37546b946769a08"}, + {file = "jsonschema-2.6.0.tar.gz", hash = "sha256:6ff5f3180870836cae40f06fa10419f557208175f13ad7bc26caa77beb1f6e02"}, ] keyring = [ {file = "keyring-22.4.0-py3-none-any.whl", hash = "sha256:d6c531f6d12f3304db6029af1d19894bd446ecbbadd22465fa0f096b3e12d258"}, diff --git a/pyproject.toml b/pyproject.toml index 106ae788f9..2b30d92cdb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ Click = "^7" dnspython = "^2.1.0" ftrack-python-api = "2.0.*" google-api-python-client = "^1.12.8" # sync server google support (should be separate?) -jsonschema = "^3.2.0" +jsonschema = "^2.6.0" keyring = "^22.0.1" log4mongo = "^1.7" pathlib2= "^2.3.5" # deadline submit publish job only (single place, maybe not needed?) From 8617b6d3892684c6e9dfd255e4c563151dd315b1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 8 Mar 2022 11:57:06 +0100 Subject: [PATCH 120/302] processing review feedback --- openpype/plugins/publish/extract_review.py | 42 ++++++++++------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index fedeee6f08..fb0e553a9e 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1107,8 +1107,10 @@ class ExtractReview(pyblish.api.InstancePlugin): output.extend([left_line, right_line]) else: - raise ValueError( - "Letterbox state \"{}\" is not recognized".format(state) + raise ValueError(( + "Letterbox not working: ratio set \"{}\", " + "Image ratio\"{}\"").format( + format(ratio, ".3f"), format(output_ratio, ".3f")) ) return output @@ -1124,9 +1126,20 @@ class ExtractReview(pyblish.api.InstancePlugin): """ filters = [] + # if reformat input video file is already reforamted from upstream + reformat_in_baking = bool("reformated" in new_repre["tags"]) + self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) + # Get instance data pixel_aspect = temp_data["pixel_aspect"] + if reformat_in_baking: + self.log.debug(( + "Using resolution from input. It is already " + "reformated from upstream process" + )) + pixel_aspect = 1 + # NOTE Skipped using instance's resolution full_input_path_single_file = temp_data["full_input_path_single_file"] try: @@ -1161,19 +1174,6 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = output_def.get("width") or None output_height = output_def.get("height") or None - # if nuke baking profile was having set reformat node - reformat_in_baking = bool("reformated" in new_repre["tags"]) - self.log.debug("reformat_in_baking: `{}`".format(reformat_in_baking)) - - if reformat_in_baking: - self.log.debug(( - "Using resolution from input. It is already " - "reformated from baking process" - )) - output_width = output_width or input_width - output_height = output_height or input_height - pixel_aspect = 1 - # Overscal color overscan_color_value = "black" overscan_color = output_def.get("overscan_color") @@ -1202,9 +1202,6 @@ class ExtractReview(pyblish.api.InstancePlugin): output_width = input_width output_height = input_height - letter_box_def = output_def["letter_box"] - letter_box_enabled = letter_box_def["enabled"] - # Make sure input width and height is not an odd number input_width_is_odd = bool(input_width % 2 != 0) input_height_is_odd = bool(input_height % 2 != 0) @@ -1263,6 +1260,9 @@ class ExtractReview(pyblish.api.InstancePlugin): "Output resolution is {}x{}".format(output_width, output_height) ) + letter_box_def = output_def["letter_box"] + letter_box_enabled = letter_box_def["enabled"] + # Skip processing if resolution is same as input's and letterbox is # not set if ( @@ -1347,12 +1347,6 @@ class ExtractReview(pyblish.api.InstancePlugin): # letter_box if letter_box_enabled: - filters.extend([ - "scale={}x{}:flags=lanczos".format( - output_width, output_height - ), - "setsar=1" - ]) filters.extend( self.get_letterbox_filters( letter_box_def, From f753143eec7fb1328838aca2ae12338ae4ac2fd8 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 8 Mar 2022 11:59:22 +0100 Subject: [PATCH 121/302] removing what was already removed --- repos/avalon-unreal-integration | 1 - 1 file changed, 1 deletion(-) delete mode 160000 repos/avalon-unreal-integration diff --git a/repos/avalon-unreal-integration b/repos/avalon-unreal-integration deleted file mode 160000 index 43f6ea9439..0000000000 --- a/repos/avalon-unreal-integration +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 43f6ea943980b29c02a170942b566ae11f2b7080 From 319ec9e8684843e3562e3dc7680bbec35e919f94 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:32:29 +0100 Subject: [PATCH 122/302] removed unused validate method --- openpype/lib/events.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/openpype/lib/events.py b/openpype/lib/events.py index 9cb80b2a6e..f4ad82d919 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -223,18 +223,6 @@ class StoredCallbacks: cls._registered_callbacks.append(callback) return callback - @classmethod - def validate(cls): - invalid_callbacks = [] - for callbacks in cls._registered_callbacks: - for callback in tuple(callbacks): - callback.validate_ref() - if not callback.is_ref_valid: - invalid_callbacks.append(callback) - - for callback in invalid_callbacks: - cls._registered_callbacks.remove(callback) - @classmethod def emit_event(cls, event): invalid_callbacks = [] From 605e8dabb0467826ba160767c7102ebfedf828c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:33:29 +0100 Subject: [PATCH 123/302] modified docstring --- openpype/lib/events.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/lib/events.py b/openpype/lib/events.py index f4ad82d919..2ae3efa55d 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -169,13 +169,14 @@ class EventCallback(object): class Event(object): """Base event object. - Can be used to anything because data are not much specific. Only required - argument is topic which defines why event is happening and may be used for + Can be used for any event because is not specific. Only required argument + is topic which defines why event is happening and may be used for filtering. Arg: topic (str): Identifier of event. data (Any): Data specific for event. Dictionary is recommended. + source (str): Identifier of source. """ _data = {} From 42a185fef9008b58e28d2686ca02013afa719b87 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:35:59 +0100 Subject: [PATCH 124/302] change arguments for after.workfile.save event --- openpype/tools/workfiles/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 87e1492a20..df16498df5 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -859,7 +859,7 @@ class FilesWidget(QtWidgets.QWidget): # Trigger after save events emit_event( "after.workfile.save", - {"filepath": filepath}, + {"filename": work_filename, "workdir_path": self._workdir_path}, source="workfiles.tool" ) From 663d425635d63e89ffd1c893d03b88ef40b6af90 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:39:15 +0100 Subject: [PATCH 125/302] changed topics in workfiles tool to have before and after as last part --- openpype/hosts/maya/api/pipeline.py | 2 +- openpype/tools/workfiles/app.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 4945a9ba56..1dbcfbad6b 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -77,7 +77,7 @@ def install(): register_event_callback("new", on_new) register_event_callback("before.save", on_before_save) register_event_callback("taskChanged", on_task_changed) - register_event_callback("before.workfile.save", before_workfile_save) + register_event_callback("workfile.save.before", before_workfile_save) def _set_project(): diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index df16498df5..63958ac57b 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -824,7 +824,7 @@ class FilesWidget(QtWidgets.QWidget): # Trigger before save event emit_event( - "before.workfile.save", + "workfile.save.before", {"filename": work_filename, "workdir_path": self._workdir_path}, source="workfiles.tool" ) @@ -858,7 +858,7 @@ class FilesWidget(QtWidgets.QWidget): ) # Trigger after save events emit_event( - "after.workfile.save", + "workfile.save.after", {"filename": work_filename, "workdir_path": self._workdir_path}, source="workfiles.tool" ) From 2c75a20b340042c40ff924c6e24f5c7b8042653f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:42:17 +0100 Subject: [PATCH 126/302] added returncode in maya's 'before.save' --- openpype/hosts/maya/api/pipeline.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 1dbcfbad6b..07743b7fc4 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -169,7 +169,10 @@ def _before_scene_save(return_code, client_data): # in order to block the operation. OpenMaya.MScriptUtil.setBool(return_code, True) - emit_event("before.save") + emit_event( + "before.save", + {"return_code": return_code} + ) def uninstall(): From c84abf1faa9606c70d8fc3e877e14e6276f09df1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 12:50:11 +0100 Subject: [PATCH 127/302] raise TypeError when function is not callable --- openpype/lib/events.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/openpype/lib/events.py b/openpype/lib/events.py index 2ae3efa55d..7bec6ee30d 100644 --- a/openpype/lib/events.py +++ b/openpype/lib/events.py @@ -35,7 +35,11 @@ class EventCallback(object): Args: topic(str): Topic which will be listened. func(func): Callback to a topic. + + Raises: + TypeError: When passed function is not a callable object. """ + def __init__(self, topic, func): self._log = None self._topic = topic @@ -61,26 +65,21 @@ class EventCallback(object): elif callable(func): func_ref = weakref.ref(func) else: - func_ref = None - self.log.warning(( + raise TypeError(( "Registered callback is not callable. \"{}\"" ).format(str(func))) - func_name = None - func_path = None - expect_args = False # Collect additional data about function # - name # - path # - if expect argument or not - if func_ref is not None: - func_name = func.__name__ - func_path = os.path.abspath(inspect.getfile(func)) - if hasattr(inspect, "signature"): - sig = inspect.signature(func) - expect_args = len(sig.parameters) > 0 - else: - expect_args = len(inspect.getargspec(func)[0]) > 0 + func_name = func.__name__ + func_path = os.path.abspath(inspect.getfile(func)) + if hasattr(inspect, "signature"): + sig = inspect.signature(func) + expect_args = len(sig.parameters) > 0 + else: + expect_args = len(inspect.getargspec(func)[0]) > 0 self._func_ref = func_ref self._func_name = func_name From 650260309ecf96ef1a10b96fec17a4e272e6e0d5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Mar 2022 13:43:04 +0100 Subject: [PATCH 128/302] OP-2860 - extracted get_fps function to lib --- openpype/lib/vendor_bin_utils.py | 20 ++++++++++++++++++++ openpype/scripts/otio_burnin.py | 20 +------------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 4c2cf93dfa..c94fd2a956 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -130,3 +130,23 @@ def is_oiio_supported(): )) return False return True + + +def get_fps(str_value): + """Returns (str) value of fps from ffprobe frame format (120/1)""" + if str_value == "0/0": + print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".") + return "Unknown" + + items = str_value.split("/") + if len(items) == 1: + fps = float(items[0]) + + elif len(items) == 2: + fps = float(items[0]) / float(items[1]) + + # Check if fps is integer or float number + if int(fps) == fps: + fps = int(fps) + + return str(fps) diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index abf69645b7..874c08064a 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -6,6 +6,7 @@ import platform import json import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins import openpype.lib +from openpype.lib.vendor_bin_utils import get_fps ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg") @@ -50,25 +51,6 @@ def _get_ffprobe_data(source): return json.loads(out) -def get_fps(str_value): - if str_value == "0/0": - print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".") - return "Unknown" - - items = str_value.split("/") - if len(items) == 1: - fps = float(items[0]) - - elif len(items) == 2: - fps = float(items[0]) / float(items[1]) - - # Check if fps is integer or float number - if int(fps) == fps: - fps = int(fps) - - return str(fps) - - def _prores_codec_args(stream_data, source_ffmpeg_cmd): output = [] From 5e84f4566ac97b3cb48d99f435b0e894457c3e8a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Mar 2022 13:50:42 +0100 Subject: [PATCH 129/302] OP-2860 - added possibility to get number of frames from video file with ffprobe Previously wrong hardcoded value was used. This implementation needs to be monitored for weird format of published video files. --- .../publish/collect_published_files.py | 71 ++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index abad14106f..8b21842635 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -10,14 +10,18 @@ Provides: import os import clique import tempfile +import math + from avalon import io import pyblish.api -from openpype.lib import prepare_template_data +from openpype.lib import prepare_template_data, get_asset, ffprobe_streams +from openpype.lib.vendor_bin_utils import get_fps from openpype.lib.plugin_tools import ( parse_json, get_subset_name_with_asset_doc ) + class CollectPublishedFiles(pyblish.api.ContextPlugin): """ This collector will try to find json files in provided @@ -49,10 +53,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): self.log.info("task_sub:: {}".format(task_subfolders)) asset_name = context.data["asset"] - asset_doc = io.find_one({ - "type": "asset", - "name": asset_name - }) + asset_doc = get_asset() task_name = context.data["task"] task_type = context.data["taskType"] project_name = context.data["project_name"] @@ -97,11 +98,26 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): instance.data["frameEnd"] = \ instance.data["representations"][0]["frameEnd"] else: - instance.data["frameStart"] = 0 - instance.data["frameEnd"] = 1 + frame_start = asset_doc["data"]["frameStart"] + instance.data["frameStart"] = frame_start + instance.data["frameEnd"] = asset_doc["data"]["frameEnd"] instance.data["representations"] = self._get_single_repre( task_dir, task_data["files"], tags ) + file_url = os.path.join(task_dir, task_data["files"][0]) + duration = self._get_duration(file_url) + if duration: + try: + frame_end = int(frame_start) + math.ceil(duration) + instance.data["frameEnd"] = math.ceil(frame_end) + self.log.debug("frameEnd:: {}".format( + instance.data["frameEnd"])) + except ValueError: + self.log.warning("Unable to count frames " + "duration {}".format(duration)) + + instance.data["handleStart"] = asset_doc["data"]["handleStart"] + instance.data["handleEnd"] = asset_doc["data"]["handleEnd"] self.log.info("instance.data:: {}".format(instance.data)) @@ -127,7 +143,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): return [repre_data] def _process_sequence(self, files, task_dir, tags): - """Prepare reprentations for sequence of files.""" + """Prepare representation for sequence of files.""" collections, remainder = clique.assemble(files) assert len(collections) == 1, \ "Too many collections in {}".format(files) @@ -188,6 +204,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): msg = "No family found for combination of " +\ "task_type: {}, is_sequence:{}, extension: {}".format( task_type, is_sequence, extension) + found_family = "render" assert found_family, msg return (found_family, @@ -243,3 +260,41 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): return version[0].get("version") or 0 else: return 0 + + def _get_duration(self, file_url): + """Return duration in frames""" + try: + streams = ffprobe_streams(file_url, self.log) + except Exception as exc: + raise AssertionError(( + "FFprobe couldn't read information about input file: \"{}\"." + " Error message: {}" + ).format(file_url, str(exc))) + + first_video_stream = None + for stream in streams: + if "width" in stream and "height" in stream: + first_video_stream = stream + break + + if first_video_stream: + nb_frames = stream.get("nb_frames") + if nb_frames: + try: + return int(nb_frames) + except ValueError: + self.log.warning( + "nb_frames {} not convertible".format(nb_frames)) + + duration = stream.get("duration") + frame_rate = get_fps(stream.get("r_frame_rate", '0/0')) + self.log.debu("duration:: {} frame_rate:: {}".format( + duration, frame_rate)) + try: + return float(duration) * float(frame_rate) + except ValueError: + self.log.warning( + "{} or {} cannot be converted".format(duration, + frame_rate)) + + self.log.warning("Cannot get number of frames") From 365901656f5ef395d394b051a2483c39a68d0cf2 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 8 Mar 2022 13:50:59 +0100 Subject: [PATCH 130/302] redundant code --- openpype/plugins/publish/extract_review.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index fb0e553a9e..d2d361228a 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -19,7 +19,6 @@ from openpype.lib import ( should_convert_for_ffmpeg, convert_for_ffmpeg, - get_transcode_temp_directory, get_transcode_temp_directory ) import speedcopy From 2d9cecd1ae4cd9bfbd8ac40ffe0d1395591d097d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 15:20:02 +0100 Subject: [PATCH 131/302] replace widht with width --- openpype/plugins/publish/extract_review.py | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index d2d361228a..96a90a63b7 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1000,11 +1000,11 @@ class ExtractReview(pyblish.api.InstancePlugin): if need_mask and not pillar: if fill_color_alpha > 0: top_box = ( - "drawbox=0:0:{widht}:round(" - "({height}-({widht}*(1/{ratio})))/2)" + "drawbox=0:0:{width}:round(" + "({height}-({width}*(1/{ratio})))/2)" ":t=fill:c={color}@{alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, color=fill_color_hex, @@ -1013,12 +1013,12 @@ class ExtractReview(pyblish.api.InstancePlugin): bottom_box = ( "drawbox=0:{height}-round(" - "({height}-({widht}*(1/{ratio})))/2)" - ":{widht}:round(({height}-({widht}" + "({height}-({width}*(1/{ratio})))/2)" + ":{width}:round(({height}-({width}" "*(1/{ratio})))/2):t=fill:" "c={color}@{alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, color=fill_color_hex, @@ -1028,11 +1028,11 @@ class ExtractReview(pyblish.api.InstancePlugin): if line_color_alpha > 0 and line_thickness > 0: top_line = ( - "drawbox=0:round(({height}-({widht}" - "*(1/{ratio})))/2)-{l_thick}:{widht}:{l_thick}:" + "drawbox=0:round(({height}-({width}" + "*(1/{ratio})))/2)-{l_thick}:{width}:{l_thick}:" "t=fill:c={l_color}@{l_alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, l_thick=line_thickness, @@ -1040,11 +1040,11 @@ class ExtractReview(pyblish.api.InstancePlugin): l_alpha=line_color_alpha ) bottom_line = ( - "drawbox=0:{height}-round(({height}-({widht}" + "drawbox=0:{height}-round(({height}-({width}" "*(1/{ratio})))/2)" - ":{widht}:{l_thick}:t=fill:c={l_color}@{l_alpha}" + ":{width}:{l_thick}:t=fill:c={l_color}@{l_alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, l_thick=line_thickness, @@ -1056,10 +1056,10 @@ class ExtractReview(pyblish.api.InstancePlugin): elif need_mask and pillar: if fill_color_alpha > 0: left_box = ( - "drawbox=0:0:round(({widht}-({height}" + "drawbox=0:0:round(({width}-({height}" "*{ratio}))/2):{height}:t=fill:c={color}@{alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, color=fill_color_hex, @@ -1067,11 +1067,11 @@ class ExtractReview(pyblish.api.InstancePlugin): ) right_box = ( - "drawbox={widht}-round(({widht}-({height}*{ratio}))/2))" - ":0:round(({widht}-({height}*{ratio}))/2):{height}" + "drawbox={width}-round(({width}-({height}*{ratio}))/2))" + ":0:round(({width}-({height}*{ratio}))/2):{height}" ":t=fill:c={color}@{alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, color=fill_color_hex, @@ -1081,10 +1081,10 @@ class ExtractReview(pyblish.api.InstancePlugin): if line_color_alpha > 0 and line_thickness > 0: left_line = ( - "drawbox=round(({widht}-({height}*{ratio}))/2)" + "drawbox=round(({width}-({height}*{ratio}))/2)" ":0:{l_thick}:{height}:t=fill:c={l_color}@{l_alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, l_thick=line_thickness, @@ -1093,10 +1093,10 @@ class ExtractReview(pyblish.api.InstancePlugin): ) right_line = ( - "drawbox={widht}-round(({widht}-({height}*{ratio}))/2))" + "drawbox={width}-round(({width}-({height}*{ratio}))/2))" ":0:{l_thick}:{height}:t=fill:c={l_color}@{l_alpha}" ).format( - widht=output_width, + width=output_width, height=output_height, ratio=ratio, l_thick=line_thickness, From 404232c37a498af5712b1cb8683fd5b8c883121e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 15:21:07 +0100 Subject: [PATCH 132/302] skip need mask checks --- openpype/plugins/publish/extract_review.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 96a90a63b7..ce7c06bd3c 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -996,8 +996,10 @@ class ExtractReview(pyblish.api.InstancePlugin): output_ratio = output_width / output_height pillar = output_ratio > ratio need_mask = format(output_ratio, ".3f") != format(ratio, ".3f") + if not need_mask: + return [] - if need_mask and not pillar: + if not pillar: if fill_color_alpha > 0: top_box = ( "drawbox=0:0:{width}:round(" @@ -1053,7 +1055,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ) output.extend([top_line, bottom_line]) - elif need_mask and pillar: + else: if fill_color_alpha > 0: left_box = ( "drawbox=0:0:round(({width}-({height}" @@ -1105,13 +1107,6 @@ class ExtractReview(pyblish.api.InstancePlugin): ) output.extend([left_line, right_line]) - else: - raise ValueError(( - "Letterbox not working: ratio set \"{}\", " - "Image ratio\"{}\"").format( - format(ratio, ".3f"), format(output_ratio, ".3f")) - ) - return output def rescaling_filters(self, temp_data, output_def, new_repre): From 70e158792bac4d9d638e1c2838ef335213aeee64 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 15:22:34 +0100 Subject: [PATCH 133/302] fixed pillar boxes --- openpype/plugins/publish/extract_review.py | 45 +++++++++++++--------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index ce7c06bd3c..bec1f75425 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -993,7 +993,10 @@ class ExtractReview(pyblish.api.InstancePlugin): line_color_alpha = float(l_alpha) / 255 # test ratios and define if pillar or letter boxes - output_ratio = output_width / output_height + output_ratio = float(output_width) / float(output_height) + self.log.debug("Output ratio: {} LetterBox ratio: {}".format( + output_ratio, ratio + )) pillar = output_ratio > ratio need_mask = format(output_ratio, ".3f") != format(ratio, ".3f") if not need_mask: @@ -1002,8 +1005,8 @@ class ExtractReview(pyblish.api.InstancePlugin): if not pillar: if fill_color_alpha > 0: top_box = ( - "drawbox=0:0:{width}:round(" - "({height}-({width}*(1/{ratio})))/2)" + "drawbox=0:0:{width}" + ":round(({height}-({width}/{ratio}))/2)" ":t=fill:c={color}@{alpha}" ).format( width=output_width, @@ -1014,11 +1017,11 @@ class ExtractReview(pyblish.api.InstancePlugin): ) bottom_box = ( - "drawbox=0:{height}-round(" - "({height}-({width}*(1/{ratio})))/2)" - ":{width}:round(({height}-({width}" - "*(1/{ratio})))/2):t=fill:" - "c={color}@{alpha}" + "drawbox=0" + ":{height}-round(({height}-({width}/{ratio}))/2)" + ":{width}" + ":round(({height}-({width}/{ratio}))/2)" + ":t=fill:c={color}@{alpha}" ).format( width=output_width, height=output_height, @@ -1030,9 +1033,9 @@ class ExtractReview(pyblish.api.InstancePlugin): if line_color_alpha > 0 and line_thickness > 0: top_line = ( - "drawbox=0:round(({height}-({width}" - "*(1/{ratio})))/2)-{l_thick}:{width}:{l_thick}:" - "t=fill:c={l_color}@{l_alpha}" + "drawbox=0" + ":round(({height}-({width}/{ratio}))/2)-{l_thick}" + ":{width}:{l_thick}:t=fill:c={l_color}@{l_alpha}" ).format( width=output_width, height=output_height, @@ -1042,8 +1045,8 @@ class ExtractReview(pyblish.api.InstancePlugin): l_alpha=line_color_alpha ) bottom_line = ( - "drawbox=0:{height}-round(({height}-({width}" - "*(1/{ratio})))/2)" + "drawbox=0" + ":{height}-round(({height}-({width}/{ratio}))/2)" ":{width}:{l_thick}:t=fill:c={l_color}@{l_alpha}" ).format( width=output_width, @@ -1058,8 +1061,10 @@ class ExtractReview(pyblish.api.InstancePlugin): else: if fill_color_alpha > 0: left_box = ( - "drawbox=0:0:round(({width}-({height}" - "*{ratio}))/2):{height}:t=fill:c={color}@{alpha}" + "drawbox=0:0" + ":round(({width}-({height}*{ratio}))/2)" + ":{height}" + ":t=fill:c={color}@{alpha}" ).format( width=output_width, height=output_height, @@ -1069,8 +1074,11 @@ class ExtractReview(pyblish.api.InstancePlugin): ) right_box = ( - "drawbox={width}-round(({width}-({height}*{ratio}))/2))" - ":0:round(({width}-({height}*{ratio}))/2):{height}" + "drawbox=" + "{width}-round(({width}-({height}*{ratio}))/2)" + ":0" + ":round(({width}-({height}*{ratio}))/2)" + ":{height}" ":t=fill:c={color}@{alpha}" ).format( width=output_width, @@ -1095,7 +1103,7 @@ class ExtractReview(pyblish.api.InstancePlugin): ) right_line = ( - "drawbox={width}-round(({width}-({height}*{ratio}))/2))" + "drawbox={width}-round(({width}-({height}*{ratio}))/2)" ":0:{l_thick}:{height}:t=fill:c={l_color}@{l_alpha}" ).format( width=output_width, @@ -1300,7 +1308,6 @@ class ExtractReview(pyblish.api.InstancePlugin): "scale_factor_by_height: `{}`".format(scale_factor_by_height) ) - # scaling none square pixels and 1920 width if ( input_height != output_height From 413e03cae155de3dbe9e4dce2c4d30ec46d237a8 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 15:54:16 +0100 Subject: [PATCH 134/302] moved 'create_hard_link' to path_tools --- openpype/lib/__init__.py | 4 ++-- openpype/lib/path_tools.py | 35 ++++++++++++++++++++++++++++++++ openpype/lib/vendor_bin_utils.py | 35 -------------------------------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index c4097086e0..34b217f690 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -17,7 +17,6 @@ site.addsitedir(python_version_dir) from .vendor_bin_utils import ( - create_hard_link, find_executable, get_vendor_bin_path, get_oiio_tools_path, @@ -160,6 +159,7 @@ from .plugin_tools import ( ) from .path_tools import ( + create_hard_link, version_up, get_version_from_path, get_last_version_from_path, @@ -210,7 +210,6 @@ __all__ = [ "get_paths_from_environ", "get_global_environments", - "create_hard_link", "get_vendor_bin_path", "get_oiio_tools_path", "get_ffmpeg_tool_path", @@ -293,6 +292,7 @@ __all__ = [ "get_unique_layer_name", "get_background_layers", + "create_hard_link", "version_up", "get_version_from_path", "get_last_version_from_path", diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index 71fc0fe25c..c36e45c51f 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -13,6 +13,41 @@ from .profiles_filtering import filter_profiles log = logging.getLogger(__name__) +def create_hard_link(src_path, dst_path): + """Create hardlink of file. + + Args: + src_path(str): Full path to a file which is used as source for + hardlink. + dst_path(str): Full path to a file where a link of source will be + added. + """ + # Use `os.link` if is available + # - should be for all platforms with newer python versions + if hasattr(os, "link"): + os.link(src_path, dst_path) + return + + # Windows implementation of hardlinks + # - used in Python 2 + if platform.system().lower() == "windows": + import ctypes + from ctypes.wintypes import BOOL + CreateHardLink = ctypes.windll.kernel32.CreateHardLinkW + CreateHardLink.argtypes = [ + ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_void_p + ] + CreateHardLink.restype = BOOL + + res = CreateHardLink(dst_path, src_path, None) + if res == 0: + raise ctypes.WinError() + # Raises not implemented error if gets here + raise NotImplementedError( + "Implementation of hardlink for current environment is missing." + ) + + def _rreplace(s, a, b, n=1): """Replace a with b in string s from right side n times.""" return b.join(s.rsplit(a, n)) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 4a62da8f0c..4be016f656 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -86,41 +86,6 @@ def find_executable(executable): return None -def create_hard_link(src_path, dst_path): - """Create hardlink of file. - - Args: - src_path(str): Full path to a file which is used as source for - hardlink. - dst_path(str): Full path to a file where a link of source will be - added. - """ - # Use `os.link` if is available - # - should be for all platforms with newer python versions - if hasattr(os, "link"): - os.link(src_path, dst_path) - return - - # Windows implementation of hardlinks - # - used in Python 2 - if platform.system().lower() == "windows": - import ctypes - from ctypes.wintypes import BOOL - CreateHardLink = ctypes.windll.kernel32.CreateHardLinkW - CreateHardLink.argtypes = [ - ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_void_p - ] - CreateHardLink.restype = BOOL - - res = CreateHardLink(dst_path, src_path, None) - if res == 0: - raise ctypes.WinError() - # Raises not implemented error if gets here - raise NotImplementedError( - "Implementation of hardlink for current environment is missing." - ) - - def get_vendor_bin_path(bin_app): """Path to OpenPype vendorized binaries. From 295002d7543f9ac47896bb3c4cb78dc7e1691b08 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 15:54:58 +0100 Subject: [PATCH 135/302] added missing platform --- openpype/lib/path_tools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index c36e45c51f..3a9f835272 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -4,6 +4,7 @@ import abc import json import logging import six +import platform from openpype.settings import get_project_settings From 8c38c4f332c9b188cf1a1abf00a525fc7648ef03 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Mar 2022 16:34:22 +0100 Subject: [PATCH 136/302] OP-2877 - use same value for burnin user, version and representation author --- openpype/modules/ftrack/plugins/publish/collect_username.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/plugins/publish/collect_username.py b/openpype/modules/ftrack/plugins/publish/collect_username.py index 84d7f60a3f..a9b746ea51 100644 --- a/openpype/modules/ftrack/plugins/publish/collect_username.py +++ b/openpype/modules/ftrack/plugins/publish/collect_username.py @@ -23,8 +23,11 @@ class CollectUsername(pyblish.api.ContextPlugin): Expects "pype.club" user created on Ftrack and FTRACK_BOT_API_KEY env var set up. + Resets `context.data["user"] to correctly populate `version.author` and + `representation.context.username` + """ - order = pyblish.api.CollectorOrder - 0.488 + order = pyblish.api.CollectorOrder + 0.0015 label = "Collect ftrack username" hosts = ["webpublisher", "photoshop"] targets = ["remotepublish", "filespublish", "tvpaint_worker"] @@ -65,3 +68,4 @@ class CollectUsername(pyblish.api.ContextPlugin): if '@' in burnin_name: burnin_name = burnin_name[:burnin_name.index('@')] os.environ["WEBPUBLISH_OPENPYPE_USERNAME"] = burnin_name + context.data["user"] = burnin_name From e1d07b2b13d7af7c604c5f5293aea2b8c7639c2e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 8 Mar 2022 17:11:13 +0100 Subject: [PATCH 137/302] nuke: adding input resolution of input video file --- .../plugins/publish/extract_review_slate.py | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index 7002168cdb..948cee0639 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -14,7 +14,7 @@ class ExtractReviewSlate(openpype.api.Extractor): families = ["slate", "review"] match = pyblish.api.Subset - hosts = ["nuke", "maya", "shell"] + hosts = ["nuke", "shell"] optional = True def process(self, instance): @@ -59,13 +59,44 @@ class ExtractReviewSlate(openpype.api.Extractor): if "slate-frame" not in p_tags: continue + # get repre file + stagingdir = repre["stagingDir"] + input_file = "{0}".format(repre["files"]) + input_path = os.path.join( + os.path.normpath(stagingdir), repre["files"]) + self.log.debug("__ input_path: {}".format(input_path)) + + video_streams = openpype.lib.ffprobe_streams( + input_path, self.log + ) + + # Try to find first stream with defined 'width' and 'height' + # - this is to avoid order of streams where audio can be as first + # - there may be a better way (checking `codec_type`?) + input_width = None + input_height = None + for stream in video_streams: + if "width" in stream and "height" in stream: + input_width = int(stream["width"]) + input_height = int(stream["height"]) + break + + # Raise exception of any stream didn't define input resolution + if input_width is None: + raise AssertionError(( + "FFprobe couldn't read resolution from input file: \"{}\"" + ).format(input_path)) + # values are set in ExtractReview if use_legacy_code: to_width = inst_data["reviewToWidth"] to_height = inst_data["reviewToHeight"] else: - to_width = repre["resolutionWidth"] - to_height = repre["resolutionHeight"] + to_width = input_width + to_height = input_height + + self.log.debug("to_width: `{}`".format(to_width)) + self.log.debug("to_height: `{}`".format(to_height)) # defining image ratios resolution_ratio = ( @@ -94,15 +125,9 @@ class ExtractReviewSlate(openpype.api.Extractor): _remove_at_end = [] - stagingdir = repre["stagingDir"] - input_file = "{0}".format(repre["files"]) - ext = os.path.splitext(input_file)[1] output_file = input_file.replace(ext, "") + suffix + ext - input_path = os.path.join( - os.path.normpath(stagingdir), repre["files"]) - self.log.debug("__ input_path: {}".format(input_path)) _remove_at_end.append(input_path) output_path = os.path.join( From 7600590f7cb78db7f0e008337e2b3d77609cbb4c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 17:31:16 +0100 Subject: [PATCH 138/302] moved avalon creators and added legacy prefix --- openpype/pipeline/__init__.py | 13 +- openpype/pipeline/create/__init__.py | 10 +- openpype/pipeline/create/legacy_create.py | 156 ++++++++++++++++++++++ 3 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 openpype/pipeline/create/legacy_create.py diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e968df4011..9ac7d15d5b 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -4,7 +4,12 @@ from .create import ( BaseCreator, Creator, AutoCreator, - CreatedInstance + CreatedInstance, + + CreatorError, + + LegacyCreator, + legacy_create, ) from .publish import ( @@ -22,6 +27,12 @@ __all__ = ( "AutoCreator", "CreatedInstance", + "CreatorError", + + # Legacy creation + "LegacyCreator", + "legacy_create", + "PublishValidationError", "KnownPublishError", "OpenPypePyblishPluginMixin" diff --git a/openpype/pipeline/create/__init__.py b/openpype/pipeline/create/__init__.py index 948b719851..9571f56b8f 100644 --- a/openpype/pipeline/create/__init__.py +++ b/openpype/pipeline/create/__init__.py @@ -14,6 +14,11 @@ from .context import ( CreateContext ) +from .legacy_create import ( + LegacyCreator, + legacy_create, +) + __all__ = ( "SUBSET_NAME_ALLOWED_SYMBOLS", @@ -25,5 +30,8 @@ __all__ = ( "AutoCreator", "CreatedInstance", - "CreateContext" + "CreateContext", + + "LegacyCreator", + "legacy_create", ) diff --git a/openpype/pipeline/create/legacy_create.py b/openpype/pipeline/create/legacy_create.py new file mode 100644 index 0000000000..d05cdff689 --- /dev/null +++ b/openpype/pipeline/create/legacy_create.py @@ -0,0 +1,156 @@ +"""Create workflow moved from avalon-core repository. + +Renamed classes and functions +- 'Creator' -> 'LegacyCreator' +- 'create' -> 'legacy_create' +""" + +import logging +import collections + +from openpype.lib import get_subset_name + + +class LegacyCreator(object): + """Determine how assets are created""" + label = None + family = None + defaults = None + maintain_selection = True + + dynamic_subset_keys = [] + + log = logging.getLogger("LegacyCreator") + + def __init__(self, name, asset, options=None, data=None): + self.name = name # For backwards compatibility + self.options = options + + # Default data + self.data = collections.OrderedDict() + self.data["id"] = "pyblish.avalon.instance" + self.data["family"] = self.family + self.data["asset"] = asset + self.data["subset"] = name + self.data["active"] = True + + self.data.update(data or {}) + + def process(self): + pass + + @classmethod + def get_dynamic_data( + cls, variant, task_name, asset_id, project_name, host_name + ): + """Return dynamic data for current Creator plugin. + + By default return keys from `dynamic_subset_keys` attribute as mapping + to keep formatted template unchanged. + + ``` + dynamic_subset_keys = ["my_key"] + --- + output = { + "my_key": "{my_key}" + } + ``` + + Dynamic keys may override default Creator keys (family, task, asset, + ...) but do it wisely if you need. + + All of keys will be converted into 3 variants unchanged, capitalized + and all upper letters. Because of that are all keys lowered. + + This method can be modified to prefill some values just keep in mind it + is class method. + + Returns: + dict: Fill data for subset name template. + """ + dynamic_data = {} + for key in cls.dynamic_subset_keys: + key = key.lower() + dynamic_data[key] = "{" + key + "}" + return dynamic_data + + @classmethod + def get_subset_name( + cls, variant, task_name, asset_id, project_name, host_name=None + ): + """Return subset name created with entered arguments. + + Logic extracted from Creator tool. This method should give ability + to get subset name without the tool. + + TODO: Maybe change `variant` variable. + + By default is output concatenated family with user text. + + Args: + variant (str): What is entered by user in creator tool. + task_name (str): Context's task name. + asset_id (ObjectId): Mongo ID of context's asset. + project_name (str): Context's project name. + host_name (str): Name of host. + + Returns: + str: Formatted subset name with entered arguments. Should match + config's logic. + """ + + dynamic_data = cls.get_dynamic_data( + variant, task_name, asset_id, project_name, host_name + ) + + return get_subset_name( + cls.family, + variant, + task_name, + asset_id, + project_name, + host_name, + dynamic_data=dynamic_data + ) + + +def legacy_create(Creator, name, asset, options=None, data=None): + """Create a new instance + + Associate nodes with a subset and family. These nodes are later + validated, according to their `family`, and integrated into the + shared environment, relative their `subset`. + + Data relative each family, along with default data, are imprinted + into the resulting objectSet. This data is later used by extractors + and finally asset browsers to help identify the origin of the asset. + + Arguments: + Creator (Creator): Class of creator + name (str): Name of subset + asset (str): Name of asset + options (dict, optional): Additional options from GUI + data (dict, optional): Additional data from GUI + + Raises: + NameError on `subset` already exists + KeyError on invalid dynamic property + RuntimeError on host error + + Returns: + Name of instance + + """ + from avalon.api import registered_host + host = registered_host() + plugin = Creator(name, asset, options, data) + + if plugin.maintain_selection is True: + with host.maintained_selection(): + print("Running %s with maintained selection" % plugin) + instance = plugin.process() + return instance + + print("Running %s" % plugin) + instance = plugin.process() + return instance From 4d0d25534647446142a3b2a7dfbb93e6691b979c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 8 Mar 2022 17:36:26 +0100 Subject: [PATCH 139/302] Fix for new publish validations for Harmony --- .../hosts/harmony/plugins/publish/validate_instances.py | 1 - .../harmony/plugins/publish/validate_scene_settings.py | 8 +++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/harmony/plugins/publish/validate_instances.py b/openpype/hosts/harmony/plugins/publish/validate_instances.py index 03b6e5db75..373ef94cc3 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_instances.py +++ b/openpype/hosts/harmony/plugins/publish/validate_instances.py @@ -1,6 +1,5 @@ import os -from avalon import harmony import pyblish.api import openpype.api from openpype.pipeline import PublishXmlValidationError diff --git a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py index 19a9d46026..4c3a6c4465 100644 --- a/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py +++ b/openpype/hosts/harmony/plugins/publish/validate_scene_settings.py @@ -105,11 +105,9 @@ class ValidateSceneSettings(pyblish.api.InstancePlugin): invalid_keys = set() for key, value in expected_settings.items(): if value != current_settings[key]: - invalid_settings.append({ - "name": key, - "expected": value, - "current": current_settings[key] - }) + invalid_settings.append( + "{} expected: {} found: {}".format(key, value, + current_settings[key])) invalid_keys.add(key) if ((expected_settings["handleStart"] From d4f177f7bc4b6398240504c665777731b0dcb01f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 8 Mar 2022 17:39:50 +0100 Subject: [PATCH 140/302] use moved create functions in hosts --- openpype/__init__.py | 3 +- openpype/api.py | 6 -- openpype/hosts/aftereffects/api/pipeline.py | 5 +- .../plugins/create/create_render.py | 7 +- openpype/hosts/blender/api/pipeline.py | 5 +- openpype/hosts/blender/api/plugin.py | 4 +- .../blender/plugins/load/load_layout_blend.py | 3 +- .../blender/plugins/load/load_layout_json.py | 4 +- .../hosts/blender/plugins/load/load_rig.py | 3 +- openpype/hosts/flame/api/pipeline.py | 5 +- openpype/hosts/flame/api/plugin.py | 3 +- openpype/hosts/fusion/api/pipeline.py | 5 +- .../fusion/plugins/create/create_exr_saver.py | 4 +- openpype/hosts/harmony/api/pipeline.py | 5 +- openpype/hosts/harmony/api/plugin.py | 5 +- openpype/hosts/hiero/api/pipeline.py | 5 +- openpype/hosts/hiero/api/plugin.py | 9 ++- openpype/hosts/houdini/api/pipeline.py | 3 +- openpype/hosts/houdini/api/plugin.py | 9 +-- openpype/hosts/maya/api/pipeline.py | 6 +- openpype/hosts/maya/api/plugin.py | 4 +- .../maya/plugins/create/create_render.py | 2 +- .../maya/plugins/create/create_vrayscene.py | 2 +- .../hosts/maya/plugins/load/load_reference.py | 3 +- openpype/hosts/nuke/api/pipeline.py | 5 +- openpype/hosts/nuke/api/plugin.py | 8 +-- openpype/hosts/photoshop/api/__init__.py | 3 +- openpype/hosts/photoshop/api/pipeline.py | 6 +- openpype/hosts/photoshop/api/plugin.py | 34 ---------- .../photoshop/plugins/create/create_image.py | 4 +- openpype/hosts/resolve/api/pipeline.py | 5 +- openpype/hosts/resolve/api/plugin.py | 3 +- openpype/hosts/tvpaint/api/pipeline.py | 5 +- openpype/hosts/tvpaint/api/plugin.py | 4 +- .../plugins/create/create_render_layer.py | 3 +- .../plugins/create/create_render_pass.py | 2 +- openpype/hosts/unreal/api/pipeline.py | 7 +- openpype/hosts/unreal/api/plugin.py | 4 +- openpype/hosts/webpublisher/api/__init__.py | 5 +- openpype/lib/plugin_tools.py | 2 +- openpype/plugin.py | 67 ------------------- openpype/tests/test_avalon_plugin_presets.py | 7 +- openpype/tools/creator/model.py | 3 +- openpype/tools/creator/window.py | 13 ++-- .../widgets/widget_family.py | 9 +-- 45 files changed, 111 insertions(+), 198 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 11b563ebfe..c41afaa47d 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -5,6 +5,7 @@ import platform import functools import logging +from openpype.pipeline import LegacyCreator from .settings import get_project_settings from .lib import ( Anatomy, @@ -113,7 +114,7 @@ def install(): pyblish.register_plugin_path(path) avalon.register_plugin_path(avalon.Loader, path) - avalon.register_plugin_path(avalon.Creator, path) + avalon.register_plugin_path(LegacyCreator, path) avalon.register_plugin_path(avalon.InventoryAction, path) # apply monkey patched discover to original one diff --git a/openpype/api.py b/openpype/api.py index 51854492ab..b692b36065 100644 --- a/openpype/api.py +++ b/openpype/api.py @@ -45,9 +45,6 @@ from .lib.avalon_context import ( from . import resources from .plugin import ( - PypeCreatorMixin, - Creator, - Extractor, ValidatePipelineOrder, @@ -89,9 +86,6 @@ __all__ = [ # Resources "resources", - # Pype creator mixin - "PypeCreatorMixin", - "Creator", # plugin classes "Extractor", # ordering diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 94f1e3d105..ef56e96155 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -9,6 +9,7 @@ from avalon import io, pipeline from openpype import lib from openpype.api import Logger +from openpype.pipeline import LegacyCreator import openpype.hosts.aftereffects from .launch_logic import get_stub @@ -66,7 +67,7 @@ def install(): pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) pyblish.api.register_callback( @@ -79,7 +80,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) def on_pyblish_instance_toggled(instance, old_value, new_value): diff --git a/openpype/hosts/aftereffects/plugins/create/create_render.py b/openpype/hosts/aftereffects/plugins/create/create_render.py index 8dfc85cdc8..41efb4b0ba 100644 --- a/openpype/hosts/aftereffects/plugins/create/create_render.py +++ b/openpype/hosts/aftereffects/plugins/create/create_render.py @@ -1,13 +1,12 @@ -from avalon.api import CreatorError - -import openpype.api +from openpype.pipeline import create +from openpype.pipeline import CreatorError from openpype.hosts.aftereffects.api import ( get_stub, list_instances ) -class CreateRender(openpype.api.Creator): +class CreateRender(create.LegacyCreator): """Render folder for publish. Creates subsets in format 'familyTaskSubsetname', diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 6da0ba3dcb..1c9820ff22 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -14,6 +14,7 @@ import avalon.api from avalon import io, schema from avalon.pipeline import AVALON_CONTAINER_ID +from openpype.pipeline import LegacyCreator from openpype.api import Logger import openpype.hosts.blender @@ -46,7 +47,7 @@ def install(): pyblish.api.register_plugin_path(str(PUBLISH_PATH)) avalon.api.register_plugin_path(avalon.api.Loader, str(LOAD_PATH)) - avalon.api.register_plugin_path(avalon.api.Creator, str(CREATE_PATH)) + avalon.api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) lib.append_user_scripts() @@ -67,7 +68,7 @@ def uninstall(): pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) avalon.api.deregister_plugin_path(avalon.api.Loader, str(LOAD_PATH)) - avalon.api.deregister_plugin_path(avalon.api.Creator, str(CREATE_PATH)) + avalon.api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) if not IS_HEADLESS: ops.unregister() diff --git a/openpype/hosts/blender/api/plugin.py b/openpype/hosts/blender/api/plugin.py index 8c9ab9a27f..20d1e4c8db 100644 --- a/openpype/hosts/blender/api/plugin.py +++ b/openpype/hosts/blender/api/plugin.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy import avalon.api -from openpype.api import PypeCreatorMixin +from openpype.pipeline import LegacyCreator from .pipeline import AVALON_CONTAINERS from .ops import ( MainThreadItem, @@ -129,7 +129,7 @@ def deselect_all(): bpy.context.view_layer.objects.active = active -class Creator(PypeCreatorMixin, avalon.api.Creator): +class Creator(LegacyCreator): """Base class for Creator plug-ins.""" defaults = ['Main'] diff --git a/openpype/hosts/blender/plugins/load/load_layout_blend.py b/openpype/hosts/blender/plugins/load/load_layout_blend.py index 8029c38b4a..7f8ae610c6 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_blend.py +++ b/openpype/hosts/blender/plugins/load/load_layout_blend.py @@ -8,6 +8,7 @@ import bpy from avalon import api from openpype import lib +from openpype.pipeline import legacy_create from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -159,7 +160,7 @@ class BlendLayoutLoader(plugin.AssetLoader): raise ValueError("Creator plugin \"CreateAnimation\" was " "not found.") - api.create( + legacy_create( creator_plugin, name=local_obj.name.split(':')[-1] + "_animation", asset=asset, diff --git a/openpype/hosts/blender/plugins/load/load_layout_json.py b/openpype/hosts/blender/plugins/load/load_layout_json.py index 0a5bdeecaa..91817deb60 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_json.py +++ b/openpype/hosts/blender/plugins/load/load_layout_json.py @@ -8,7 +8,7 @@ from typing import Dict, Optional import bpy from avalon import api -from openpype import lib +from openpype.pipeline import legacy_create from openpype.hosts.blender.api.pipeline import ( AVALON_INSTANCES, AVALON_CONTAINERS, @@ -118,7 +118,7 @@ class JsonLayoutLoader(plugin.AssetLoader): # raise ValueError("Creator plugin \"CreateCamera\" was " # "not found.") - # api.create( + # legacy_create( # creator_plugin, # name="camera", # # name=f"{unique_number}_{subset}_animation", diff --git a/openpype/hosts/blender/plugins/load/load_rig.py b/openpype/hosts/blender/plugins/load/load_rig.py index eb6d273a51..eacabd3447 100644 --- a/openpype/hosts/blender/plugins/load/load_rig.py +++ b/openpype/hosts/blender/plugins/load/load_rig.py @@ -9,6 +9,7 @@ import bpy from avalon import api from avalon.blender import lib as avalon_lib from openpype import lib +from openpype.pipeline import legacy_create from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -248,7 +249,7 @@ class BlendRigLoader(plugin.AssetLoader): animation_asset = options.get('animation_asset') - api.create( + legacy_create( creator_plugin, name=namespace + "_animation", # name=f"{unique_number}_{subset}_animation", diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index af071439ef..f802cf160b 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -7,6 +7,7 @@ from avalon import api as avalon from avalon.pipeline import AVALON_CONTAINER_ID from pyblish import api as pyblish from openpype.api import Logger +from openpype.pipeline import LegacyCreator from .lib import ( set_segment_data_marker, set_publish_attribute, @@ -33,7 +34,7 @@ def install(): pyblish.register_host("flame") pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) log.info("OpenPype Flame plug-ins registred ...") @@ -48,7 +49,7 @@ def uninstall(): log.info("Deregistering Flame plug-ins..") pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) # register callback for switching publishable diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index ec49db1601..92296752de 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -7,6 +7,7 @@ from xml.etree import ElementTree as ET import six from Qt import QtWidgets, QtCore import openpype.api as openpype +from openpype.pipeline import LegacyCreator from openpype import style import avalon.api as avalon from . import ( @@ -299,7 +300,7 @@ class Spacer(QtWidgets.QWidget): self.setLayout(layout) -class Creator(openpype.Creator): +class Creator(LegacyCreator): """Creator class wrapper """ clip_color = constants.COLOR_MAP["purple"] diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 64dda0bc8a..5ac56fcbed 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -11,6 +11,7 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import Logger +from openpype.pipeline import LegacyCreator import openpype.hosts.fusion log = Logger().get_logger(__name__) @@ -63,7 +64,7 @@ def install(): log.info("Registering Fusion plug-ins..") avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) pyblish.api.register_callback( @@ -87,7 +88,7 @@ def uninstall(): log.info("Deregistering Fusion plug-ins..") avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH ) diff --git a/openpype/hosts/fusion/plugins/create/create_exr_saver.py b/openpype/hosts/fusion/plugins/create/create_exr_saver.py index 04717f4746..ff8bdb21ef 100644 --- a/openpype/hosts/fusion/plugins/create/create_exr_saver.py +++ b/openpype/hosts/fusion/plugins/create/create_exr_saver.py @@ -1,13 +1,13 @@ import os -import openpype.api +from openpype.pipeline import create from openpype.hosts.fusion.api import ( get_current_comp, comp_lock_and_undo_chunk ) -class CreateOpenEXRSaver(openpype.api.Creator): +class CreateOpenEXRSaver(create.LegacyCreator): name = "openexrDefault" label = "Create OpenEXR Saver" diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index 17d2870876..6d0f5e9416 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -9,6 +9,7 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype import lib +from openpype.pipeline import LegacyCreator import openpype.hosts.harmony import openpype.hosts.harmony.api as harmony @@ -179,7 +180,7 @@ def install(): pyblish.api.register_host("harmony") pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) # Register callbacks. @@ -193,7 +194,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) def on_pyblish_instance_toggled(instance, old_value, new_value): diff --git a/openpype/hosts/harmony/api/plugin.py b/openpype/hosts/harmony/api/plugin.py index d6d61a547a..c55d200d30 100644 --- a/openpype/hosts/harmony/api/plugin.py +++ b/openpype/hosts/harmony/api/plugin.py @@ -1,9 +1,8 @@ -import avalon.api -from openpype.api import PypeCreatorMixin +from openpype.pipeline import LegacyCreator import openpype.hosts.harmony.api as harmony -class Creator(PypeCreatorMixin, avalon.api.Creator): +class Creator(LegacyCreator): """Creator plugin to create instances in Harmony. By default a Composite node is created to support any number of nodes in diff --git a/openpype/hosts/hiero/api/pipeline.py b/openpype/hosts/hiero/api/pipeline.py index cbcaf23994..5cb23ea355 100644 --- a/openpype/hosts/hiero/api/pipeline.py +++ b/openpype/hosts/hiero/api/pipeline.py @@ -9,6 +9,7 @@ from avalon import api as avalon from avalon import schema from pyblish import api as pyblish from openpype.api import Logger +from openpype.pipeline import LegacyCreator from openpype.tools.utils import host_tools from . import lib, menu, events @@ -45,7 +46,7 @@ def install(): pyblish.register_host("hiero") pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) # register callback for switching publishable @@ -67,7 +68,7 @@ def uninstall(): pyblish.deregister_host("hiero") pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) # register callback for switching publishable pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled) diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py index 3506af2d6a..3963985f0c 100644 --- a/openpype/hosts/hiero/api/plugin.py +++ b/openpype/hosts/hiero/api/plugin.py @@ -1,12 +1,15 @@ -import re import os +import re +from copy import deepcopy + import hiero + from Qt import QtWidgets, QtCore from avalon.vendor import qargparse import avalon.api as avalon import openpype.api as openpype +from openpype.pipeline import LegacyCreator from . import lib -from copy import deepcopy log = openpype.Logger().get_logger(__name__) @@ -589,7 +592,7 @@ class ClipLoader: return track_item -class Creator(openpype.Creator): +class Creator(LegacyCreator): """Creator class wrapper """ clip_color = "Purple" diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 1c08e72d65..21027dad2e 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -11,6 +11,7 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon.lib import find_submodule +from openpype.pipeline import LegacyCreator import openpype.hosts.houdini from openpype.hosts.houdini.api import lib @@ -48,7 +49,7 @@ def install(): pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info("Installing callbacks ... ") # avalon.on("init", on_init) diff --git a/openpype/hosts/houdini/api/plugin.py b/openpype/hosts/houdini/api/plugin.py index 4967d01d43..2bbb65aa05 100644 --- a/openpype/hosts/houdini/api/plugin.py +++ b/openpype/hosts/houdini/api/plugin.py @@ -2,11 +2,12 @@ """Houdini specific Avalon/Pyblish plugin definitions.""" import sys import six -import avalon.api -from avalon.api import CreatorError import hou -from openpype.api import PypeCreatorMixin +from openpype.pipeline import ( + CreatorError, + LegacyCreator +) from .lib import imprint @@ -14,7 +15,7 @@ class OpenPypeCreatorError(CreatorError): pass -class Creator(PypeCreatorMixin, avalon.api.Creator): +class Creator(LegacyCreator): """Creator plugin to create instances in Houdini To support the wide range of node types for render output (Alembic, VDB, diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 1b3bb9feb3..8c3669c5d1 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -2,7 +2,6 @@ import os import sys import errno import logging -import contextlib from maya import utils, cmds, OpenMaya import maya.api.OpenMaya as om @@ -17,6 +16,7 @@ import openpype.hosts.maya from openpype.tools.utils import host_tools from openpype.lib import any_outdated from openpype.lib.path_tools import HostDirmap +from openpype.pipeline import LegacyCreator from openpype.hosts.maya.lib import copy_workspace_mel from . import menu, lib @@ -50,7 +50,7 @@ def install(): pyblish.api.register_host("maya") avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) log.info(PUBLISH_PATH) @@ -176,7 +176,7 @@ def uninstall(): pyblish.api.deregister_host("maya") avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH ) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index bdb8fcf13a..5e52985fec 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -4,7 +4,7 @@ from maya import cmds from avalon import api from avalon.vendor import qargparse -from openpype.api import PypeCreatorMixin +from openpype.pipeline import LegacyCreator from .pipeline import containerise from . import lib @@ -77,7 +77,7 @@ def get_reference_node_parents(ref): return parents -class Creator(PypeCreatorMixin, api.Creator): +class Creator(LegacyCreator): defaults = ['Main'] def process(self): diff --git a/openpype/hosts/maya/plugins/create/create_render.py b/openpype/hosts/maya/plugins/create/create_render.py index 743ec26778..9002ae3876 100644 --- a/openpype/hosts/maya/plugins/create/create_render.py +++ b/openpype/hosts/maya/plugins/create/create_render.py @@ -19,9 +19,9 @@ from openpype.api import ( get_project_settings, get_asset) from openpype.modules import ModulesManager +from openpype.pipeline import CreatorError from avalon.api import Session -from avalon.api import CreatorError class CreateRender(plugin.Creator): diff --git a/openpype/hosts/maya/plugins/create/create_vrayscene.py b/openpype/hosts/maya/plugins/create/create_vrayscene.py index f2096d902e..fa9c59e016 100644 --- a/openpype/hosts/maya/plugins/create/create_vrayscene.py +++ b/openpype/hosts/maya/plugins/create/create_vrayscene.py @@ -19,10 +19,10 @@ from openpype.api import ( get_project_settings ) +from openpype.pipeline import CreatorError from openpype.modules import ModulesManager from avalon.api import Session -from avalon.api import CreatorError class CreateVRayScene(plugin.Creator): diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 0565b0b95c..25db5fb1e5 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -3,6 +3,7 @@ from maya import cmds from avalon import api from openpype.api import get_project_settings from openpype.lib import get_creator_by_name +from openpype.pipeline import legacy_create import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.lib import maintained_selection @@ -151,7 +152,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): creator_plugin = get_creator_by_name(self.animation_creator_name) with maintained_selection(): cmds.select([output, controls] + roots, noExpand=True) - api.create( + legacy_create( creator_plugin, name=namespace, asset=asset, diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index 8c6c9ca55b..d98a951491 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -14,6 +14,7 @@ from openpype.api import ( BuildWorkfile, get_current_project_settings ) +from openpype.pipeline import LegacyCreator from openpype.tools.utils import host_tools from .command import viewer_update_and_undo_stop @@ -98,7 +99,7 @@ def install(): log.info("Registering Nuke plug-ins..") pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) # Register Avalon event for workfiles loading. @@ -124,7 +125,7 @@ def uninstall(): pyblish.deregister_host("nuke") pyblish.api.deregister_plugin_path(PUBLISH_PATH) avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) pyblish.api.deregister_callback( "instanceToggled", on_pyblish_instance_toggled) diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index 11e30d9fcd..ff186cd685 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -6,10 +6,8 @@ import nuke import avalon.api -from openpype.api import ( - get_current_project_settings, - PypeCreatorMixin -) +from openpype.api import get_current_project_settings +from openpype.pipeline import LegacyCreator from .lib import ( Knobby, check_subsetname_exists, @@ -20,7 +18,7 @@ from .lib import ( ) -class OpenPypeCreator(PypeCreatorMixin, avalon.api.Creator): +class OpenPypeCreator(LegacyCreator): """Pype Nuke Creator class wrapper""" node_color = "0xdfea5dff" diff --git a/openpype/hosts/photoshop/api/__init__.py b/openpype/hosts/photoshop/api/__init__.py index 4cc2aa2c78..17ea957066 100644 --- a/openpype/hosts/photoshop/api/__init__.py +++ b/openpype/hosts/photoshop/api/__init__.py @@ -16,7 +16,6 @@ from .pipeline import ( ) from .plugin import ( PhotoshopLoader, - Creator, get_unique_layer_name ) from .workio import ( @@ -42,11 +41,11 @@ __all__ = [ "list_instances", "remove_instance", "install", + "uninstall", "containerise", # Plugin "PhotoshopLoader", - "Creator", "get_unique_layer_name", # workfiles diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 25983f2471..662e9dbebc 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -1,5 +1,4 @@ import os -import sys from Qt import QtWidgets import pyblish.api @@ -7,6 +6,7 @@ import avalon.api from avalon import pipeline, io from openpype.api import Logger +from openpype.pipeline import LegacyCreator import openpype.hosts.photoshop from . import lib @@ -68,7 +68,7 @@ def install(): pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) pyblish.api.register_callback( @@ -81,7 +81,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) def ls(): diff --git a/openpype/hosts/photoshop/api/plugin.py b/openpype/hosts/photoshop/api/plugin.py index e0db67de2c..c577c67d82 100644 --- a/openpype/hosts/photoshop/api/plugin.py +++ b/openpype/hosts/photoshop/api/plugin.py @@ -33,37 +33,3 @@ class PhotoshopLoader(avalon.api.Loader): @staticmethod def get_stub(): return stub() - - -class Creator(avalon.api.Creator): - """Creator plugin to create instances in Photoshop - - A LayerSet is created to support any number of layers in an instance. If - the selection is used, these layers will be added to the LayerSet. - """ - - def process(self): - # Photoshop can have multiple LayerSets with the same name, which does - # not work with Avalon. - msg = "Instance with name \"{}\" already exists.".format(self.name) - stub = lib.stub() # only after Photoshop is up - for layer in stub.get_layers(): - if self.name.lower() == layer.Name.lower(): - msg = QtWidgets.QMessageBox() - msg.setIcon(QtWidgets.QMessageBox.Warning) - msg.setText(msg) - msg.exec_() - return False - - # Store selection because adding a group will change selection. - with lib.maintained_selection(): - - # Add selection to group. - if (self.options or {}).get("useSelection"): - group = stub.group_selected_layers(self.name) - else: - group = stub.create_group(self.name) - - stub.imprint(group, self.data) - - return group diff --git a/openpype/hosts/photoshop/plugins/create/create_image.py b/openpype/hosts/photoshop/plugins/create/create_image.py index 344a53f47e..a001b5f171 100644 --- a/openpype/hosts/photoshop/plugins/create/create_image.py +++ b/openpype/hosts/photoshop/plugins/create/create_image.py @@ -1,9 +1,9 @@ from Qt import QtWidgets -import openpype.api +from openpype.pipeline import create from openpype.hosts.photoshop import api as photoshop -class CreateImage(openpype.api.Creator): +class CreateImage(create.LegacyCreator): """Image folder for publish.""" name = "imageDefault" diff --git a/openpype/hosts/resolve/api/pipeline.py b/openpype/hosts/resolve/api/pipeline.py index 2dc5136c8a..c82545268b 100644 --- a/openpype/hosts/resolve/api/pipeline.py +++ b/openpype/hosts/resolve/api/pipeline.py @@ -9,6 +9,7 @@ from avalon import schema from avalon.pipeline import AVALON_CONTAINER_ID from pyblish import api as pyblish from openpype.api import Logger +from openpype.pipeline import LegacyCreator from . import lib from . import PLUGINS_DIR from openpype.tools.utils import host_tools @@ -42,7 +43,7 @@ def install(): log.info("Registering DaVinci Resovle plug-ins..") avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) # register callback for switching publishable @@ -67,7 +68,7 @@ def uninstall(): log.info("Deregistering DaVinci Resovle plug-ins..") avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) # register callback for switching publishable diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 8612cf82ec..b6791f7225 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -2,6 +2,7 @@ import re import uuid from avalon import api import openpype.api as pype +from openpype.pipeline import LegacyCreator from openpype.hosts import resolve from avalon.vendor import qargparse from . import lib @@ -493,7 +494,7 @@ class TimelineItemLoader(api.Loader): pass -class Creator(pype.PypeCreatorMixin, api.Creator): +class Creator(LegacyCreator): """Creator class wrapper """ marker_color = "Purple" diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 74eb41892c..f4599047b4 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -14,6 +14,7 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.hosts import tvpaint from openpype.api import get_current_project_settings +from openpype.pipeline import LegacyCreator from .lib import ( execute_george, @@ -76,7 +77,7 @@ def install(): pyblish.api.register_host("tvpaint") pyblish.api.register_plugin_path(PUBLISH_PATH) avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.register_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) registered_callbacks = ( pyblish.api.registered_callbacks().get("instanceToggled") or [] @@ -98,7 +99,7 @@ def uninstall(): pyblish.api.deregister_host("tvpaint") pyblish.api.deregister_plugin_path(PUBLISH_PATH) avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) - avalon.api.deregister_plugin_path(avalon.api.Creator, CREATE_PATH) + avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) def containerise( diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index af80c9eae2..8510794f06 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -3,14 +3,14 @@ import uuid import avalon.api -from openpype.api import PypeCreatorMixin +from openpype.pipeline import LegacyCreator from openpype.hosts.tvpaint.api import ( pipeline, lib ) -class Creator(PypeCreatorMixin, avalon.api.Creator): +class Creator(LegacyCreator): def __init__(self, *args, **kwargs): super(Creator, self).__init__(*args, **kwargs) # Add unified identifier created with `uuid` module diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py index 40a7d15990..c1af9632b1 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_layer.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_layer.py @@ -1,5 +1,4 @@ -from avalon.api import CreatorError - +from openpype.pipeline import CreatorError from openpype.lib import prepare_template_data from openpype.hosts.tvpaint.api import ( plugin, diff --git a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py index af962052fc..a7f717ccec 100644 --- a/openpype/hosts/tvpaint/plugins/create/create_render_pass.py +++ b/openpype/hosts/tvpaint/plugins/create/create_render_pass.py @@ -1,4 +1,4 @@ -from avalon.api import CreatorError +from openpype.pipeline import CreatorError from openpype.lib import prepare_template_data from openpype.hosts.tvpaint.api import ( plugin, diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index ad64d56e9e..8ab19bd697 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -7,6 +7,7 @@ import pyblish.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon import api +from openpype.pipeline import LegacyCreator from openpype.tools.utils import host_tools import openpype.hosts.unreal @@ -44,7 +45,7 @@ def install(): logger.info("installing OpenPype for Unreal") pyblish.api.register_plugin_path(str(PUBLISH_PATH)) api.register_plugin_path(api.Loader, str(LOAD_PATH)) - api.register_plugin_path(api.Creator, str(CREATE_PATH)) + api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) _register_callbacks() _register_events() @@ -53,7 +54,7 @@ def uninstall(): """Uninstall Unreal configuration for Avalon.""" pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) api.deregister_plugin_path(api.Loader, str(LOAD_PATH)) - api.deregister_plugin_path(api.Creator, str(CREATE_PATH)) + api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) def _register_callbacks(): @@ -70,7 +71,7 @@ def _register_events(): pass -class Creator(api.Creator): +class Creator(LegacyCreator): hosts = ["unreal"] asset_types = [] diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index 2327fc09c8..dd2e7750f0 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- from abc import ABC -import openpype.api +from openpype.pipeline import LegacyCreator import avalon.api -class Creator(openpype.api.Creator): +class Creator(LegacyCreator): """This serves as skeleton for future OpenPype specific functionality""" defaults = ['Main'] diff --git a/openpype/hosts/webpublisher/api/__init__.py b/openpype/hosts/webpublisher/api/__init__.py index e40d46d662..6ce8a58fc2 100644 --- a/openpype/hosts/webpublisher/api/__init__.py +++ b/openpype/hosts/webpublisher/api/__init__.py @@ -5,6 +5,7 @@ from avalon import api as avalon from avalon import io from pyblish import api as pyblish import openpype.hosts.webpublisher +from openpype.pipeline import LegacyCreator log = logging.getLogger("openpype.hosts.webpublisher") @@ -25,7 +26,7 @@ def install(): pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(avalon.Creator, CREATE_PATH) + avalon.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) io.install() @@ -35,7 +36,7 @@ def install(): def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(avalon.Creator, CREATE_PATH) + avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) # to have required methods for interface diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 183aad939a..19765a6f4a 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -293,7 +293,7 @@ def set_plugin_attributes_from_settings( plugin_type = None if superclass.__name__.split(".")[-1] in ("Loader", "SubsetLoader"): plugin_type = "load" - elif superclass.__name__.split(".")[-1] == "Creator": + elif superclass.__name__.split(".")[-1] in ("Creator", "LegacyCreator"): plugin_type = "create" if not host_name or not project_name or plugin_type is None: diff --git a/openpype/plugin.py b/openpype/plugin.py index 45c9a08209..3569936dac 100644 --- a/openpype/plugin.py +++ b/openpype/plugin.py @@ -3,79 +3,12 @@ import os import pyblish.api import avalon.api -from openpype.lib import get_subset_name - ValidatePipelineOrder = pyblish.api.ValidatorOrder + 0.05 ValidateContentsOrder = pyblish.api.ValidatorOrder + 0.1 ValidateSceneOrder = pyblish.api.ValidatorOrder + 0.2 ValidateMeshOrder = pyblish.api.ValidatorOrder + 0.3 -class PypeCreatorMixin: - """Helper to override avalon's default class methods. - - Mixin class must be used as first in inheritance order to override methods. - """ - dynamic_subset_keys = [] - - @classmethod - def get_dynamic_data( - cls, variant, task_name, asset_id, project_name, host_name - ): - """Return dynamic data for current Creator plugin. - - By default return keys from `dynamic_subset_keys` attribute as mapping - to keep formatted template unchanged. - - ``` - dynamic_subset_keys = ["my_key"] - --- - output = { - "my_key": "{my_key}" - } - ``` - - Dynamic keys may override default Creator keys (family, task, asset, - ...) but do it wisely if you need. - - All of keys will be converted into 3 variants unchanged, capitalized - and all upper letters. Because of that are all keys lowered. - - This method can be modified to prefill some values just keep in mind it - is class method. - - Returns: - dict: Fill data for subset name template. - """ - dynamic_data = {} - for key in cls.dynamic_subset_keys: - key = key.lower() - dynamic_data[key] = "{" + key + "}" - return dynamic_data - - @classmethod - def get_subset_name( - cls, variant, task_name, asset_id, project_name, host_name=None - ): - dynamic_data = cls.get_dynamic_data( - variant, task_name, asset_id, project_name, host_name - ) - - return get_subset_name( - cls.family, - variant, - task_name, - asset_id, - project_name, - host_name, - dynamic_data=dynamic_data - ) - - -class Creator(PypeCreatorMixin, avalon.api.Creator): - pass - - class ContextPlugin(pyblish.api.ContextPlugin): def process(cls, *args, **kwargs): super(ContextPlugin, cls).process(cls, *args, **kwargs) diff --git a/openpype/tests/test_avalon_plugin_presets.py b/openpype/tests/test_avalon_plugin_presets.py index ec21385d23..f1b1a94713 100644 --- a/openpype/tests/test_avalon_plugin_presets.py +++ b/openpype/tests/test_avalon_plugin_presets.py @@ -1,8 +1,9 @@ import avalon.api as api import openpype +from openpype.pipeline import LegacyCreator -class MyTestCreator(api.Creator): +class MyTestCreator(LegacyCreator): my_test_property = "A" @@ -26,8 +27,8 @@ def test_avalon_plugin_presets(monkeypatch, printer): openpype.install() api.register_host(Test()) - api.register_plugin(api.Creator, MyTestCreator) - plugins = api.discover(api.Creator) + api.register_plugin(LegacyCreator, MyTestCreator) + plugins = api.discover(LegacyCreator) printer("Test if we got our test plugin") assert MyTestCreator in plugins for p in plugins: diff --git a/openpype/tools/creator/model.py b/openpype/tools/creator/model.py index 6907e8f0aa..ef61c6e0f0 100644 --- a/openpype/tools/creator/model.py +++ b/openpype/tools/creator/model.py @@ -2,6 +2,7 @@ import uuid from Qt import QtGui, QtCore from avalon import api +from openpype.pipeline import LegacyCreator from . constants import ( FAMILY_ROLE, @@ -21,7 +22,7 @@ class CreatorsModel(QtGui.QStandardItemModel): self._creators_by_id = {} items = [] - creators = api.discover(api.Creator) + creators = api.discover(LegacyCreator) for creator in creators: item_id = str(uuid.uuid4()) self._creators_by_id[item_id] = creator diff --git a/openpype/tools/creator/window.py b/openpype/tools/creator/window.py index f1d0849dfe..51cc66e715 100644 --- a/openpype/tools/creator/window.py +++ b/openpype/tools/creator/window.py @@ -9,7 +9,12 @@ from avalon import api, io from openpype import style from openpype.api import get_current_project_settings from openpype.tools.utils.lib import qt_app_context -from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS +from openpype.pipeline.create import ( + SUBSET_NAME_ALLOWED_SYMBOLS, + legacy_create, + CreatorError, + LegacyCreator, +) from .model import CreatorsModel from .widgets import ( @@ -422,7 +427,7 @@ class CreatorWindow(QtWidgets.QDialog): error_info = None try: - api.create( + legacy_create( creator_plugin, subset_name, asset_name, @@ -430,7 +435,7 @@ class CreatorWindow(QtWidgets.QDialog): data={"variant": variant} ) - except api.CreatorError as exc: + except CreatorError as exc: self.echo("Creator error: {}".format(str(exc))) error_info = (str(exc), None) @@ -486,7 +491,7 @@ def show(debug=False, parent=None): if debug: from avalon import mock for creator in mock.creators: - api.register_plugin(api.Creator, creator) + api.register_plugin(LegacyCreator, creator) import traceback sys.excepthook = lambda typ, val, tb: traceback.print_last() diff --git a/openpype/tools/standalonepublish/widgets/widget_family.py b/openpype/tools/standalonepublish/widgets/widget_family.py index ae44899a89..08cd45bbf2 100644 --- a/openpype/tools/standalonepublish/widgets/widget_family.py +++ b/openpype/tools/standalonepublish/widgets/widget_family.py @@ -1,14 +1,11 @@ -import os import re from Qt import QtWidgets, QtCore from . import HelpRole, FamilyRole, ExistsRole, PluginRole, PluginKeyRole from . import FamilyDescriptionWidget -from openpype.api import ( - get_project_settings, - Creator -) +from openpype.api import get_project_settings +from openpype.pipeline import LegacyCreator from openpype.lib import TaskNotSetError from openpype.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS @@ -390,7 +387,7 @@ class FamilyWidget(QtWidgets.QWidget): sp_settings = settings.get('standalonepublisher', {}) for key, creator_data in sp_settings.get("create", {}).items(): - creator = type(key, (Creator, ), creator_data) + creator = type(key, (LegacyCreator, ), creator_data) label = creator.label or creator.family item = QtWidgets.QListWidgetItem(label) From d1cc05487343485db215cb88b7ce4d4879152368 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 9 Mar 2022 03:36:53 +0000 Subject: [PATCH 141/302] [Automated] Bump version --- CHANGELOG.md | 19 +++++++++---------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 711517e6c6..fa479d8f05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,12 @@ # Changelog -## [3.9.0-nightly.6](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0-nightly.7](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) **Deprecated:** +- AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845) - Houdini: Remove unused code [\#2779](https://github.com/pypeclub/OpenPype/pull/2779) ### 📖 Documentation @@ -16,21 +17,23 @@ **🚀 Enhancements** +- New: Validation exceptions [\#2841](https://github.com/pypeclub/OpenPype/pull/2841) - Ftrack: Can sync fps as string [\#2836](https://github.com/pypeclub/OpenPype/pull/2836) +- General: Custom function for find executable [\#2822](https://github.com/pypeclub/OpenPype/pull/2822) - General: Color dialog UI fixes [\#2817](https://github.com/pypeclub/OpenPype/pull/2817) +- global: letter box calculated on output as last process [\#2812](https://github.com/pypeclub/OpenPype/pull/2812) - Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811) - Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805) - General: Set context environments for non host applications [\#2803](https://github.com/pypeclub/OpenPype/pull/2803) -- Houdini: Remove duplicate ValidateOutputNode plug-in [\#2780](https://github.com/pypeclub/OpenPype/pull/2780) - Tray publisher: New Tray Publisher host \(beta\) [\#2778](https://github.com/pypeclub/OpenPype/pull/2778) -- Slack: Added regex for filtering on subset names [\#2775](https://github.com/pypeclub/OpenPype/pull/2775) -- Houdini: Implement Reset Frame Range [\#2770](https://github.com/pypeclub/OpenPype/pull/2770) - Flame: use Shot Name on segment for asset name [\#2751](https://github.com/pypeclub/OpenPype/pull/2751) -- Houdini: Move Houdini Save Current File to beginning of ExtractorOrder [\#2747](https://github.com/pypeclub/OpenPype/pull/2747) -- RoyalRender: Minor enhancements [\#2700](https://github.com/pypeclub/OpenPype/pull/2700) **🐛 Bug fixes** +- WebPublisher: Fix username stored in DB [\#2852](https://github.com/pypeclub/OpenPype/pull/2852) +- WebPublisher: Fix wrong number of frames for video file [\#2851](https://github.com/pypeclub/OpenPype/pull/2851) +- Nuke: fix multiple baking profile farm publishing [\#2842](https://github.com/pypeclub/OpenPype/pull/2842) +- Blender: Fixed parameters for FBX export of the camera [\#2840](https://github.com/pypeclub/OpenPype/pull/2840) - Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832) - Deadline: Remove recreated event [\#2828](https://github.com/pypeclub/OpenPype/pull/2828) - Deadline: Added missing events folder [\#2827](https://github.com/pypeclub/OpenPype/pull/2827) @@ -46,10 +49,6 @@ - Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) - Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) - Flame: Fix version string in default settings [\#2783](https://github.com/pypeclub/OpenPype/pull/2783) -- Houdini: Fix open last workfile [\#2767](https://github.com/pypeclub/OpenPype/pull/2767) -- Maya: Fix `unique\_namespace` when in an namespace that is empty [\#2759](https://github.com/pypeclub/OpenPype/pull/2759) -- Maya: Remove some unused code [\#2709](https://github.com/pypeclub/OpenPype/pull/2709) -- Multiple hosts: unify menu style across hosts [\#2693](https://github.com/pypeclub/OpenPype/pull/2693) **Merged pull requests:** diff --git a/openpype/version.py b/openpype/version.py index d977e87243..55ac148ed1 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.6" +__version__ = "3.9.0-nightly.7" diff --git a/pyproject.toml b/pyproject.toml index 2469cb76a9..541932bce6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.6" # OpenPype +version = "3.9.0-nightly.7" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From ee71d3b6580f363d95404b13283339ea055a183d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 10:13:51 +0100 Subject: [PATCH 142/302] fix getattr clalback on dynamic module --- openpype/modules/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/base.py b/openpype/modules/base.py index c7078475df..175957ae39 100644 --- a/openpype/modules/base.py +++ b/openpype/modules/base.py @@ -61,6 +61,7 @@ class _ModuleClass(object): def __init__(self, name): # Call setattr on super class super(_ModuleClass, self).__setattr__("name", name) + super(_ModuleClass, self).__setattr__("__name__", name) # Where modules and interfaces are stored super(_ModuleClass, self).__setattr__("__attributes__", dict()) @@ -72,7 +73,7 @@ class _ModuleClass(object): if attr_name not in self.__attributes__: if attr_name in ("__path__", "__file__"): return None - raise ImportError("No module named {}.{}".format( + raise AttributeError("'{}' has not attribute '{}'".format( self.name, attr_name )) return self.__attributes__[attr_name] From aef78c3c7580c978873c3e89ea34e1a00a2a4b92 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 11:34:02 +0100 Subject: [PATCH 143/302] use new version of error dialog and pass parent to a dialog --- .../tools/publisher/widgets/create_dialog.py | 124 +++++++++--------- 1 file changed, 65 insertions(+), 59 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index c5b77eca8b..5ebcd7291d 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -14,6 +14,8 @@ from openpype.pipeline.create import ( SUBSET_NAME_ALLOWED_SYMBOLS ) +from openpype.tools.utils import ErrorMessageBox + from .widgets import IconValuePixmapLabel from .assets_widget import CreateDialogAssetsWidget from .tasks_widget import CreateDialogTasksWidget @@ -27,7 +29,7 @@ from ..constants import ( SEPARATORS = ("---separator---", "---") -class CreateErrorMessageBox(QtWidgets.QDialog): +class CreateErrorMessageBox(ErrorMessageBox): def __init__( self, creator_label, @@ -35,24 +37,38 @@ class CreateErrorMessageBox(QtWidgets.QDialog): asset_name, exc_msg, formatted_traceback, - parent=None + parent ): - super(CreateErrorMessageBox, self).__init__(parent) - self.setWindowTitle("Creation failed") - self.setFocusPolicy(QtCore.Qt.StrongFocus) - if not parent: - self.setWindowFlags( - self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint - ) + self._creator_label = creator_label + self._subset_name = subset_name + self._asset_name = asset_name + self._exc_msg = exc_msg + self._formatted_traceback = formatted_traceback + super(CreateErrorMessageBox, self).__init__("Creation failed", parent) - body_layout = QtWidgets.QVBoxLayout(self) - - main_label = ( + def _create_top_widget(self, parent_widget): + label_widget = QtWidgets.QLabel(parent_widget) + label_widget.setText( "Failed to create" ) - main_label_widget = QtWidgets.QLabel(main_label, self) - body_layout.addWidget(main_label_widget) + return label_widget + def _get_report_data(self): + report_message = ( + "{creator}: Failed to create Subset: \"{subset}\"" + " in Asset: \"{asset}\"" + "\n\nError: {message}" + ).format( + creator=self._creator_label, + subset=self._subset_name, + asset=self._asset_name, + message=self._exc_msg, + ) + if self._formatted_traceback: + report_message += "\n\n{}".format(self._formatted_traceback) + return [report_message] + + def _create_content(self, content_layout): item_name_template = ( "Creator: {}
" "Subset: {}
" @@ -61,48 +77,29 @@ class CreateErrorMessageBox(QtWidgets.QDialog): exc_msg_template = "{}" line = self._create_line() - body_layout.addWidget(line) + content_layout.addWidget(line) - item_name = item_name_template.format( - creator_label, subset_name, asset_name - ) - item_name_widget = QtWidgets.QLabel( - item_name.replace("\n", "
"), self - ) - body_layout.addWidget(item_name_widget) - - exc_msg = exc_msg_template.format(exc_msg.replace("\n", "
")) - message_label_widget = QtWidgets.QLabel(exc_msg, self) - body_layout.addWidget(message_label_widget) - - if formatted_traceback: - tb_widget = QtWidgets.QLabel( - formatted_traceback.replace("\n", "
"), self + item_name_widget = QtWidgets.QLabel(self) + item_name_widget.setText( + item_name_template.format( + self._creator_label, self._subset_name, self._asset_name ) - tb_widget.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction - ) - body_layout.addWidget(tb_widget) - - footer_widget = QtWidgets.QWidget(self) - footer_layout = QtWidgets.QHBoxLayout(footer_widget) - button_box = QtWidgets.QDialogButtonBox(QtCore.Qt.Vertical) - button_box.setStandardButtons( - QtWidgets.QDialogButtonBox.StandardButton.Ok ) - button_box.accepted.connect(self._on_accept) - footer_layout.addWidget(button_box, alignment=QtCore.Qt.AlignRight) - body_layout.addWidget(footer_widget) + content_layout.addWidget(item_name_widget) - def _on_accept(self): - self.close() + message_label_widget = QtWidgets.QLabel(self) + message_label_widget.setText( + exc_msg_template.format(self.convert_text_for_html(self._exc_msg)) + ) + content_layout.addWidget(message_label_widget) - def _create_line(self): - line = QtWidgets.QFrame(self) - line.setFixedHeight(2) - line.setFrameShape(QtWidgets.QFrame.HLine) - line.setFrameShadow(QtWidgets.QFrame.Sunken) - return line + if self._formatted_traceback: + line_widget = self._create_line() + tb_widget = self._create_traceback_widget( + self._formatted_traceback + ) + content_layout.addWidget(line_widget) + content_layout.addWidget(tb_widget) # TODO add creator identifier/label to details @@ -201,7 +198,7 @@ class CreateDialog(QtWidgets.QDialog): self._prereq_available = False - self.message_dialog = None + self._message_dialog = None name_pattern = "^[{}]*$".format(SUBSET_NAME_ALLOWED_SYMBOLS) self._name_pattern = name_pattern @@ -694,14 +691,18 @@ class CreateDialog(QtWidgets.QDialog): "family": family } - error_info = None + error_msg = None + formatted_traceback = None try: self.controller.create( - creator_identifier, subset_name, instance_data, pre_create_data + creator_identifier, + subset_name, + instance_data, + pre_create_data ) except CreatorError as exc: - error_info = (str(exc), None) + error_msg = str(exc) # Use bare except because some hosts raise their exceptions that # do not inherit from python's `BaseException` @@ -710,12 +711,17 @@ class CreateDialog(QtWidgets.QDialog): formatted_traceback = "".join(traceback.format_exception( exc_type, exc_value, exc_traceback )) - error_info = (str(exc_value), formatted_traceback) + error_msg = str(exc_value) - if error_info: + if error_msg is not None: box = CreateErrorMessageBox( - creator_label, subset_name, asset_name, *error_info + creator_label, + subset_name, + asset_name, + error_msg, + formatted_traceback, + parent=self ) box.show() # Store dialog so is not garbage collected before is shown - self.message_dialog = box + self._message_dialog = box From 52ad3caf170c9ba6c9e6c1c68ed91591adf57bd6 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 9 Mar 2022 12:04:35 +0100 Subject: [PATCH 144/302] Fix frameEnd was 1 frame too high --- .../plugins/publish/collect_published_files.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index 8b21842635..afd6f349db 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -105,17 +105,18 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): task_dir, task_data["files"], tags ) file_url = os.path.join(task_dir, task_data["files"][0]) - duration = self._get_duration(file_url) - if duration: + no_of_frames = self._get_number_of_frames(file_url) + if no_of_frames: try: - frame_end = int(frame_start) + math.ceil(duration) - instance.data["frameEnd"] = math.ceil(frame_end) + frame_end = int(frame_start) + math.ceil(no_of_frames) + instance.data["frameEnd"] = math.ceil(frame_end) - 1 self.log.debug("frameEnd:: {}".format( instance.data["frameEnd"])) except ValueError: self.log.warning("Unable to count frames " - "duration {}".format(duration)) + "duration {}".format(no_of_frames)) + # raise ValueError("STOP") instance.data["handleStart"] = asset_doc["data"]["handleStart"] instance.data["handleEnd"] = asset_doc["data"]["handleEnd"] @@ -261,7 +262,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): else: return 0 - def _get_duration(self, file_url): + def _get_number_of_frames(self, file_url): """Return duration in frames""" try: streams = ffprobe_streams(file_url, self.log) @@ -288,7 +289,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): duration = stream.get("duration") frame_rate = get_fps(stream.get("r_frame_rate", '0/0')) - self.log.debu("duration:: {} frame_rate:: {}".format( + self.log.debug("duration:: {} frame_rate:: {}".format( duration, frame_rate)) try: return float(duration) * float(frame_rate) From 65cd0c55a839b03fcc471e1215b2dcf3f46d974e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 12:08:11 +0100 Subject: [PATCH 145/302] added check of subclasses in patched discover --- openpype/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index c41afaa47d..942112835b 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -59,10 +59,15 @@ def patched_discover(superclass): """ # run original discover and get plugins plugins = _original_discover(superclass) + filtered_plugins = [ + plugin + for plugin in plugins + if issubclass(plugin, superclass) + ] - set_plugin_attributes_from_settings(plugins, superclass) + set_plugin_attributes_from_settings(filtered_plugins, superclass) - return plugins + return filtered_plugins @import_wrapper From 6d2ebaa68e3ec4d448845d89738465edc671a255 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Wed, 9 Mar 2022 12:11:27 +0100 Subject: [PATCH 146/302] remove unused code --- openpype/hosts/blender/plugins/load/load_layout_json.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/blender/plugins/load/load_layout_json.py b/openpype/hosts/blender/plugins/load/load_layout_json.py index 91817deb60..5b5f9ab83d 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_json.py +++ b/openpype/hosts/blender/plugins/load/load_layout_json.py @@ -8,7 +8,6 @@ from typing import Dict, Optional import bpy from avalon import api -from openpype.pipeline import legacy_create from openpype.hosts.blender.api.pipeline import ( AVALON_INSTANCES, AVALON_CONTAINERS, From f515f360dc204d122a7288306a1bd47289959fdc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 14:34:18 +0100 Subject: [PATCH 147/302] Choose project widget is more clear --- openpype/style/style.css | 6 ++++ openpype/tools/traypublisher/window.py | 38 +++++++++++++++++--------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index ba40b780ab..5586cf766d 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -1266,6 +1266,12 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { font-size: 15pt; font-weight: 750; } +#ChooseProjectFrame { + border-radius: 10px; +} +#ChooseProjectView { + background: transparent; +} /* Globally used names */ #Separator { diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index 53f8ca450a..4c35d84f98 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -28,38 +28,50 @@ class StandaloneOverlayWidget(QtWidgets.QFrame): super(StandaloneOverlayWidget, self).__init__(publisher_window) self.setObjectName("OverlayFrame") + middle_frame = QtWidgets.QFrame(self) + middle_frame.setObjectName("ChooseProjectFrame") + + content_widget = QtWidgets.QWidget(middle_frame) + # Create db connection for projects model dbcon = AvalonMongoDB() dbcon.install() - header_label = QtWidgets.QLabel("Choose project", self) + header_label = QtWidgets.QLabel("Choose project", content_widget) header_label.setObjectName("ChooseProjectLabel") # Create project models and view projects_model = ProjectModel(dbcon) projects_proxy = ProjectSortFilterProxy() projects_proxy.setSourceModel(projects_model) - projects_view = QtWidgets.QListView(self) + projects_view = QtWidgets.QListView(content_widget) + projects_view.setObjectName("ChooseProjectView") projects_view.setModel(projects_proxy) projects_view.setEditTriggers( QtWidgets.QAbstractItemView.NoEditTriggers ) - confirm_btn = QtWidgets.QPushButton("Choose", self) + confirm_btn = QtWidgets.QPushButton("Confirm", content_widget) btns_layout = QtWidgets.QHBoxLayout() btns_layout.addStretch(1) btns_layout.addWidget(confirm_btn, 0) - layout = QtWidgets.QGridLayout(self) - layout.addWidget(header_label, 0, 1, alignment=QtCore.Qt.AlignCenter) - layout.addWidget(projects_view, 1, 1) - layout.addLayout(btns_layout, 2, 1) - layout.setColumnStretch(0, 1) - layout.setColumnStretch(1, 0) - layout.setColumnStretch(2, 1) - layout.setRowStretch(0, 0) - layout.setRowStretch(1, 1) - layout.setRowStretch(2, 0) + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(20) + content_layout.addWidget(header_label, 0) + content_layout.addWidget(projects_view, 1) + content_layout.addLayout(btns_layout, 0) + + middle_layout = QtWidgets.QHBoxLayout(middle_frame) + middle_layout.setContentsMargins(30, 30, 10, 10) + middle_layout.addWidget(content_widget) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.addStretch(1) + main_layout.addWidget(middle_frame, 3) + main_layout.addStretch(1) projects_view.doubleClicked.connect(self._on_double_click) confirm_btn.clicked.connect(self._on_confirm_click) From 9280eacf40d821bad8fa85c8db78ac551728b957 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 14:38:22 +0100 Subject: [PATCH 148/302] missing task does not make invalid asset --- openpype/pipeline/create/context.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py index 706279fd72..c2757a4502 100644 --- a/openpype/pipeline/create/context.py +++ b/openpype/pipeline/create/context.py @@ -1005,12 +1005,14 @@ class CreateContext: if not instances: return - task_names_by_asset_name = collections.defaultdict(set) + task_names_by_asset_name = {} for instance in instances: task_name = instance.get("task") asset_name = instance.get("asset") - if asset_name and task_name: - task_names_by_asset_name[asset_name].add(task_name) + if asset_name: + task_names_by_asset_name[asset_name] = set() + if task_name: + task_names_by_asset_name[asset_name].add(task_name) asset_names = [ asset_name From 20b3b38fb8ed4ecedd0dca398fae0a934f16f1b6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 16:18:02 +0100 Subject: [PATCH 149/302] change ratio --- openpype/tools/traypublisher/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py index 4c35d84f98..d0453c4f23 100644 --- a/openpype/tools/traypublisher/window.py +++ b/openpype/tools/traypublisher/window.py @@ -70,7 +70,7 @@ class StandaloneOverlayWidget(QtWidgets.QFrame): main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(10, 10, 10, 10) main_layout.addStretch(1) - main_layout.addWidget(middle_frame, 3) + main_layout.addWidget(middle_frame, 2) main_layout.addStretch(1) projects_view.doubleClicked.connect(self._on_double_click) From acb7546e3896c6ec66c9c80a9e6fb68fa2a6468f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 16:25:03 +0100 Subject: [PATCH 150/302] handle 'TaskNotSetError' in create dialog --- .../tools/publisher/widgets/create_dialog.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index c5b77eca8b..607060da7e 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -8,7 +8,7 @@ try: except Exception: commonmark = None from Qt import QtWidgets, QtCore, QtGui - +from openpype.lib import TaskNotSetError from openpype.pipeline.create import ( CreatorError, SUBSET_NAME_ALLOWED_SYMBOLS @@ -566,10 +566,9 @@ class CreateDialog(QtWidgets.QDialog): if variant_value is None: variant_value = self.variant_input.text() - match = self._compiled_name_pattern.match(variant_value) - valid = bool(match) - self.create_btn.setEnabled(valid) - if not valid: + self.create_btn.setEnabled(True) + if not self._compiled_name_pattern.match(variant_value): + self.create_btn.setEnabled(False) self._set_variant_state_property("invalid") self.subset_name_input.setText("< Invalid variant >") return @@ -579,9 +578,16 @@ class CreateDialog(QtWidgets.QDialog): asset_doc = copy.deepcopy(self._asset_doc) # Calculate subset name with Creator plugin - subset_name = self._selected_creator.get_subset_name( - variant_value, task_name, asset_doc, project_name - ) + try: + subset_name = self._selected_creator.get_subset_name( + variant_value, task_name, asset_doc, project_name + ) + except TaskNotSetError: + self.create_btn.setEnabled(False) + self._set_variant_state_property("invalid") + self.subset_name_input.setText("< Missing task >") + return + self.subset_name_input.setText(subset_name) self._validate_subset_name(subset_name, variant_value) From 09dbeffe7122d9ad3ee37aab4354637bbbb16e6c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 9 Mar 2022 16:42:49 +0100 Subject: [PATCH 151/302] global: slate could not be created when prores 4444 --- .../plugins/publish/extract_review_slate.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index 7002168cdb..f9ed3cfab6 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -347,8 +347,21 @@ class ExtractReviewSlate(openpype.api.Extractor): profile_name = no_audio_stream.get("profile") if profile_name: - profile_name = profile_name.replace(" ", "_").lower() - codec_args.append("-profile:v {}".format(profile_name)) + # Rest of arguments is prores_kw specific + if codec_name == "prores_ks": + codec_tag_to_profile_map = { + "apco": "proxy", + "apcs": "lt", + "apcn": "standard", + "apch": "hq", + "ap4h": "4444", + "ap4x": "4444xq" + } + codec_tag_str = no_audio_stream.get("codec_tag_string") + if codec_tag_str: + profile = codec_tag_to_profile_map.get(codec_tag_str) + if profile: + codec_args.extend(["-profile:v", profile]) pix_fmt = no_audio_stream.get("pix_fmt") if pix_fmt: From a0b2995f15c2f40b81b73d0f8356a75eaa81bc32 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 17:39:08 +0100 Subject: [PATCH 152/302] tasks model has ability to have empty task --- .../tools/publisher/widgets/tasks_widget.py | 19 +++++++--- openpype/tools/publisher/widgets/widgets.py | 35 +++++++++++++++---- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index a0b3a340ae..2d1cc017af 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -17,9 +17,10 @@ class TasksModel(QtGui.QStandardItemModel): controller (PublisherController): Controller which handles creation and publishing. """ - def __init__(self, controller): + def __init__(self, controller, allow_empty_task=False): super(TasksModel, self).__init__() + self._allow_empty_task = allow_empty_task self._controller = controller self._items_by_name = {} self._asset_names = [] @@ -70,8 +71,14 @@ class TasksModel(QtGui.QStandardItemModel): task_name (str): Name of task which should be available in asset's tasks. """ - task_names = self._task_names_by_asset_name.get(asset_name) - if task_names and task_name in task_names: + if asset_name not in self._task_names_by_asset_name: + return False + + if self._allow_empty_task and not task_name: + return True + + task_names = self._task_names_by_asset_name[asset_name] + if task_name in task_names: return True return False @@ -92,6 +99,8 @@ class TasksModel(QtGui.QStandardItemModel): new_task_names = self.get_intersection_of_tasks( task_names_by_asset_name ) + if self._allow_empty_task: + new_task_names.add("") old_task_names = set(self._items_by_name.keys()) if new_task_names == old_task_names: return @@ -111,7 +120,9 @@ class TasksModel(QtGui.QStandardItemModel): item.setData(task_name, TASK_NAME_ROLE) self._items_by_name[task_name] = item new_items.append(item) - root_item.appendRows(new_items) + + if new_items: + root_item.appendRows(new_items) def headerData(self, section, orientation, role=None): if role is None: diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index fb1f0e54aa..3f913d7e52 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -7,6 +7,7 @@ from Qt import QtWidgets, QtCore, QtGui from avalon.vendor import qtawesome +from openpype.lib import TaskNotSetError from openpype.widgets.attribute_defs import create_widget_for_attr_def from openpype.tools import resources from openpype.tools.flickcharm import FlickCharm @@ -490,13 +491,16 @@ class TasksCombobox(QtWidgets.QComboBox): delegate = QtWidgets.QStyledItemDelegate() self.setItemDelegate(delegate) - model = TasksModel(controller) - self.setModel(model) + model = TasksModel(controller, True) + proxy_model = QtCore.QSortFilterProxyModel() + proxy_model.setSourceModel(model) + self.setModel(proxy_model) self.currentIndexChanged.connect(self._on_index_change) self._delegate = delegate self._model = model + self._proxy_model = proxy_model self._origin_value = [] self._origin_selection = [] self._selected_items = [] @@ -596,6 +600,7 @@ class TasksCombobox(QtWidgets.QComboBox): self._ignore_index_change = True self._model.set_asset_names(asset_names) + self._proxy_model.invalidate() self._ignore_index_change = False @@ -1016,10 +1021,26 @@ class GlobalAttrsWidget(QtWidgets.QWidget): asset_doc = asset_docs_by_name[new_asset_name] - new_subset_name = instance.creator.get_subset_name( - new_variant_value, new_task_name, asset_doc, project_name - ) + try: + new_subset_name = instance.creator.get_subset_name( + new_variant_value, new_task_name, asset_doc, project_name + ) + except TaskNotSetError: + instance.set_task_invalid(True) + continue + subset_names.add(new_subset_name) + if variant_value is not None: + instance["variant"] = variant_value + + if asset_name is not None: + instance["asset"] = asset_name + instance.set_asset_invalid(False) + + if task_name is not None: + instance["task"] = task_name + instance.set_task_invalid(False) + instance["subset"] = new_subset_name self.subset_value_widget.set_value(subset_names) @@ -1098,7 +1119,9 @@ class GlobalAttrsWidget(QtWidgets.QWidget): variants.add(instance.get("variant") or self.unknown_value) families.add(instance.get("family") or self.unknown_value) asset_name = instance.get("asset") or self.unknown_value - task_name = instance.get("task") or self.unknown_value + task_name = instance.get("task") + if task_name is None: + task_name = self.unknown_value asset_names.add(asset_name) asset_task_combinations.append((asset_name, task_name)) subset_names.add(instance.get("subset") or self.unknown_value) From 8efbe92ccb3b90981e8a17ba1ef4c1a9d81e22b6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 17:39:41 +0100 Subject: [PATCH 153/302] changed "Sumbit" to "Confirm" --- openpype/tools/publisher/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 3f913d7e52..a5528e52a6 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -937,7 +937,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): family_value_widget.set_value() subset_value_widget.set_value() - submit_btn = QtWidgets.QPushButton("Submit", self) + submit_btn = QtWidgets.QPushButton("Confirm", self) cancel_btn = QtWidgets.QPushButton("Cancel", self) submit_btn.setEnabled(False) cancel_btn.setEnabled(False) From db1f2ce8c415f2fdf99c201f90602e18f00e69be Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 17:44:33 +0100 Subject: [PATCH 154/302] fix method name --- openpype/hosts/testhost/plugins/publish/collect_context.py | 2 +- openpype/hosts/testhost/plugins/publish/collect_instance_1.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/testhost/plugins/publish/collect_context.py b/openpype/hosts/testhost/plugins/publish/collect_context.py index bbb8477cdf..0ab98fb84b 100644 --- a/openpype/hosts/testhost/plugins/publish/collect_context.py +++ b/openpype/hosts/testhost/plugins/publish/collect_context.py @@ -19,7 +19,7 @@ class CollectContextDataTestHost( hosts = ["testhost"] @classmethod - def get_instance_attr_defs(cls): + def get_attribute_defs(cls): return [ attribute_definitions.BoolDef( "test_bool", diff --git a/openpype/hosts/testhost/plugins/publish/collect_instance_1.py b/openpype/hosts/testhost/plugins/publish/collect_instance_1.py index 979ab83f11..3c035eccb6 100644 --- a/openpype/hosts/testhost/plugins/publish/collect_instance_1.py +++ b/openpype/hosts/testhost/plugins/publish/collect_instance_1.py @@ -20,7 +20,7 @@ class CollectInstanceOneTestHost( hosts = ["testhost"] @classmethod - def get_instance_attr_defs(cls): + def get_attribute_defs(cls): return [ attribute_definitions.NumberDef( "version", From 342fa4c817c740eb8afb5d4c823595f9bd5eeaad Mon Sep 17 00:00:00 2001 From: joblet Date: Wed, 9 Mar 2022 17:50:06 +0100 Subject: [PATCH 155/302] fix phooshop and after effects openPype plugin path --- website/docs/artist_hosts_aftereffects.md | 2 +- website/docs/artist_hosts_photoshop.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/artist_hosts_aftereffects.md b/website/docs/artist_hosts_aftereffects.md index f9ef40fe1a..a9660bd13c 100644 --- a/website/docs/artist_hosts_aftereffects.md +++ b/website/docs/artist_hosts_aftereffects.md @@ -15,7 +15,7 @@ sidebar_label: AfterEffects ## Setup -To install the extension, download, install [Anastasyi's Extension Manager](https://install.anastasiy.com/). Open Anastasyi's Extension Manager and select AfterEffects in menu. Then go to `{path to pype}/repos/avalon-core/avalon/aftereffects/extension.zxp`. +To install the extension, download, install [Anastasyi's Extension Manager](https://install.anastasiy.com/). Open Anastasyi's Extension Manager and select AfterEffects in menu. Then go to `{path to pype}hosts/aftereffects/api/extension.zxp`. Drag extension.zxp and drop it to Anastasyi's Extension Manager. The extension will install itself. diff --git a/website/docs/artist_hosts_photoshop.md b/website/docs/artist_hosts_photoshop.md index 16539bcf79..b2b5fd58da 100644 --- a/website/docs/artist_hosts_photoshop.md +++ b/website/docs/artist_hosts_photoshop.md @@ -14,7 +14,7 @@ sidebar_label: Photoshop ## Setup -To install the extension, download, install [Anastasyi's Extension Manager](https://install.anastasiy.com/). Open Anastasyi's Extension Manager and select Photoshop in menu. Then go to `{path to pype}/repos/avalon-core/avalon/photoshop/extension.zxp`. Drag extension.zxp and drop it to Anastasyi's Extension Manager. The extension will install itself. +To install the extension, download, install [Anastasyi's Extension Manager](https://install.anastasiy.com/). Open Anastasyi's Extension Manager and select Photoshop in menu. Then go to `{path to pype}hosts/photoshop/api/extension.zxp`. Drag extension.zxp and drop it to Anastasyi's Extension Manager. The extension will install itself. ## Usage From c1304f4e691fec99de2d639992dee04a5957bc7a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 18:45:05 +0100 Subject: [PATCH 156/302] it is possible to catch invalid tasks on confirm submit --- openpype/tools/publisher/widgets/widgets.py | 65 ++++++++++++++++----- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index a5528e52a6..ace7b4e02d 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -472,6 +472,28 @@ class AssetsField(BaseClickableFrame): self.set_selected_items(self._origin_value) +class TasksComboboxProxy(QtCore.QSortFilterProxyModel): + def __init__(self, *args, **kwargs): + super(TasksComboboxProxy, self).__init__(*args, **kwargs) + self._filter_empty = False + + def set_filter_empty(self, filter_empty): + if self._filter_empty is filter_empty: + return + self._filter_empty = filter_empty + self.invalidate() + + def filterAcceptsRow(self, source_row, parent_index): + if self._filter_empty: + model = self.sourceModel() + source_index = model.index( + source_row, self.filterKeyColumn(), parent_index + ) + if not source_index.data(QtCore.Qt.DisplayRole): + return False + return True + + class TasksCombobox(QtWidgets.QComboBox): """Combobox to show tasks for selected instances. @@ -492,7 +514,7 @@ class TasksCombobox(QtWidgets.QComboBox): self.setItemDelegate(delegate) model = TasksModel(controller, True) - proxy_model = QtCore.QSortFilterProxyModel() + proxy_model = TasksComboboxProxy() proxy_model.setSourceModel(model) self.setModel(proxy_model) @@ -511,6 +533,14 @@ class TasksCombobox(QtWidgets.QComboBox): self._text = None + def set_invalid_empty_task(self, invalid=True): + self._proxy_model.set_filter_empty(invalid) + if invalid: + self._set_is_valid(False) + self.set_text("< One or more subsets require Task selected >") + else: + self.set_text(None) + def set_multiselection_text(self, text): """Change text shown when multiple different tasks are in context.""" self._multiselection_text = text @@ -600,7 +630,8 @@ class TasksCombobox(QtWidgets.QComboBox): self._ignore_index_change = True self._model.set_asset_names(asset_names) - self._proxy_model.invalidate() + self._proxy_model.set_filter_empty(False) + self._proxy_model.sort(0) self._ignore_index_change = False @@ -646,6 +677,9 @@ class TasksCombobox(QtWidgets.QComboBox): asset_task_combinations (list): List of tuples. Each item in the list contain asset name and task name. """ + self._proxy_model.set_filter_empty(False) + self._proxy_model.sort(0) + if asset_task_combinations is None: asset_task_combinations = [] @@ -1003,21 +1037,19 @@ class GlobalAttrsWidget(QtWidgets.QWidget): project_name = self.controller.project_name subset_names = set() + invalid_tasks = False for instance in self._current_instances: - if variant_value is not None: - instance["variant"] = variant_value - - if asset_name is not None: - instance["asset"] = asset_name - instance.set_asset_invalid(False) - - if task_name is not None: - instance["task"] = task_name - instance.set_task_invalid(False) - new_variant_value = instance.get("variant") new_asset_name = instance.get("asset") new_task_name = instance.get("task") + if variant_value is not None: + new_variant_value = variant_value + + if asset_name is not None: + new_asset_name = asset_name + + if task_name is not None: + new_task_name = task_name asset_doc = asset_docs_by_name[new_asset_name] @@ -1026,7 +1058,9 @@ class GlobalAttrsWidget(QtWidgets.QWidget): new_variant_value, new_task_name, asset_doc, project_name ) except TaskNotSetError: + invalid_tasks = True instance.set_task_invalid(True) + subset_names.add(instance["subset"]) continue subset_names.add(new_subset_name) @@ -1043,10 +1077,13 @@ class GlobalAttrsWidget(QtWidgets.QWidget): instance["subset"] = new_subset_name + if invalid_tasks: + self.task_value_widget.set_invalid_empty_task() + self.subset_value_widget.set_value(subset_names) self._set_btns_enabled(False) - self._set_btns_visible(False) + self._set_btns_visible(invalid_tasks) self.instance_context_changed.emit() From 41bee1d2fac264cac48d3d5f6d2edd5139c5e2b5 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 18:49:57 +0100 Subject: [PATCH 157/302] change checkstate of goups on key press events --- .../tools/publisher/widgets/list_view_widgets.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/list_view_widgets.py b/openpype/tools/publisher/widgets/list_view_widgets.py index 23a86cd070..6bddaf66c8 100644 --- a/openpype/tools/publisher/widgets/list_view_widgets.py +++ b/openpype/tools/publisher/widgets/list_view_widgets.py @@ -467,12 +467,22 @@ class InstanceListView(AbstractInstanceView): else: active = False + group_names = set() for instance_id in selected_instance_ids: widget = self._widgets_by_id.get(instance_id) - if widget is not None: - widget.set_active(active) + if widget is None: + continue + + widget.set_active(active) + group_name = self._group_by_instance_id.get(instance_id) + if group_name is not None: + group_names.add(group_name) + + for group_name in group_names: + self._update_group_checkstate(group_name) def _update_group_checkstate(self, group_name): + """Update checkstate of one group.""" widget = self._group_widgets.get(group_name) if widget is None: return From 5a2f917c2aec35286efe030f5b4f0c28c3fb293d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 18:54:54 +0100 Subject: [PATCH 158/302] empty task is stored as None --- openpype/tools/publisher/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index ace7b4e02d..200e85ba5c 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1072,7 +1072,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): instance.set_asset_invalid(False) if task_name is not None: - instance["task"] = task_name + instance["task"] = task_name or None instance.set_task_invalid(False) instance["subset"] = new_subset_name From 86ddbf8c5cf5455249cfe8d11d58593333190a5e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 18:56:35 +0100 Subject: [PATCH 159/302] task as none will use empty string --- openpype/tools/publisher/widgets/widgets.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 200e85ba5c..8a950feb8b 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1156,9 +1156,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): variants.add(instance.get("variant") or self.unknown_value) families.add(instance.get("family") or self.unknown_value) asset_name = instance.get("asset") or self.unknown_value - task_name = instance.get("task") - if task_name is None: - task_name = self.unknown_value + task_name = instance.get("task") or "" asset_names.add(asset_name) asset_task_combinations.append((asset_name, task_name)) subset_names.add(instance.get("subset") or self.unknown_value) From a7b8cf26260f60c69d212dae7fb611c46503da72 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 19:20:04 +0100 Subject: [PATCH 160/302] add plugin to report before it's processing --- openpype/tools/publisher/control.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 5a84b1d8ca..6707feac9c 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -873,8 +873,6 @@ class PublisherController: """ for idx, plugin in enumerate(self.publish_plugins): self._publish_progress = idx - # Add plugin to publish report - self._publish_report.add_plugin_iter(plugin, self._publish_context) # Reset current plugin validations error self._publish_current_plugin_validation_errors = None @@ -902,6 +900,9 @@ class PublisherController: ): yield MainThreadItem(self.stop_publish) + # Add plugin to publish report + self._publish_report.add_plugin_iter(plugin, self._publish_context) + # Trigger callback that new plugin is going to be processed self._trigger_callbacks( self._publish_plugin_changed_callback_refs, plugin From 23b5f3828a4d248590f1035875eb05d0f674fea4 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 9 Mar 2022 19:20:23 +0100 Subject: [PATCH 161/302] don't use task if is not available in intergrate new --- openpype/plugins/publish/integrate_new.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 6e0940d459..33d365fe42 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -192,11 +192,15 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "short": task_code } - else: + elif "task" in anatomy_data: # Just set 'task_name' variable to context task task_name = anatomy_data["task"]["name"] task_type = anatomy_data["task"]["type"] + else: + task_name = None + task_type = None + # Fill family in anatomy data anatomy_data["family"] = instance.data.get("family") @@ -816,8 +820,12 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): # - is there a chance that task name is not filled in anatomy # data? # - should we use context task in that case? - task_name = instance.data["anatomyData"]["task"]["name"] - task_type = instance.data["anatomyData"]["task"]["type"] + anatomy_data = instance.data["anatomyData"] + task_name = None + task_type = None + if "task" in anatomy_data: + task_name = anatomy_data["task"]["name"] + task_type = anatomy_data["task"]["type"] filtering_criteria = { "families": instance.data["family"], "hosts": instance.context.data["hostName"], From ca94cf247aceac8a3c61d38465625c9c82c85aed Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 10 Mar 2022 09:37:42 +0100 Subject: [PATCH 162/302] add refactor sections to CI --- .github/workflows/prerelease.yml | 2 +- .github/workflows/release.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 258458e2d4..d9b4d8089c 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -43,7 +43,7 @@ jobs: uses: heinrichreimer/github-changelog-generator-action@v2.2 with: token: ${{ secrets.ADMIN_TOKEN }} - addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}}' + addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' issues: false issuesWoLabels: false sinceTag: "3.0.0" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f85525c26..917e6c884c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,7 +39,7 @@ jobs: uses: heinrichreimer/github-changelog-generator-action@v2.2 with: token: ${{ secrets.ADMIN_TOKEN }} - addSections: '{"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]},"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]}}' + addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' issues: false issuesWoLabels: false sinceTag: "3.0.0" @@ -81,7 +81,7 @@ jobs: uses: heinrichreimer/github-changelog-generator-action@v2.2 with: token: ${{ secrets.ADMIN_TOKEN }} - addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}}' + addSections: '{"documentation":{"prefix":"### 📖 Documentation","labels":["type: documentation"]},"tests":{"prefix":"### ✅ Testing","labels":["tests"]},"feature":{"prefix":"**🆕 New features**", "labels":["type: feature"]},"breaking":{"prefix":"**💥 Breaking**", "labels":["breaking"]},"enhancements":{"prefix":"**🚀 Enhancements**", "labels":["type: enhancement"]},"bugs":{"prefix":"**🐛 Bug fixes**", "labels":["type: bug"]},"deprecated":{"prefix":"**⚠️ Deprecations**", "labels":["depreciated"]}, "refactor":{"prefix":"**🔀 Refactored code**", "labels":["refactor"]}}' issues: false issuesWoLabels: false sinceTag: ${{ steps.version.outputs.last_release }} From c274e64c6aac478fac799df50b0cdc8c1d209fe0 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 09:47:39 +0100 Subject: [PATCH 163/302] fix houdini topic --- openpype/hosts/houdini/api/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index e34b8811b9..6cfb713661 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -108,7 +108,7 @@ def on_file_event_callback(event): elif event == hou.hipFileEventType.AfterSave: emit_event("save") elif event == hou.hipFileEventType.BeforeSave: - emit_event("before_save") + emit_event("before.save") elif event == hou.hipFileEventType.AfterClear: emit_event("new") From e61c8d992bc3a2308293b9077bc5ae85fa82ba05 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 10:57:01 +0100 Subject: [PATCH 164/302] fix hardlink for windows --- openpype/lib/path_tools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/lib/path_tools.py b/openpype/lib/path_tools.py index 3a9f835272..851bc872fb 100644 --- a/openpype/lib/path_tools.py +++ b/openpype/lib/path_tools.py @@ -43,6 +43,7 @@ def create_hard_link(src_path, dst_path): res = CreateHardLink(dst_path, src_path, None) if res == 0: raise ctypes.WinError() + return # Raises not implemented error if gets here raise NotImplementedError( "Implementation of hardlink for current environment is missing." From 58aaa25c5536cdc61d1852e132c3e88e313dc587 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 11:56:06 +0100 Subject: [PATCH 165/302] added short description widget --- .../tools/publisher/widgets/create_dialog.py | 101 +++++++++++------- 1 file changed, 63 insertions(+), 38 deletions(-) diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 6396b77901..83418b8bef 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -107,67 +107,100 @@ class CreatorDescriptionWidget(QtWidgets.QWidget): def __init__(self, parent=None): super(CreatorDescriptionWidget, self).__init__(parent=parent) - icon_widget = IconValuePixmapLabel(None, self) + # --- Short description widget --- + short_desc_widget = QtWidgets.QWidget(self) + + icon_widget = IconValuePixmapLabel(None, short_desc_widget) icon_widget.setObjectName("FamilyIconLabel") - family_label = QtWidgets.QLabel("family") + # --- Short description inputs --- + short_desc_input_widget = QtWidgets.QWidget(short_desc_widget) + + family_label = QtWidgets.QLabel(short_desc_input_widget) family_label.setAlignment( QtCore.Qt.AlignBottom | QtCore.Qt.AlignLeft ) - description_label = QtWidgets.QLabel("description") + description_label = QtWidgets.QLabel(short_desc_input_widget) description_label.setAlignment( QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft ) - detail_description_widget = QtWidgets.QTextEdit(self) + short_desc_input_layout = QtWidgets.QVBoxLayout( + short_desc_input_widget + ) + short_desc_input_layout.setSpacing(0) + short_desc_input_layout.addWidget(family_label) + short_desc_input_layout.addWidget(description_label) + # -------------------------------- + + short_desc_layout = QtWidgets.QHBoxLayout(short_desc_widget) + short_desc_layout.setContentsMargins(0, 0, 0, 0) + short_desc_layout.addWidget(icon_widget, 0) + short_desc_layout.addWidget(short_desc_input_widget, 1) + # -------------------------------- + + separator_widget = QtWidgets.QWidget(self) + separator_widget.setObjectName("Separator") + separator_widget.setMinimumHeight(2) + separator_widget.setMaximumHeight(2) + + # --- Bottom part ---------------- + bottom_widget = QtWidgets.QWidget(self) + + # Precreate attributes widget + pre_create_widget = PreCreateWidget(bottom_widget) + + # Detailed description of creator + detail_description_widget = QtWidgets.QTextEdit(bottom_widget) detail_description_widget.setObjectName("InfoText") detail_description_widget.setTextInteractionFlags( QtCore.Qt.TextBrowserInteraction ) + # TODO add HELP button + detail_description_widget.setVisible(False) - label_layout = QtWidgets.QVBoxLayout() - label_layout.setSpacing(0) - label_layout.addWidget(family_label) - label_layout.addWidget(description_label) - - top_layout = QtWidgets.QHBoxLayout() - top_layout.setContentsMargins(0, 0, 0, 0) - top_layout.addWidget(icon_widget, 0) - top_layout.addLayout(label_layout, 1) + bottom_layout = QtWidgets.QHBoxLayout(bottom_widget) + bottom_layout.setContentsMargins(0, 0, 0, 0) + bottom_layout.addWidget(pre_create_widget, 1) + bottom_layout.addWidget(detail_description_widget, 1) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.addLayout(top_layout, 0) - layout.addWidget(detail_description_widget, 1) + layout.addWidget(short_desc_widget, 0) + layout.addWidget(separator_widget, 0) + layout.addWidget(bottom_widget, 1) - self.icon_widget = icon_widget - self.family_label = family_label - self.description_label = description_label - self.detail_description_widget = detail_description_widget + self._icon_widget = icon_widget + self._family_label = family_label + self._description_label = description_label + self._detail_description_widget = detail_description_widget + self._pre_create_widget = pre_create_widget def set_plugin(self, plugin=None): if not plugin: - self.icon_widget.set_icon_def(None) - self.family_label.setText("") - self.description_label.setText("") - self.detail_description_widget.setPlainText("") + self._icon_widget.set_icon_def(None) + self._family_label.setText("") + self._description_label.setText("") + self._detail_description_widget.setPlainText("") + self._pre_create_widget.set_plugin(plugin) return plugin_icon = plugin.get_icon() description = plugin.get_description() or "" detailed_description = plugin.get_detail_description() or "" - self.icon_widget.set_icon_def(plugin_icon) - self.family_label.setText("{}".format(plugin.family)) - self.family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) - self.description_label.setText(description) + self._icon_widget.set_icon_def(plugin_icon) + self._family_label.setText("{}".format(plugin.family)) + self._family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) + self._description_label.setText(description) if commonmark: html = commonmark.commonmark(detailed_description) - self.detail_description_widget.setHtml(html) + self._detail_description_widget.setHtml(html) else: - self.detail_description_widget.setMarkdown(detailed_description) + self._detail_description_widget.setMarkdown(detailed_description) + self._pre_create_widget.set_plugin(plugin) class CreateDialog(QtWidgets.QDialog): @@ -215,12 +248,7 @@ class CreateDialog(QtWidgets.QDialog): context_layout.addWidget(assets_widget, 2) context_layout.addWidget(tasks_widget, 1) - # Precreate attributes widgets - pre_create_widget = PreCreateWidget(self) - - # TODO add HELP button creator_description_widget = CreatorDescriptionWidget(self) - creator_description_widget.setVisible(False) creators_view = QtWidgets.QListView(self) creators_model = QtGui.QStandardItemModel() @@ -264,7 +292,7 @@ class CreateDialog(QtWidgets.QDialog): splitter_widget = QtWidgets.QSplitter(self) splitter_widget.addWidget(context_widget) splitter_widget.addWidget(mid_widget) - splitter_widget.addWidget(pre_create_widget) + splitter_widget.addWidget(creator_description_widget) splitter_widget.setStretchFactor(0, 1) splitter_widget.setStretchFactor(1, 1) splitter_widget.setStretchFactor(2, 1) @@ -295,8 +323,6 @@ class CreateDialog(QtWidgets.QDialog): self._splitter_widget = splitter_widget - self._pre_create_widget = pre_create_widget - self._context_widget = context_widget self._assets_widget = assets_widget self._tasks_widget = tasks_widget @@ -510,7 +536,6 @@ class CreateDialog(QtWidgets.QDialog): creator = self.controller.manual_creators.get(identifier) self.creator_description_widget.set_plugin(creator) - self._pre_create_widget.set_plugin(creator) self._selected_creator = creator From 2bce15f106611e9e3e6572b3d6c6b7cb77509f67 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 11:57:21 +0100 Subject: [PATCH 166/302] changed Name to Variant --- openpype/tools/publisher/widgets/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index 4e55e86491..5ced469b59 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -982,7 +982,7 @@ class GlobalAttrsWidget(QtWidgets.QWidget): btns_layout.addWidget(cancel_btn) main_layout = QtWidgets.QFormLayout(self) - main_layout.addRow("Name", variant_input) + main_layout.addRow("Variant", variant_input) main_layout.addRow("Asset", asset_value_widget) main_layout.addRow("Task", task_value_widget) main_layout.addRow("Family", family_value_widget) From 752af659bee065658e76d7a5ecb0996864de8069 Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Thu, 10 Mar 2022 13:01:52 +0100 Subject: [PATCH 167/302] families is None for group/gizmo --- openpype/hosts/nuke/plugins/publish/precollect_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 97ddef0a59..29c706f302 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -80,7 +80,7 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): # Add all nodes in group instances. if node.Class() == "Group": # only alter families for render family - if "write" in families_ak.lower(): + if families_ak and "write" in families_ak.lower(): target = node["render"].value() if target == "Use existing frames": # Local rendering From 76b630aaac31e03ac867fa6ea4affa6a2c7b82b6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 14:44:07 +0100 Subject: [PATCH 168/302] added description widget and detailed info widget --- openpype/style/style.css | 13 ++ .../tools/publisher/widgets/create_dialog.py | 208 +++++++++++++----- 2 files changed, 165 insertions(+), 56 deletions(-) diff --git a/openpype/style/style.css b/openpype/style/style.css index 5586cf766d..df83600973 100644 --- a/openpype/style/style.css +++ b/openpype/style/style.css @@ -836,6 +836,19 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { } /* New Create/Publish UI */ +#CreateDialogHelpButton { + background: rgba(255, 255, 255, 31); + border-top-right-radius: 0; + border-bottom-right-radius: 0; + font-size: 10pt; + font-weight: bold; + padding: 3px 3px 3px 3px; +} + +#CreateDialogHelpButton:hover { + background: rgba(255, 255, 255, 63); +} + #PublishLogConsole { font-family: "Noto Sans Mono"; } diff --git a/openpype/tools/publisher/widgets/create_dialog.py b/openpype/tools/publisher/widgets/create_dialog.py index 83418b8bef..27ce97955a 100644 --- a/openpype/tools/publisher/widgets/create_dialog.py +++ b/openpype/tools/publisher/widgets/create_dialog.py @@ -103,18 +103,16 @@ class CreateErrorMessageBox(ErrorMessageBox): # TODO add creator identifier/label to details -class CreatorDescriptionWidget(QtWidgets.QWidget): +class CreatorShortDescWidget(QtWidgets.QWidget): def __init__(self, parent=None): - super(CreatorDescriptionWidget, self).__init__(parent=parent) + super(CreatorShortDescWidget, self).__init__(parent=parent) # --- Short description widget --- - short_desc_widget = QtWidgets.QWidget(self) - - icon_widget = IconValuePixmapLabel(None, short_desc_widget) + icon_widget = IconValuePixmapLabel(None, self) icon_widget.setObjectName("FamilyIconLabel") # --- Short description inputs --- - short_desc_input_widget = QtWidgets.QWidget(short_desc_widget) + short_desc_input_widget = QtWidgets.QWidget(self) family_label = QtWidgets.QLabel(short_desc_input_widget) family_label.setAlignment( @@ -134,73 +132,69 @@ class CreatorDescriptionWidget(QtWidgets.QWidget): short_desc_input_layout.addWidget(description_label) # -------------------------------- - short_desc_layout = QtWidgets.QHBoxLayout(short_desc_widget) - short_desc_layout.setContentsMargins(0, 0, 0, 0) - short_desc_layout.addWidget(icon_widget, 0) - short_desc_layout.addWidget(short_desc_input_widget, 1) - # -------------------------------- - - separator_widget = QtWidgets.QWidget(self) - separator_widget.setObjectName("Separator") - separator_widget.setMinimumHeight(2) - separator_widget.setMaximumHeight(2) - - # --- Bottom part ---------------- - bottom_widget = QtWidgets.QWidget(self) - - # Precreate attributes widget - pre_create_widget = PreCreateWidget(bottom_widget) - - # Detailed description of creator - detail_description_widget = QtWidgets.QTextEdit(bottom_widget) - detail_description_widget.setObjectName("InfoText") - detail_description_widget.setTextInteractionFlags( - QtCore.Qt.TextBrowserInteraction - ) - # TODO add HELP button - detail_description_widget.setVisible(False) - - bottom_layout = QtWidgets.QHBoxLayout(bottom_widget) - bottom_layout.setContentsMargins(0, 0, 0, 0) - bottom_layout.addWidget(pre_create_widget, 1) - bottom_layout.addWidget(detail_description_widget, 1) - - layout = QtWidgets.QVBoxLayout(self) + layout = QtWidgets.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) - layout.addWidget(short_desc_widget, 0) - layout.addWidget(separator_widget, 0) - layout.addWidget(bottom_widget, 1) + layout.addWidget(icon_widget, 0) + layout.addWidget(short_desc_input_widget, 1) + # -------------------------------- self._icon_widget = icon_widget self._family_label = family_label self._description_label = description_label - self._detail_description_widget = detail_description_widget - self._pre_create_widget = pre_create_widget def set_plugin(self, plugin=None): if not plugin: self._icon_widget.set_icon_def(None) self._family_label.setText("") self._description_label.setText("") - self._detail_description_widget.setPlainText("") - self._pre_create_widget.set_plugin(plugin) return plugin_icon = plugin.get_icon() description = plugin.get_description() or "" - detailed_description = plugin.get_detail_description() or "" self._icon_widget.set_icon_def(plugin_icon) self._family_label.setText("{}".format(plugin.family)) self._family_label.setTextInteractionFlags(QtCore.Qt.NoTextInteraction) self._description_label.setText(description) - if commonmark: - html = commonmark.commonmark(detailed_description) - self._detail_description_widget.setHtml(html) + +class HelpButton(QtWidgets.QPushButton): + resized = QtCore.Signal() + + def __init__(self, *args, **kwargs): + super(HelpButton, self).__init__(*args, **kwargs) + self.setObjectName("CreateDialogHelpButton") + + self._expanded = None + self.set_expanded() + + def set_expanded(self, expanded=None): + if self._expanded is expanded: + if expanded is not None: + return + expanded = False + self._expanded = expanded + if expanded: + text = "<" else: - self._detail_description_widget.setMarkdown(detailed_description) - self._pre_create_widget.set_plugin(plugin) + text = "?" + self.setText(text) + + self._update_size() + + def _update_size(self): + new_size = self.minimumSizeHint() + if self.size() != new_size: + self.resize(new_size) + self.resized.emit() + + def showEvent(self, event): + super(HelpButton, self).showEvent(event) + self._update_size() + + def resizeEvent(self, event): + super(HelpButton, self).resizeEvent(event) + self._update_size() class CreateDialog(QtWidgets.QDialog): @@ -248,8 +242,7 @@ class CreateDialog(QtWidgets.QDialog): context_layout.addWidget(assets_widget, 2) context_layout.addWidget(tasks_widget, 1) - creator_description_widget = CreatorDescriptionWidget(self) - + # --- Creators view --- creators_view = QtWidgets.QListView(self) creators_model = QtGui.QStandardItemModel() creators_view.setModel(creators_model) @@ -288,24 +281,65 @@ class CreateDialog(QtWidgets.QDialog): mid_layout.addWidget(creators_view, 1) mid_layout.addLayout(form_layout, 0) mid_layout.addWidget(create_btn, 0) + # ------------ + + # --- Creator short info and attr defs --- + creator_attrs_widget = QtWidgets.QWidget(self) + + creator_short_desc_widget = CreatorShortDescWidget( + creator_attrs_widget + ) + + separator_widget = QtWidgets.QWidget(self) + separator_widget.setObjectName("Separator") + separator_widget.setMinimumHeight(2) + separator_widget.setMaximumHeight(2) + + # Precreate attributes widget + pre_create_widget = PreCreateWidget(creator_attrs_widget) + + creator_attrs_layout = QtWidgets.QVBoxLayout(creator_attrs_widget) + creator_attrs_layout.setContentsMargins(0, 0, 0, 0) + creator_attrs_layout.addWidget(creator_short_desc_widget, 0) + creator_attrs_layout.addWidget(separator_widget, 0) + creator_attrs_layout.addWidget(pre_create_widget, 1) + # ------------------------------------- + + # --- Detailed information about creator --- + # Detailed description of creator + detail_description_widget = QtWidgets.QTextEdit(self) + detail_description_widget.setObjectName("InfoText") + detail_description_widget.setTextInteractionFlags( + QtCore.Qt.TextBrowserInteraction + ) + detail_description_widget.setVisible(False) + # ------------------------------------------- splitter_widget = QtWidgets.QSplitter(self) splitter_widget.addWidget(context_widget) splitter_widget.addWidget(mid_widget) - splitter_widget.addWidget(creator_description_widget) + splitter_widget.addWidget(creator_attrs_widget) + splitter_widget.addWidget(detail_description_widget) splitter_widget.setStretchFactor(0, 1) splitter_widget.setStretchFactor(1, 1) splitter_widget.setStretchFactor(2, 1) + splitter_widget.setStretchFactor(3, 1) layout = QtWidgets.QHBoxLayout(self) layout.addWidget(splitter_widget, 1) + # Floating help button + help_btn = HelpButton(self) + prereq_timer = QtCore.QTimer() prereq_timer.setInterval(50) prereq_timer.setSingleShot(True) prereq_timer.timeout.connect(self._on_prereq_timer) + help_btn.clicked.connect(self._on_help_btn) + help_btn.resized.connect(self._on_help_btn_resize) + create_btn.clicked.connect(self._on_create) variant_input.returnPressed.connect(self._on_create) variant_input.textChanged.connect(self._on_variant_change) @@ -326,7 +360,6 @@ class CreateDialog(QtWidgets.QDialog): self._context_widget = context_widget self._assets_widget = assets_widget self._tasks_widget = tasks_widget - self.creator_description_widget = creator_description_widget self.subset_name_input = subset_name_input @@ -339,6 +372,11 @@ class CreateDialog(QtWidgets.QDialog): self.creators_view = creators_view self.create_btn = create_btn + self._creator_short_desc_widget = creator_short_desc_widget + self._pre_create_widget = pre_create_widget + self._detail_description_widget = detail_description_widget + self._help_btn = help_btn + self._prereq_timer = prereq_timer self._first_show = True @@ -532,10 +570,62 @@ class CreateDialog(QtWidgets.QDialog): identifier = new_index.data(CREATOR_IDENTIFIER_ROLE) self._set_creator(identifier) + def _update_help_btn(self): + pos_x = self.width() - self._help_btn.width() + point = self._creator_short_desc_widget.rect().topRight() + mapped_point = self._creator_short_desc_widget.mapTo(self, point) + pos_y = mapped_point.y() + self._help_btn.move(max(0, pos_x), max(0, pos_y)) + + def _on_help_btn_resize(self): + self._update_help_btn() + + def _on_help_btn(self): + final_size = self.size() + cur_sizes = self._splitter_widget.sizes() + spacing = self._splitter_widget.handleWidth() + + sizes = [] + for idx, value in enumerate(cur_sizes): + if idx < 3: + sizes.append(value) + + now_visible = self._detail_description_widget.isVisible() + if now_visible: + width = final_size.width() - ( + spacing + self._detail_description_widget.width() + ) + + else: + last_size = self._detail_description_widget.sizeHint().width() + width = final_size.width() + spacing + last_size + sizes.append(last_size) + + final_size.setWidth(width) + + self._detail_description_widget.setVisible(not now_visible) + self._splitter_widget.setSizes(sizes) + self.resize(final_size) + + self._help_btn.set_expanded(not now_visible) + + def _set_creator_detailed_text(self, creator): + if not creator: + self._detail_description_widget.setPlainText("") + return + detailed_description = creator.get_detail_description() or "" + if commonmark: + html = commonmark.commonmark(detailed_description) + self._detail_description_widget.setHtml(html) + else: + self._detail_description_widget.setMarkdown(detailed_description) + def _set_creator(self, identifier): creator = self.controller.manual_creators.get(identifier) - self.creator_description_widget.set_plugin(creator) + self._creator_short_desc_widget.set_plugin(creator) + self._set_creator_detailed_text(creator) + self._pre_create_widget.set_plugin(creator) self._selected_creator = creator @@ -694,8 +784,14 @@ class CreateDialog(QtWidgets.QDialog): if self._last_pos is not None: self.move(self._last_pos) + self._update_help_btn() + self.refresh() + def resizeEvent(self, event): + super(CreateDialog, self).resizeEvent(event) + self._update_help_btn() + def _on_create(self): indexes = self.creators_view.selectedIndexes() if not indexes or len(indexes) > 1: From be19709107bc7c33781175c6948fa43b38891cd1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 15:56:02 +0100 Subject: [PATCH 169/302] nuke: fix slate check for frame length --- openpype/hosts/nuke/plugins/publish/extract_slate_frame.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py b/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py index 50e5f995f4..a91181c81b 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py +++ b/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py @@ -48,8 +48,13 @@ class ExtractSlateFrame(openpype.api.Extractor): self.log.info( "StagingDir `{0}`...".format(instance.data["stagingDir"])) + frame_start = instance.data["frameStart"] + frame_end = instance.data["frameEnd"] + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleEnd"] + frame_length = int( - instance.data["frameEnd"] - instance.data["frameStart"] + 1 + (frame_start - frame_end + 1) + (handle_start + handle_end) ) temporary_nodes = [] From 32923208687a221fb11f2f85cb43680292bc3981 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 15:56:39 +0100 Subject: [PATCH 170/302] implemented get asset icon function --- openpype/tools/utils/__init__.py | 1 + openpype/tools/utils/assets_widget.py | 27 +++++------------ openpype/tools/utils/lib.py | 42 +++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index c15e9f8139..6ab9e75b52 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -16,6 +16,7 @@ from .lib import ( set_style_property, DynamicQThread, qt_app_context, + get_asset_icon, ) from .models import ( diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index d410b0f1c3..4c77b81c0e 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -16,7 +16,10 @@ from .views import ( ) from .widgets import PlaceholderLineEdit from .models import RecursiveSortFilterProxyModel -from .lib import DynamicQThread +from .lib import ( + DynamicQThread, + get_asset_icon +) if Qt.__binding__ == "PySide": from PySide.QtGui import QStyleOptionViewItemV4 @@ -508,25 +511,9 @@ class AssetModel(QtGui.QStandardItemModel): item.setData(asset_label, QtCore.Qt.DisplayRole) item.setData(asset_label, ASSET_LABEL_ROLE) - icon_color = asset_data.get("color") or style.colors.default - icon_name = asset_data.get("icon") - if not icon_name: - # Use default icons if no custom one is specified. - # If it has children show a full folder, otherwise - # show an open folder - if item.rowCount() > 0: - icon_name = "folder" - else: - icon_name = "folder-o" - - try: - # font-awesome key - full_icon_name = "fa.{0}".format(icon_name) - icon = qtawesome.icon(full_icon_name, color=icon_color) - item.setData(icon, QtCore.Qt.DecorationRole) - - except Exception: - pass + has_children = item.rowCount() > 0 + icon = get_asset_icon(asset_data, has_children) + item.setData(icon, QtCore.Qt.DecorationRole) def _threaded_fetch(self): asset_docs = self._fetch_asset_docs() diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 1cbc632804..d57b44728d 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -98,6 +98,48 @@ application = qt_app_context class SharedObjects: jobs = {} + icons = {} + + +def get_qta_icon_by_name_and_color(icon_name, icon_color): + if not icon_name or not icon_color: + return None + + full_icon_name = "{0}-{1}".format(icon_name, icon_color) + if full_icon_name in SharedObjects.icons: + return SharedObjects.icons[full_icon_name] + + variants = [icon_name] + qta_instance = qtawesome._instance() + for key in qta_instance.charmap.keys(): + variants.append("{0}.{1}".format(key, icon_name)) + + icon = None + for variant in variants: + try: + icon = qtawesome.icon(variant, color=icon_color) + break + except Exception: + pass + + SharedObjects.icons[full_icon_name] = icon + return icon + + +def get_asset_icon(asset_doc, has_children=False): + asset_data = asset_doc.get("data") or {} + icon_color = asset_data.get("color") or style.colors.default + icon_name = asset_data.get("icon") + if not icon_name: + # Use default icons if no custom one is specified. + # If it has children show a full folder, otherwise + # show an open folder + if has_children: + icon_name = "folder" + else: + icon_name = "folder-o" + + return get_qta_icon_by_name_and_color(icon_name, icon_color) def schedule(func, time, channel="default"): From ff440612a2bc00b84ebd9275192f12025732bb62 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 15:56:50 +0100 Subject: [PATCH 171/302] added icons into publisher UI --- openpype/tools/publisher/widgets/assets_widget.py | 7 ++++++- openpype/tools/publisher/widgets/tasks_widget.py | 3 +++ openpype/tools/utils/lib.py | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/widgets/assets_widget.py b/openpype/tools/publisher/widgets/assets_widget.py index b8696a2665..984da59c77 100644 --- a/openpype/tools/publisher/widgets/assets_widget.py +++ b/openpype/tools/publisher/widgets/assets_widget.py @@ -3,7 +3,8 @@ import collections from Qt import QtWidgets, QtCore, QtGui from openpype.tools.utils import ( PlaceholderLineEdit, - RecursiveSortFilterProxyModel + RecursiveSortFilterProxyModel, + get_asset_icon, ) from openpype.tools.utils.assets_widget import ( SingleSelectAssetsWidget, @@ -102,11 +103,15 @@ class AssetsHierarchyModel(QtGui.QStandardItemModel): for name in sorted(children_by_name.keys()): child = children_by_name[name] child_id = child["_id"] + has_children = bool(assets_by_parent_id.get(child_id)) + icon = get_asset_icon(child, has_children) + item = QtGui.QStandardItem(name) item.setFlags( QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable ) + item.setData(icon, QtCore.Qt.DecorationRole) item.setData(child_id, ASSET_ID_ROLE) item.setData(name, ASSET_NAME_ROLE) diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index 2d1cc017af..8a913b7114 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -1,6 +1,7 @@ from Qt import QtCore, QtGui from openpype.tools.utils.tasks_widget import TasksWidget, TASK_NAME_ROLE +from openpype.tools.utils.lib import get_task_icon class TasksModel(QtGui.QStandardItemModel): @@ -118,6 +119,8 @@ class TasksModel(QtGui.QStandardItemModel): item = QtGui.QStandardItem(task_name) item.setData(task_name, TASK_NAME_ROLE) + if task_name: + item.setData(get_task_icon(), QtCore.Qt.DecorationRole) self._items_by_name[task_name] = item new_items.append(item) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index d57b44728d..042ceaab88 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -142,6 +142,16 @@ def get_asset_icon(asset_doc, has_children=False): return get_qta_icon_by_name_and_color(icon_name, icon_color) +def get_task_icon(): + """Get icon for a task. + + TODO: Get task icon based on data in database. + + Icon should be defined by task type which is stored on project. + """ + return get_qta_icon_by_name_and_color("fa.male", style.colors.default) + + def schedule(func, time, channel="default"): """Run `func` at a later `time` in a dedicated `channel` From 87719ed878a55ec096a9cf57f0e3493577b3b3ce Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 15:59:28 +0100 Subject: [PATCH 172/302] nuke: wrong expression --- openpype/hosts/nuke/plugins/publish/extract_slate_frame.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py b/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py index a91181c81b..e917a28046 100644 --- a/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py +++ b/openpype/hosts/nuke/plugins/publish/extract_slate_frame.py @@ -54,7 +54,7 @@ class ExtractSlateFrame(openpype.api.Extractor): handle_end = instance.data["handleEnd"] frame_length = int( - (frame_start - frame_end + 1) + (handle_start + handle_end) + (frame_end - frame_start + 1) + (handle_start + handle_end) ) temporary_nodes = [] From 0b36fc2c65356b9b787cb79e58cf3822c4bd1e81 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 16:08:12 +0100 Subject: [PATCH 173/302] fixing reformat in extract review when slate reformate --- openpype/plugins/publish/extract_review.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index bec1f75425..f9a02f58bb 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1165,6 +1165,18 @@ class ExtractReview(pyblish.api.InstancePlugin): input_height = int(stream["height"]) break + # Get instance data + pixel_aspect = temp_data["pixel_aspect"] + + if reformat_in_baking: + self.log.debug(( + "Using resolution from input. It is already " + "reformated from upstream process" + )) + pixel_aspect = 1 + output_width = input_width + output_height = input_height + # Raise exception of any stream didn't define input resolution if input_width is None: raise AssertionError(( From 99d7912495b5a57eba48f5d614aac6cd08e7d1a3 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 16:12:11 +0100 Subject: [PATCH 174/302] defining default none values for Output resolution --- openpype/plugins/publish/extract_review.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index f9a02f58bb..0b139a73e4 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1159,6 +1159,8 @@ class ExtractReview(pyblish.api.InstancePlugin): # - there may be a better way (checking `codec_type`?) input_width = None input_height = None + output_width = None + output_height = None for stream in streams: if "width" in stream and "height" in stream: input_width = int(stream["width"]) @@ -1185,8 +1187,8 @@ class ExtractReview(pyblish.api.InstancePlugin): # NOTE Setting only one of `width` or `heigth` is not allowed # - settings value can't have None but has value of 0 - output_width = output_def.get("width") or None - output_height = output_def.get("height") or None + output_width = output_width or output_def.get("width") or None + output_height = output_height or output_def.get("height") or None # Overscal color overscan_color_value = "black" From 68171265dc684490b2594dec218c8f82ed100ec3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 17:41:58 +0100 Subject: [PATCH 175/302] fix plugin name from 'oiio' to 'OpenPypeTileAssembler' --- openpype/settings/defaults/project_settings/deadline.json | 2 +- .../schemas/projects_schema/schema_project_deadline.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 7aff6d0b30..5bb0a4022e 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -46,7 +46,7 @@ "enabled": true, "optional": false, "active": true, - "tile_assembler_plugin": "oiio", + "tile_assembler_plugin": "OpenPypeTileAssembler", "use_published": true, "asset_dependencies": true, "group": "none", diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 788b1d8575..e6097a2b14 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -103,7 +103,7 @@ "DraftTileAssembler": "Draft Tile Assembler" }, { - "oiio": "Open Image IO" + "OpenPypeTileAssembler": "Open Image IO" } ] }, From 2f4b165aafaa71d600b0e61376d11c5fab78ebac Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 10 Mar 2022 18:55:59 +0100 Subject: [PATCH 176/302] fix info reading from oiio --- openpype/lib/transcoding.py | 19 +- .../OpenPypeTileAssembler.py | 269 ++++++++++++++---- 2 files changed, 233 insertions(+), 55 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index e89fa6331e..462745bcda 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -90,7 +90,7 @@ class RationalToInt: if len(parts) != 1: bottom = float(parts[1]) - self._value = top / bottom + self._value = float(top) / float(bottom) self._string_value = string_value @property @@ -170,6 +170,23 @@ def convert_value_by_type_name(value_type, value, logger=None): if value_type == "rational2i": return RationalToInt(value) + if value_type == "vector": + parts = [part.strip() for part in value.split(",")] + output = [] + for part in parts: + if part == "-nan": + output.append(None) + continue + try: + part = float(part) + except ValueError: + pass + output.append(part) + return output + + if value_type == "timecode": + return value + # Array of other types is converted to list re_result = ARRAY_TYPE_REGEX.findall(value_type) if re_result: diff --git a/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py b/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py index cf864b03d3..9fca1b5391 100644 --- a/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py +++ b/openpype/modules/deadline/repository/custom/plugins/OpenPypeTileAssembler/OpenPypeTileAssembler.py @@ -5,8 +5,9 @@ Todo: Currently we support only EXRs with their data window set. """ import os +import re import subprocess -from xml.dom import minidom +import xml.etree.ElementTree from System.IO import Path @@ -15,17 +16,220 @@ from Deadline.Scripting import ( FileUtils, RepositoryUtils, SystemUtils) -INT_KEYS = { - "x", "y", "height", "width", "full_x", "full_y", - "full_width", "full_height", "full_depth", "full_z", - "tile_width", "tile_height", "tile_depth", "deep", "depth", - "nchannels", "z_channel", "alpha_channel", "subimages" +STRING_TAGS = { + "format" } -LIST_KEYS = { - "channelnames" +INT_TAGS = { + "x", "y", "z", + "width", "height", "depth", + "full_x", "full_y", "full_z", + "full_width", "full_height", "full_depth", + "tile_width", "tile_height", "tile_depth", + "nchannels", + "alpha_channel", + "z_channel", + "deep", + "subimages", } +XML_CHAR_REF_REGEX_HEX = re.compile(r"&#x?[0-9a-fA-F]+;") + +# Regex to parse array attributes +ARRAY_TYPE_REGEX = re.compile(r"^(int|float|string)\[\d+\]$") + + +def convert_value_by_type_name(value_type, value): + """Convert value to proper type based on type name. + + In some cases value types have custom python class. + """ + + # Simple types + if value_type == "string": + return value + + if value_type == "int": + return int(value) + + if value_type == "float": + return float(value) + + # Vectors will probably have more types + if value_type == "vec2f": + return [float(item) for item in value.split(",")] + + # Matrix should be always have square size of element 3x3, 4x4 + # - are returned as list of lists + if value_type == "matrix": + output = [] + current_index = -1 + parts = value.split(",") + parts_len = len(parts) + if parts_len == 1: + divisor = 1 + elif parts_len == 4: + divisor = 2 + elif parts_len == 9: + divisor == 3 + elif parts_len == 16: + divisor = 4 + else: + print("Unknown matrix resolution {}. Value: \"{}\"".format( + parts_len, value + )) + for part in parts: + output.append(float(part)) + return output + + for idx, item in enumerate(parts): + list_index = idx % divisor + if list_index > current_index: + current_index = list_index + output.append([]) + output[list_index].append(float(item)) + return output + + if value_type == "rational2i": + parts = value.split("/") + top = float(parts[0]) + bottom = 1.0 + if len(parts) != 1: + bottom = float(parts[1]) + return float(top) / float(bottom) + + if value_type == "vector": + parts = [part.strip() for part in value.split(",")] + output = [] + for part in parts: + if part == "-nan": + output.append(None) + continue + try: + part = float(part) + except ValueError: + pass + output.append(part) + return output + + if value_type == "timecode": + return value + + # Array of other types is converted to list + re_result = ARRAY_TYPE_REGEX.findall(value_type) + if re_result: + array_type = re_result[0] + output = [] + for item in value.split(","): + output.append( + convert_value_by_type_name(array_type, item) + ) + return output + + print(( + "MISSING IMPLEMENTATION:" + " Unknown attrib type \"{}\". Value: {}" + ).format(value_type, value)) + return value + + +def parse_oiio_xml_output(xml_string): + """Parse xml output from OIIO info command.""" + output = {} + if not xml_string: + return output + + # Fix values with ampresand (lazy fix) + # - oiiotool exports invalid xml which ElementTree can't handle + # e.g. "" + # WARNING: this will affect even valid character entities. If you need + # those values correctly, this must take care of valid character ranges. + # See https://github.com/pypeclub/OpenPype/pull/2729 + matches = XML_CHAR_REF_REGEX_HEX.findall(xml_string) + for match in matches: + new_value = match.replace("&", "&") + xml_string = xml_string.replace(match, new_value) + + tree = xml.etree.ElementTree.fromstring(xml_string) + attribs = {} + output["attribs"] = attribs + for child in tree: + tag_name = child.tag + if tag_name == "attrib": + attrib_def = child.attrib + value = convert_value_by_type_name( + attrib_def["type"], child.text + ) + + attribs[attrib_def["name"]] = value + continue + + # Channels are stored as tex on each child + if tag_name == "channelnames": + value = [] + for channel in child: + value.append(channel.text) + + # Convert known integer type tags to int + elif tag_name in INT_TAGS: + value = int(child.text) + + # Keep value of known string tags + elif tag_name in STRING_TAGS: + value = child.text + + # Keep value as text for unknown tags + # - feel free to add more tags + else: + value = child.text + print(( + "MISSING IMPLEMENTATION:" + " Unknown tag \"{}\". Value \"{}\"" + ).format(tag_name, value)) + + output[child.tag] = value + + return output + + +def info_about_input(oiiotool_path, filepath): + args = [ + oiiotool_path, + "--info", + "-v", + "-i:infoformat=xml", + filepath + ] + popen = subprocess.Popen(args, stdout=subprocess.PIPE) + _stdout, _stderr = popen.communicate() + output = "" + if _stdout: + output += _stdout.decode("utf-8") + + if _stderr: + output += _stderr.decode("utf-8") + + output = output.replace("\r\n", "\n") + xml_started = False + lines = [] + for line in output.split("\n"): + if not xml_started: + if not line.startswith("<"): + continue + xml_started = True + if xml_started: + lines.append(line) + + if not xml_started: + raise ValueError( + "Failed to read input file \"{}\".\nOutput:\n{}".format( + filepath, output + ) + ) + xml_text = "\n".join(lines) + return parse_oiio_xml_output(xml_text) + + def GetDeadlinePlugin(): # noqa: N802 """Helper.""" return OpenPypeTileAssembler() @@ -218,8 +422,9 @@ class OpenPypeTileAssembler(DeadlinePlugin): # Create new image with output resolution, and with same type and # channels as input + oiiotool_path = self.render_executable() first_tile_path = tile_info[0]["filepath"] - first_tile_info = self.info_about_input(first_tile_path) + first_tile_info = info_about_input(oiiotool_path, first_tile_path) create_arg_template = "--create{} {}x{} {}" image_type = "" @@ -236,7 +441,7 @@ class OpenPypeTileAssembler(DeadlinePlugin): for tile in tile_info: path = tile["filepath"] pos_x = tile["pos_x"] - tile_height = self.info_about_input(path)["height"] + tile_height = info_about_input(oiiotool_path, path)["height"] if self.renderer == "vray": pos_y = tile["pos_y"] else: @@ -326,47 +531,3 @@ class OpenPypeTileAssembler(DeadlinePlugin): ffmpeg_args.append("\"{}\"".format(output_path)) return ffmpeg_args - - def info_about_input(self, input_path): - args = [self.render_executable(), "--info:format=xml", input_path] - popen = subprocess.Popen( - " ".join(args), - shell=True, - stdout=subprocess.PIPE - ) - popen_output = popen.communicate()[0].replace(b"\r\n", b"") - - xmldoc = minidom.parseString(popen_output) - image_spec = None - for main_child in xmldoc.childNodes: - if main_child.nodeName.lower() == "imagespec": - image_spec = main_child - break - - info = {} - if not image_spec: - return info - - def child_check(node): - if len(node.childNodes) != 1: - self.FailRender(( - "Implementation BUG. Node {} has more children than 1" - ).format(node.nodeName)) - - for child in image_spec.childNodes: - if child.nodeName in LIST_KEYS: - values = [] - for node in child.childNodes: - child_check(node) - values.append(node.childNodes[0].nodeValue) - - info[child.nodeName] = values - - elif child.nodeName in INT_KEYS: - child_check(child) - info[child.nodeName] = int(child.childNodes[0].nodeValue) - - else: - child_check(child) - info[child.nodeName] = child.childNodes[0].nodeValue - return info From 2f9dcc0c55683c6e5a338dda6a05fdd1dafd250e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 22:51:15 +0100 Subject: [PATCH 177/302] flame: define fps table --- openpype/hosts/flame/hooks/pre_flame_setup.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 0d63b0d926..5db5757d50 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -63,7 +63,7 @@ class FlamePrelaunch(PreLaunchHook): _db_p_data = project_doc["data"] width = _db_p_data["resolutionWidth"] height = _db_p_data["resolutionHeight"] - fps = float(_db_p_data["fps"]) + fps = float(_db_p_data["fps_string"]) project_data = { "Name": project_doc["name"], @@ -73,7 +73,7 @@ class FlamePrelaunch(PreLaunchHook): "FrameWidth": int(width), "FrameHeight": int(height), "AspectRatio": float((width / height) * _db_p_data["pixelAspect"]), - "FrameRate": "{} fps".format(fps), + "FrameRate": self._get_flame_fps(fps), "FrameDepth": str(imageio_flame["project"]["frameDepth"]), "FieldDominance": str(imageio_flame["project"]["fieldDominance"]) } @@ -101,6 +101,28 @@ class FlamePrelaunch(PreLaunchHook): self.launch_context.launch_args.extend(app_arguments) + def _get_flame_fps(self, fps_num): + fps_table = { + float(23.976): "23.976 fps", + int(25): "25 fps", + int(24): "24 fps", + float(29.97): "29.97 fps DF", + int(30): "30 fps", + int(50): "50 fps", + float(59.94): "59.94 fps DF", + int(60): "60 fps" + } + + match_key = min(fps_table.keys(), key=lambda x: abs(x - fps_num)) + + try: + return fps_table[match_key] + except KeyError as msg: + raise KeyError(( + "Missing FPS key in conversion table. " + "Following keys are available: {}".format(fps_table.keys()) + )) from msg + def _add_pythonpath(self): pythonpath = self.launch_context.env.get("PYTHONPATH") From 3996223ce530158b84101203f2b957874c90c470 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 10 Mar 2022 23:21:00 +0100 Subject: [PATCH 178/302] flame: colour policy can be path --- openpype/hosts/flame/api/scripts/wiretap_com.py | 9 ++++++++- .../projects_schema/schemas/schema_anatomy_imageio.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/scripts/wiretap_com.py b/openpype/hosts/flame/api/scripts/wiretap_com.py index c864399608..ee906c2608 100644 --- a/openpype/hosts/flame/api/scripts/wiretap_com.py +++ b/openpype/hosts/flame/api/scripts/wiretap_com.py @@ -420,13 +420,20 @@ class WireTapCom(object): RuntimeError: Not able to set colorspace policy """ color_policy = color_policy or "Legacy" + + # check if the colour policy in custom dir + if not os.path.exists(color_policy): + color_policy = "/syncolor/policies/Autodesk/{}".format( + color_policy) + + # create arguments project_colorspace_cmd = [ os.path.join( self.wiretap_tools_dir, "wiretap_duplicate_node" ), "-s", - "/syncolor/policies/Autodesk/{}".format(color_policy), + color_policy, "-n", "/projects/{}/syncolor".format(project_name) ] diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index 3bec19c3d0..2867332d82 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -457,7 +457,7 @@ { "type": "text", "key": "colourPolicy", - "label": "Colour Policy" + "label": "Colour Policy (name or path)" }, { "type": "text", From bdcb4ab8b32d2e63215acdbb9c01715007d53c93 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 11 Mar 2022 00:06:36 +0100 Subject: [PATCH 179/302] move method to parent --- openpype/hosts/maya/api/plugin.py | 19 ++++++++++++++++--- .../hosts/maya/plugins/load/load_reference.py | 12 +----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index bdb8fcf13a..e5edb39d20 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -5,6 +5,7 @@ from maya import cmds from avalon import api from avalon.vendor import qargparse from openpype.api import PypeCreatorMixin +from avalon.pipeline import AVALON_CONTAINER_ID from .pipeline import containerise from . import lib @@ -168,16 +169,18 @@ class ReferenceLoader(Loader): return ref_node = get_reference_node(nodes, self.log) - loaded_containers.append(containerise( + container = containerise( name=name, namespace=namespace, nodes=[ref_node], context=context, loader=self.__class__.__name__ - )) - + ) + loaded_containers.append(container) + self._organize_containers([ref_node], container) c += 1 namespace = None + return loaded_containers def process_reference(self, context, name, namespace, data): @@ -310,3 +313,13 @@ class ReferenceLoader(Loader): deleteNamespaceContent=True) except RuntimeError: pass + + @staticmethod + def _organize_containers(nodes, container): + # type: (list, str) -> None + for node in nodes: + id_attr = "{}.id".format(node) + if not cmds.attributeQuery("id", node=node, exists=True): + continue + if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: + cmds.sets(node, forceElement=container) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 017f89d08c..ece02bef49 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -1,7 +1,7 @@ import os from maya import cmds from avalon import api -from avalon.pipeline import AVALON_CONTAINER_ID + from openpype.api import get_project_settings from openpype.lib import get_creator_by_name import openpype.hosts.maya.api.plugin @@ -165,13 +165,3 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): options={"useSelection": True}, data={"dependencies": dependency} ) - - @staticmethod - def _organize_containers(nodes, container): - # type: (list, str) -> None - for node in nodes: - id_attr = "{}.id".format(node) - if not cmds.attributeQuery("id", node=node, exists=True): - continue - if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID: - cmds.sets(node, forceElement=container) From c92943ca1addeded642b5f5cdd69f732378d1d50 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 11 Mar 2022 00:18:59 +0100 Subject: [PATCH 180/302] remove case insensivity in family processing --- .../hosts/maya/plugins/publish/extract_maya_scene_raw.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 591789917e..5cc7b52090 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -59,10 +59,9 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): members = instance[:] loaded_containers = None - if {f.lower() for f in self.add_for_families}.intersection( - {f.lower() for f in instance.data.get("families")}, - {instance.data.get("family").lower()}, - ): + if set(self.add_for_families).intersection( + set(instance.data.get("families")), + set(instance.data.get("family").lower())): loaded_containers = self._add_loaded_containers(members) selection = members From 21351af22e05067dcff38e5aea074eed175564b0 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Fri, 11 Mar 2022 09:14:05 +0000 Subject: [PATCH 181/302] [Automated] Bump version --- CHANGELOG.md | 40 +++++++++++++++++++++------------------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa479d8f05..f971c33208 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [3.9.0-nightly.7](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0-nightly.8](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) @@ -9,15 +9,13 @@ - AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845) - Houdini: Remove unused code [\#2779](https://github.com/pypeclub/OpenPype/pull/2779) -### 📖 Documentation - -- Documentation: fixed broken links [\#2799](https://github.com/pypeclub/OpenPype/pull/2799) -- Documentation: broken link fix [\#2785](https://github.com/pypeclub/OpenPype/pull/2785) -- Various testing updates [\#2726](https://github.com/pypeclub/OpenPype/pull/2726) - **🚀 Enhancements** +- NewPublisher: Descriptions and Icons in creator dialog [\#2867](https://github.com/pypeclub/OpenPype/pull/2867) +- NewPublisher: Changing task on publishing instance [\#2863](https://github.com/pypeclub/OpenPype/pull/2863) +- TrayPublisher: Choose project widget is more clear [\#2859](https://github.com/pypeclub/OpenPype/pull/2859) - New: Validation exceptions [\#2841](https://github.com/pypeclub/OpenPype/pull/2841) +- Maya: add loaded containers to published instance [\#2837](https://github.com/pypeclub/OpenPype/pull/2837) - Ftrack: Can sync fps as string [\#2836](https://github.com/pypeclub/OpenPype/pull/2836) - General: Custom function for find executable [\#2822](https://github.com/pypeclub/OpenPype/pull/2822) - General: Color dialog UI fixes [\#2817](https://github.com/pypeclub/OpenPype/pull/2817) @@ -25,11 +23,16 @@ - Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811) - Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805) - General: Set context environments for non host applications [\#2803](https://github.com/pypeclub/OpenPype/pull/2803) -- Tray publisher: New Tray Publisher host \(beta\) [\#2778](https://github.com/pypeclub/OpenPype/pull/2778) -- Flame: use Shot Name on segment for asset name [\#2751](https://github.com/pypeclub/OpenPype/pull/2751) **🐛 Bug fixes** +- Deadline: Fix plugin name for tile assemble [\#2868](https://github.com/pypeclub/OpenPype/pull/2868) +- General: Fix hardlink for windows [\#2864](https://github.com/pypeclub/OpenPype/pull/2864) +- General: ffmpeg was crashing on slate merge [\#2860](https://github.com/pypeclub/OpenPype/pull/2860) +- WebPublisher: Video file was published with one too many frame [\#2858](https://github.com/pypeclub/OpenPype/pull/2858) +- New Publisher: Error dialog got right styles [\#2857](https://github.com/pypeclub/OpenPype/pull/2857) +- General: Fix getattr clalback on dynamic modules [\#2855](https://github.com/pypeclub/OpenPype/pull/2855) +- Nuke: slate resolution to input video resolution [\#2853](https://github.com/pypeclub/OpenPype/pull/2853) - WebPublisher: Fix username stored in DB [\#2852](https://github.com/pypeclub/OpenPype/pull/2852) - WebPublisher: Fix wrong number of frames for video file [\#2851](https://github.com/pypeclub/OpenPype/pull/2851) - Nuke: fix multiple baking profile farm publishing [\#2842](https://github.com/pypeclub/OpenPype/pull/2842) @@ -42,26 +45,25 @@ - General: Host name was formed from obsolete code [\#2821](https://github.com/pypeclub/OpenPype/pull/2821) - Settings UI: Fix "Apply from" action [\#2820](https://github.com/pypeclub/OpenPype/pull/2820) - Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) +- Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818) - StandalonePublisher: use dynamic groups in subset names [\#2816](https://github.com/pypeclub/OpenPype/pull/2816) - Settings UI: Search case sensitivity [\#2810](https://github.com/pypeclub/OpenPype/pull/2810) - Flame Babypublisher optimalization [\#2806](https://github.com/pypeclub/OpenPype/pull/2806) -- resolve: fixing fusion module loading [\#2802](https://github.com/pypeclub/OpenPype/pull/2802) -- Ftrack: Unset task ids from asset versions before tasks are removed [\#2800](https://github.com/pypeclub/OpenPype/pull/2800) -- Slack: fail gracefully if slack exception [\#2798](https://github.com/pypeclub/OpenPype/pull/2798) -- Flame: Fix version string in default settings [\#2783](https://github.com/pypeclub/OpenPype/pull/2783) -**Merged pull requests:** +**🔀 Refactored code** +- General: Move create logic from avalon to OpenPype [\#2854](https://github.com/pypeclub/OpenPype/pull/2854) +- General: Add vendors from avalon [\#2848](https://github.com/pypeclub/OpenPype/pull/2848) - General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839) - Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829) - Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823) -- Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818) -- Ftrack: Moved module one hierarchy level higher [\#2792](https://github.com/pypeclub/OpenPype/pull/2792) -- SyncServer: Moved module one hierarchy level higher [\#2791](https://github.com/pypeclub/OpenPype/pull/2791) -- Royal render: Move module one hierarchy level higher [\#2790](https://github.com/pypeclub/OpenPype/pull/2790) -- Deadline: Move module one hierarchy level higher [\#2789](https://github.com/pypeclub/OpenPype/pull/2789) - General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766) +**Merged pull requests:** + +- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866) +- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847) + ## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.8.2-nightly.3...3.8.2) diff --git a/openpype/version.py b/openpype/version.py index 55ac148ed1..d4af8d760f 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.7" +__version__ = "3.9.0-nightly.8" diff --git a/pyproject.toml b/pyproject.toml index f0d295a44c..fe681266ca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.7" # OpenPype +version = "3.9.0-nightly.8" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From f3f781b43578dc1cef8cbbbf5c9276c5e48e7469 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 11 Mar 2022 11:03:15 +0100 Subject: [PATCH 182/302] added subset name filtering in ExtractReview --- openpype/plugins/publish/extract_review.py | 25 ++++++++++++++++--- .../defaults/project_settings/global.json | 3 ++- .../schemas/schema_global_publish.json | 9 +++++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 0b139a73e4..b8599454ee 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -103,9 +103,10 @@ class ExtractReview(pyblish.api.InstancePlugin): self.log.debug("Matching profile: \"{}\"".format(json.dumps(profile))) + subset_name = instance.data.get("subset") instance_families = self.families_from_instance(instance) - filtered_outputs = self.filter_outputs_by_families( - profile, instance_families + filtered_outputs = self.filter_output_defs( + profile, subset_name, instance_families ) # Store `filename_suffix` to save arguments profile_outputs = [] @@ -1651,7 +1652,7 @@ class ExtractReview(pyblish.api.InstancePlugin): return True return False - def filter_outputs_by_families(self, profile, families): + def filter_output_defs(self, profile, subset_name, families): """Return outputs matching input instance families. Output definitions without families filter are marked as valid. @@ -1684,6 +1685,24 @@ class ExtractReview(pyblish.api.InstancePlugin): if not self.families_filter_validation(families, families_filters): continue + # Subsets name filters + subset_filters = [ + subset_filter + for subset_filter in output_filters.get("subsets", []) + # Skip empty strings + if subset_filter + ] + if subset_name and subset_filters: + match = False + for subset_filter in subset_filters: + compiled = re.compile(subset_filter) + if compiled.search(subset_name): + match = True + break + + if not match: + continue + filtered_outputs[filename_suffix] = output_def return filtered_outputs diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 9c44d9bc86..30a71b044a 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -87,7 +87,8 @@ "render", "review", "ftrack" - ] + ], + "subsets": [] }, "overscan_crop": "", "overscan_color": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json index 3eea7ccb30..12043d4205 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json @@ -291,6 +291,15 @@ "label": "Families", "type": "list", "object_type": "text" + }, + { + "type": "separator" + }, + { + "key": "subsets", + "label": "Subsets", + "type": "list", + "object_type": "text" } ] }, From fe1e2b306840a24caf0702d88182dc69812f237f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 11 Mar 2022 11:50:41 +0100 Subject: [PATCH 183/302] flame: adding `export_type` attribute --- .../publish/extract_subset_resources.py | 3 +++ .../defaults/project_settings/flame.json | 1 + .../projects_schema/schema_project_flame.json | 18 ++++++++++++++++++ 3 files changed, 22 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index db85bede85..7be41bbb76 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -22,6 +22,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "ext": "jpg", "xml_preset_file": "Jpeg (8-bit).xml", "xml_preset_dir": "", + "export_type": "File Sequence", "colorspace_out": "Output - sRGB", "representation_add_range": False, "representation_tags": ["thumbnail"] @@ -30,6 +31,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "ext": "mov", "xml_preset_file": "Apple iPad (1920x1080).xml", "xml_preset_dir": "", + "export_type": "Movie", "colorspace_out": "Output - Rec.709", "representation_add_range": True, "representation_tags": [ @@ -84,6 +86,7 @@ class ExtractSubsetResources(openpype.api.Extractor): kwargs = {} preset_file = preset_config["xml_preset_file"] preset_dir = preset_config["xml_preset_dir"] + export_type = preset_config["export_type"] repre_tags = preset_config["representation_tags"] color_out = preset_config["colorspace_out"] diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index 6fb6f55528..ef9c2b1041 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -27,6 +27,7 @@ "ext": "exr", "xml_preset_file": "OpenEXR (16-bit fp DWAA).xml", "xml_preset_dir": "", + "export_type": "File Sequence", "colorspace_out": "ACES - ACEScg", "representation_add_range": true, "representation_tags": [] diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index dc88d11f61..1f30b45981 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -171,6 +171,24 @@ "label": "XML preset folder (optional)", "type": "text" }, + { + "key": "export_type", + "label": "Eport clip type", + "type": "enum", + "default": "File Sequence", + "enum_items": [ + { + "Movie": "Movie" + }, + { + "File Sequence": "File Sequence" + }, + { + "Sequence Publish": "Sequence Publish" + } + ] + + }, { "key": "colorspace_out", "label": "Output color (imageio)", From 6706e90d1852f063670fcba5a1e630dde0c19a17 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 11 Mar 2022 12:41:14 +0100 Subject: [PATCH 184/302] =?UTF-8?q?Fixes=20for=20=F0=9F=8E=AB=20OP-2825?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openpype/hosts/maya/api/plugin.py | 3 ++- .../hosts/maya/plugins/load/load_reference.py | 8 +------- .../plugins/publish/extract_maya_scene_raw.py | 18 +++++++----------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index e0c21645e4..feaceacebc 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -178,7 +178,7 @@ class ReferenceLoader(Loader): loader=self.__class__.__name__ ) loaded_containers.append(container) - self._organize_containers([ref_node], container) + self._organize_containers(nodes, container) c += 1 namespace = None @@ -318,6 +318,7 @@ class ReferenceLoader(Loader): @staticmethod def _organize_containers(nodes, container): # type: (list, str) -> None + """Put containers in loaded data to correct hierarchy.""" for node in nodes: id_attr = "{}.id".format(node) if not cmds.attributeQuery("id", node=node, exists=True): diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 66cf95a643..d358c62724 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -121,18 +121,12 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): if family == "rig": self._post_process_rig(name, namespace, context, options) else: - if "translate" in options: cmds.setAttr(group_name + ".t", *options["translate"]) + print(new_nodes) return new_nodes - def load(self, context, name=None, namespace=None, options=None): - container = super(ReferenceLoader, self).load( - context, name, namespace, options) - # clean containers if present to AVALON_CONTAINERS - self._organize_containers(self[:], container[0]) - def switch(self, container, representation): self.update(container, representation) diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 5cc7b52090..2e1260c374 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -58,16 +58,12 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): else: members = instance[:] - loaded_containers = None - if set(self.add_for_families).intersection( - set(instance.data.get("families")), - set(instance.data.get("family").lower())): - loaded_containers = self._add_loaded_containers(members) - selection = members - if loaded_containers: - self.log.info(loaded_containers) - selection += loaded_containers + if set(self.add_for_families).intersection( + set(instance.data.get("families"))) or \ + instance.data.get("family") in self.add_for_families: + selection += self._add_loaded_containers(members) + self.log.info(selection) # Perform extraction self.log.info("Performing extraction ...") @@ -105,7 +101,7 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): if cmds.referenceQuery(ref, isNodeReferenced=True) ] - refs_to_include = set(refs_to_include) + members_with_refs = set(refs_to_include).union(members) obj_sets = cmds.ls("*.id", long=True, type="objectSet", recursive=True, objectsOnly=True) @@ -121,7 +117,7 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): continue set_content = set(cmds.sets(obj_set, query=True)) - if set_content.intersection(refs_to_include): + if set_content.intersection(members_with_refs): loaded_containers.append(obj_set) return loaded_containers From f724e0ca222bd27d6a202ab7814fca449569830a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 13:33:17 +0100 Subject: [PATCH 185/302] OP-2813 - fix for rendering single file from AE in DL Solves issue with rendering .mov or .avi file. Added test cae for collect_frames --- openpype/lib/delivery.py | 21 +++++-- .../plugins/publish/submit_publish_job.py | 1 + tests/unit/openpype/lib/test_delivery.py | 57 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 tests/unit/openpype/lib/test_delivery.py diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index 9fc65aae8e..f1855d9550 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -13,18 +13,30 @@ def collect_frames(files): Uses clique as most precise solution Args: - files(list): list of source paths + files(list) or (set with single value): list of source paths Returns: (dict): {'/asset/subset_v001.0001.png': '0001', ....} """ collections, remainder = clique.assemble(files, minimum_items=1) + real_file_name = None + if len(files) == 1: + real_file_name = list(files)[0] + sources_and_frames = {} if collections: for collection in collections: src_head = collection.head src_tail = collection.tail + if src_head.endswith("_v"): + # print("Collection gathered incorrectly, not a sequence " + # "just a version found in {}".format(files)) + if len(collections) > 1: + continue + else: + return {real_file_name: None} + for index in collection.indexes: src_frame = collection.format("{padding}") % index src_file_name = "{}{}{}".format(src_head, src_frame, @@ -71,14 +83,15 @@ def path_from_representation(representation, anatomy): def copy_file(src_path, dst_path): """Hardlink file if possible(to save space), copy if not""" - from openpype.lib import create_hard_link # safer importing + from avalon.vendor import filelink # safer importing if os.path.exists(dst_path): return try: - create_hard_link( + filelink.create( src_path, - dst_path + dst_path, + filelink.HARDLINK ) except OSError: shutil.copyfile(src_path, dst_path) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 1de1c37575..964fe003fd 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -599,6 +599,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "files": os.path.basename(remainder), "stagingDir": os.path.dirname(remainder), } + representations.append(rep) if "render" in instance.get("families"): rep.update({ "fps": instance.get("fps"), diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py new file mode 100644 index 0000000000..affe14a89f --- /dev/null +++ b/tests/unit/openpype/lib/test_delivery.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +"""Test suite for delivery functions.""" +from openpype.lib.delivery import collect_frames + + +def test_collect_frames_multi_sequence(): + files = ["Asset_renderCompositingMain_v001.0000.png", + "Asset_renderCompositingMain_v001.0001.png", + "Asset_renderCompositingMain_v001.0002.png"] + ret = collect_frames(files) + + expected = { + "Asset_renderCompositingMain_v001.0000.png": "0000", + "Asset_renderCompositingMain_v001.0001.png": "0001", + "Asset_renderCompositingMain_v001.0002.png": "0002" + } + + print(ret) + assert ret == expected, "Not matching" + + +def test_collect_frames_single_sequence(): + files = ["Asset_renderCompositingMain_v001.0000.png"] + ret = collect_frames(files) + + expected = { + "Asset_renderCompositingMain_v001.0000.png": "0000" + } + + print(ret) + assert ret == expected, "Not matching" + + +def test_collect_frames_single_sequence_as_dict(): + files = {"Asset_renderCompositingMain_v001.0000.png"} + ret = collect_frames(files) + + expected = { + "Asset_renderCompositingMain_v001.0000.png": "0000" + } + + print(ret) + assert ret == expected, "Not matching" + + +def test_collect_frames_single_file(): + files = {"Asset_renderCompositingMain_v001.png"} + ret = collect_frames(files) + + expected = { + "Asset_renderCompositingMain_v001.png": None + } + + print(ret) + assert ret == expected, "Not matching" + + From 032fce29b3b642440e2ad1c8a4c17dabf0f705e0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 11 Mar 2022 13:39:23 +0100 Subject: [PATCH 186/302] flame: adding xml preset modify method --- openpype/hosts/flame/api/__init__.py | 6 ++++-- openpype/hosts/flame/api/render_utils.py | 27 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/flame/api/__init__.py b/openpype/hosts/flame/api/__init__.py index 56bbadd2fc..f210c27f87 100644 --- a/openpype/hosts/flame/api/__init__.py +++ b/openpype/hosts/flame/api/__init__.py @@ -68,7 +68,8 @@ from .workio import ( ) from .render_utils import ( export_clip, - get_preset_path_by_xml_name + get_preset_path_by_xml_name, + modify_preset_file ) __all__ = [ @@ -140,5 +141,6 @@ __all__ = [ # render utils "export_clip", - "get_preset_path_by_xml_name" + "get_preset_path_by_xml_name", + "modify_preset_file" ] diff --git a/openpype/hosts/flame/api/render_utils.py b/openpype/hosts/flame/api/render_utils.py index 1b086646cc..473fb2f985 100644 --- a/openpype/hosts/flame/api/render_utils.py +++ b/openpype/hosts/flame/api/render_utils.py @@ -1,4 +1,5 @@ import os +from xml.etree import ElementTree as ET def export_clip(export_path, clip, preset_path, **kwargs): @@ -123,3 +124,29 @@ def get_preset_path_by_xml_name(xml_preset_name): # if nothing found then return False return False + + +def modify_preset_file(xml_path, staging_dir, data): + """Modify xml preset with input data + + Args: + xml_path (str ): path for input xml preset + staging_dir (str): staging dir path + data (dict): data where key is xmlTag and value as string + + Returns: + str: _description_ + """ + # create temp path + dirname, basename = os.path.split(xml_path) + temp_path = os.path.join(staging_dir, basename) + + # change xml following data keys + with open(xml_path, "r") as datafile: + tree = ET.parse(datafile) + for key, value in data.items(): + for element in tree.findall(".//{}".format(key)): + element.text = str(value) + tree.write(temp_path) + + return temp_path From 7ca997de92fd465d9c46b3473f3198a82dd84e2a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 14:14:47 +0100 Subject: [PATCH 187/302] OP-2813 - fix for rendering single file from AE in DL for sequence Solves issue with rendering single frame sequence, eg with 00000 in its file. --- .../publish/submit_aftereffects_deadline.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py index 2918b54d4a..c499c14d40 100644 --- a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py @@ -6,6 +6,7 @@ import pyblish.api from avalon import api from openpype.lib import env_value_to_bool +from openpype.lib.delivery import collect_frames from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo @@ -102,24 +103,18 @@ class AfterEffectsSubmitDeadline( def get_plugin_info(self): deadline_plugin_info = DeadlinePluginInfo() - context = self._instance.context - script_path = context.data["currentFile"] render_path = self._instance.data["expectedFiles"][0] - if len(self._instance.data["expectedFiles"]) > 1: + file_name, frame = list(collect_frames([render_path]).items())[0] + if frame: # replace frame ('000001') with Deadline's required '[#######]' # expects filename in format project_asset_subset_version.FRAME.ext render_dir = os.path.dirname(render_path) file_name = os.path.basename(render_path) - arr = file_name.split('.') - assert len(arr) == 3, \ - "Unable to parse frames from {}".format(file_name) - hashed = '[{}]'.format(len(arr[1]) * "#") - - render_path = os.path.join(render_dir, - '{}.{}.{}'.format(arr[0], hashed, - arr[2])) + hashed = '[{}]'.format(len(frame) * "#") + file_name = file_name.replace(frame, hashed) + render_path = os.path.join(render_dir, file_name) deadline_plugin_info.Comp = self._instance.data["comp_name"] deadline_plugin_info.Version = self._instance.data["app_version"] From e8b2299c00c5989430855faef2dd28005f5203ba Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 09:55:14 +0100 Subject: [PATCH 188/302] OP-2815 - copied webserver tool for AE from avalon to openpype --- .../hosts/aftereffects/api/launch_logic.py | 2 +- openpype/hosts/aftereffects/api/ws_stub.py | 2 +- openpype/tools/adobe_webserver/app.py | 237 ++++++++++++++++++ openpype/tools/adobe_webserver/readme.txt | 12 + 4 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 openpype/tools/adobe_webserver/app.py create mode 100644 openpype/tools/adobe_webserver/readme.txt diff --git a/openpype/hosts/aftereffects/api/launch_logic.py b/openpype/hosts/aftereffects/api/launch_logic.py index 97f14c9332..c549268978 100644 --- a/openpype/hosts/aftereffects/api/launch_logic.py +++ b/openpype/hosts/aftereffects/api/launch_logic.py @@ -15,7 +15,7 @@ from Qt import QtCore from openpype.tools.utils import host_tools from avalon import api -from avalon.tools.webserver.app import WebServerTool +from openpype.tools.adobe_webserver.app import WebServerTool from .ws_stub import AfterEffectsServerStub diff --git a/openpype/hosts/aftereffects/api/ws_stub.py b/openpype/hosts/aftereffects/api/ws_stub.py index 5a0600e92e..b0893310c1 100644 --- a/openpype/hosts/aftereffects/api/ws_stub.py +++ b/openpype/hosts/aftereffects/api/ws_stub.py @@ -8,7 +8,7 @@ import logging import attr from wsrpc_aiohttp import WebSocketAsync -from avalon.tools.webserver.app import WebServerTool +from openpype.tools.adobe_webserver.app import WebServerTool @attr.s diff --git a/openpype/tools/adobe_webserver/app.py b/openpype/tools/adobe_webserver/app.py new file mode 100644 index 0000000000..b79d6c6c60 --- /dev/null +++ b/openpype/tools/adobe_webserver/app.py @@ -0,0 +1,237 @@ +"""This Webserver tool is python 3 specific. + +Don't import directly to avalon.tools or implementation of Python 2 hosts +would break. +""" +import os +import logging +import urllib +import threading +import asyncio +import socket + +from aiohttp import web + +from wsrpc_aiohttp import ( + WSRPCClient +) + +from avalon import api + +log = logging.getLogger(__name__) + + +class WebServerTool: + """ + Basic POC implementation of asychronic websocket RPC server. + Uses class in external_app_1.py to mimic implementation for single + external application. + 'test_client' folder contains two test implementations of client + """ + _instance = None + + def __init__(self): + WebServerTool._instance = self + + self.client = None + self.handlers = {} + self.on_stop_callbacks = [] + + port = None + host_name = "localhost" + websocket_url = os.getenv("WEBSOCKET_URL") + if websocket_url: + parsed = urllib.parse.urlparse(websocket_url) + port = parsed.port + host_name = parsed.netloc.split(":")[0] + if not port: + port = 8098 # fallback + + self.port = port + self.host_name = host_name + + self.app = web.Application() + + # add route with multiple methods for single "external app" + self.webserver_thread = WebServerThread(self, self.port) + + def add_route(self, *args, **kwargs): + self.app.router.add_route(*args, **kwargs) + + def add_static(self, *args, **kwargs): + self.app.router.add_static(*args, **kwargs) + + def start_server(self): + if self.webserver_thread and not self.webserver_thread.is_alive(): + self.webserver_thread.start() + + def stop_server(self): + self.stop() + + async def send_context_change(self, host): + """ + Calls running webserver to inform about context change + + Used when new PS/AE should be triggered, + but one already running, without + this publish would point to old context. + """ + client = WSRPCClient(os.getenv("WEBSOCKET_URL"), + loop=asyncio.get_event_loop()) + await client.connect() + + project = api.Session["AVALON_PROJECT"] + asset = api.Session["AVALON_ASSET"] + task = api.Session["AVALON_TASK"] + log.info("Sending context change to {}-{}-{}".format(project, + asset, + task)) + + await client.call('{}.set_context'.format(host), + project=project, asset=asset, task=task) + await client.close() + + def port_occupied(self, host_name, port): + """ + Check if 'url' is already occupied. + + This could mean, that app is already running and we are trying open it + again. In that case, use existing running webserver. + Check here is easier than capturing exception from thread. + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = True + try: + sock.bind((host_name, port)) + result = False + except: + print("Port is in use") + + return result + + def call(self, func): + log.debug("websocket.call {}".format(func)) + future = asyncio.run_coroutine_threadsafe( + func, + self.webserver_thread.loop + ) + result = future.result() + return result + + @staticmethod + def get_instance(): + if WebServerTool._instance is None: + WebServerTool() + return WebServerTool._instance + + @property + def is_running(self): + if not self.webserver_thread: + return False + return self.webserver_thread.is_running + + def stop(self): + if not self.is_running: + return + try: + log.debug("Stopping websocket server") + self.webserver_thread.is_running = False + self.webserver_thread.stop() + except Exception: + log.warning( + "Error has happened during Killing websocket server", + exc_info=True + ) + + def thread_stopped(self): + for callback in self.on_stop_callbacks: + callback() + + +class WebServerThread(threading.Thread): + """ Listener for websocket rpc requests. + + It would be probably better to "attach" this to main thread (as for + example Harmony needs to run something on main thread), but currently + it creates separate thread and separate asyncio event loop + """ + def __init__(self, module, port): + super(WebServerThread, self).__init__() + + self.is_running = False + self.port = port + self.module = module + self.loop = None + self.runner = None + self.site = None + self.tasks = [] + + def run(self): + self.is_running = True + + try: + log.info("Starting web server") + self.loop = asyncio.new_event_loop() # create new loop for thread + asyncio.set_event_loop(self.loop) + + self.loop.run_until_complete(self.start_server()) + + websocket_url = "ws://localhost:{}/ws".format(self.port) + + log.debug( + "Running Websocket server on URL: \"{}\"".format(websocket_url) + ) + + asyncio.ensure_future(self.check_shutdown(), loop=self.loop) + self.loop.run_forever() + except Exception: + self.is_running = False + log.warning( + "Websocket Server service has failed", exc_info=True + ) + raise + finally: + self.loop.close() # optional + + self.is_running = False + self.module.thread_stopped() + log.info("Websocket server stopped") + + async def start_server(self): + """ Starts runner and TCPsite """ + self.runner = web.AppRunner(self.module.app) + await self.runner.setup() + self.site = web.TCPSite(self.runner, 'localhost', self.port) + await self.site.start() + + def stop(self): + """Sets is_running flag to false, 'check_shutdown' shuts server down""" + self.is_running = False + + async def check_shutdown(self): + """ Future that is running and checks if server should be running + periodically. + """ + while self.is_running: + while self.tasks: + task = self.tasks.pop(0) + log.debug("waiting for task {}".format(task)) + await task + log.debug("returned value {}".format(task.result)) + + await asyncio.sleep(0.5) + + log.debug("Starting shutdown") + await self.site.stop() + log.debug("Site stopped") + await self.runner.cleanup() + log.debug("Runner stopped") + tasks = [task for task in asyncio.all_tasks() if + task is not asyncio.current_task()] + list(map(lambda task: task.cancel(), tasks)) # cancel all the tasks + results = await asyncio.gather(*tasks, return_exceptions=True) + log.debug(f'Finished awaiting cancelled tasks, results: {results}...') + await self.loop.shutdown_asyncgens() + # to really make sure everything else has time to stop + await asyncio.sleep(0.07) + self.loop.stop() diff --git a/openpype/tools/adobe_webserver/readme.txt b/openpype/tools/adobe_webserver/readme.txt new file mode 100644 index 0000000000..06cf140fc4 --- /dev/null +++ b/openpype/tools/adobe_webserver/readme.txt @@ -0,0 +1,12 @@ +Adobe webserver +--------------- +Aiohttp (Asyncio) based websocket server used for communication with host +applications, currently only for Adobe (but could be used for any non python +DCC which has websocket client). + +This webserver is started in spawned Python process that opens DCC during +its launch, waits for connection from DCC and handles communication going +forward. Server is closed before Python process is killed. + +(Different from `openpype/modules/webserver` as that one is running in Tray, +this one is running in spawn Python process.) \ No newline at end of file From 8a3be301414af9542ecb50b4424d8895d479ae4f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 14:31:00 +0100 Subject: [PATCH 189/302] OP-2815 - moved webserver tool to Openpype repo for PS --- openpype/hosts/photoshop/api/launch_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/photoshop/api/launch_logic.py b/openpype/hosts/photoshop/api/launch_logic.py index 112cd8fe3f..0021905cb5 100644 --- a/openpype/hosts/photoshop/api/launch_logic.py +++ b/openpype/hosts/photoshop/api/launch_logic.py @@ -14,7 +14,7 @@ from openpype.api import Logger from openpype.tools.utils import host_tools from avalon import api -from avalon.tools.webserver.app import WebServerTool +from openpype.tools.adobe_webserver.app import WebServerTool from .ws_stub import PhotoshopServerStub From 9de8504c4d89e800e4bff2b69376fe6f9f1f3eb2 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 14:37:48 +0100 Subject: [PATCH 190/302] OP-2815 - Hound --- tests/unit/openpype/lib/test_delivery.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py index affe14a89f..7c2c92c101 100644 --- a/tests/unit/openpype/lib/test_delivery.py +++ b/tests/unit/openpype/lib/test_delivery.py @@ -54,4 +54,3 @@ def test_collect_frames_single_file(): print(ret) assert ret == expected, "Not matching" - From 7c34eb7331f4e67dc4ac24f257c77602d2bbd0e5 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 11 Mar 2022 14:43:26 +0100 Subject: [PATCH 191/302] OP-2815 - added missed import for PS --- openpype/hosts/photoshop/api/ws_stub.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/photoshop/api/ws_stub.py b/openpype/hosts/photoshop/api/ws_stub.py index d4406d17b9..64d89f5420 100644 --- a/openpype/hosts/photoshop/api/ws_stub.py +++ b/openpype/hosts/photoshop/api/ws_stub.py @@ -2,12 +2,11 @@ Stub handling connection from server to client. Used anywhere solution is calling client methods. """ -import sys import json import attr from wsrpc_aiohttp import WebSocketAsync -from avalon.tools.webserver.app import WebServerTool +from openpype.tools.adobe_webserver.app import WebServerTool @attr.s From c033eaad65afcb43752f9c5da85b94bab31bffb6 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 11 Mar 2022 15:13:43 +0100 Subject: [PATCH 192/302] fix update --- openpype/hosts/maya/api/plugin.py | 5 +++++ openpype/hosts/maya/plugins/load/load_reference.py | 2 -- .../hosts/maya/plugins/publish/extract_maya_scene_raw.py | 7 +++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index feaceacebc..1f90b3ffbd 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -247,6 +247,11 @@ class ReferenceLoader(Loader): self.log.warning("Ignoring file read error:\n%s", exc) + shapes = cmds.ls(content, shapes=True, long=True) + new_nodes = (list(set(content) - set(shapes))) + + self._organize_containers(new_nodes, container["objectName"]) + # Reapply alembic settings. if representation["name"] == "abc" and alembic_data: alembic_nodes = cmds.ls( diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index d358c62724..04a25f6493 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -123,8 +123,6 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): else: if "translate" in options: cmds.setAttr(group_name + ".t", *options["translate"]) - - print(new_nodes) return new_nodes def switch(self, container, representation): diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 2e1260c374..9d73bea7d2 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -60,10 +60,9 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): selection = members if set(self.add_for_families).intersection( - set(instance.data.get("families"))) or \ + set(instance.data.get("families", []))) or \ instance.data.get("family") in self.add_for_families: - selection += self._add_loaded_containers(members) - self.log.info(selection) + selection += self._get_loaded_containers(members) # Perform extraction self.log.info("Performing extraction ...") @@ -93,7 +92,7 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): self.log.info("Extracted instance '%s' to: %s" % (instance.name, path)) @staticmethod - def _add_loaded_containers(members): + def _get_loaded_containers(members): # type: (list) -> list refs_to_include = [ cmds.referenceQuery(ref, referenceNode=True) From 9052adfbd35855c03a1ccb3da99ff0b019d5c73c Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Fri, 11 Mar 2022 15:56:33 +0100 Subject: [PATCH 193/302] flame: exporting also sequence_clip with `Sequence Publish` preset with mark in/out it should only publish particular segment --- .../publish/extract_subset_resources.py | 113 ++++++++++++++---- 1 file changed, 92 insertions(+), 21 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 7be41bbb76..bfd723f5d8 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -56,21 +56,35 @@ class ExtractSubsetResources(openpype.api.Extractor): ): instance.data["representations"] = [] - frame_start = instance.data["frameStart"] - handle_start = instance.data["handleStart"] - frame_start_handle = frame_start - handle_start - source_first_frame = instance.data["sourceFirstFrame"] - source_start_handles = instance.data["sourceStartH"] - source_end_handles = instance.data["sourceEndH"] - source_duration_handles = ( - source_end_handles - source_start_handles) + 1 - + # flame objects + segment = instance.data["item"] + sequence_clip = instance.context.data["flameSequence"] clip_data = instance.data["flameSourceClip"] clip = clip_data["PyClip"] - in_mark = (source_start_handles - source_first_frame) + 1 - out_mark = in_mark + source_duration_handles + # segment's parent track name + s_track_name = segment.parent.name.get_value() + # get configured workfile frame start/end (handles excluded) + frame_start = instance.data["frameStart"] + # get media source first frame + source_first_frame = instance.data["sourceFirstFrame"] + + # get timeline in/out of segment + clip_in = instance.data["clipIn"] + clip_out = instance.data["clipOut"] + + # get handles value - take only the max from both + handle_start = instance.data["handleStart"] + handle_end = instance.data["handleStart"] + handles = max(handle_start, handle_end) + + # get media source range with handles + source_end_handles = instance.data["sourceEndH"] + source_start_handles = instance.data["sourceStartH"] + source_end_handles = instance.data["sourceEndH"] + + # create staging dir path staging_dir = self.staging_dir(instance) # add default preset type for thumbnail and reviewable video @@ -79,16 +93,52 @@ class ExtractSubsetResources(openpype.api.Extractor): export_presets = deepcopy(self.default_presets) export_presets.update(self.export_presets_mapping) - # with maintained duplication loop all presets - with opfapi.maintained_object_duplication(clip) as duplclip: - # loop all preset names and - for unique_name, preset_config in export_presets.items(): + # loop all preset names and + for unique_name, preset_config in export_presets.items(): + modify_xml_data = {} + + # get all presets attributes + preset_file = preset_config["xml_preset_file"] + preset_dir = preset_config["xml_preset_dir"] + export_type = preset_config["export_type"] + repre_tags = preset_config["representation_tags"] + color_out = preset_config["colorspace_out"] + + # get frame range with handles for representation range + frame_start_handle = frame_start - handle_start + source_duration_handles = ( + source_end_handles - source_start_handles) + 1 + + # define in/out marks + in_mark = (source_start_handles - source_first_frame) + 1 + out_mark = in_mark + source_duration_handles + + # by default export source clips + exporting_clip = clip + + if export_type == "Sequence Publish": + # change export clip to sequence + exporting_clip = sequence_clip + + # change in/out marks to timeline in/out + in_mark = clip_in + out_mark = clip_out + + # add xml tags modifications + modify_xml_data.update({ + "exportHandles": True, + "nbHandles": handles, + "startFrame": frame_start + }) + + # with maintained duplication loop all presets + with opfapi.maintained_object_duplication( + exporting_clip) as duplclip: kwargs = {} - preset_file = preset_config["xml_preset_file"] - preset_dir = preset_config["xml_preset_dir"] - export_type = preset_config["export_type"] - repre_tags = preset_config["representation_tags"] - color_out = preset_config["colorspace_out"] + + if export_type == "Sequence Publish": + # only keep visible layer where instance segment is child + self.hide_other_tracks(duplclip, s_track_name) # validate xml preset file is filled if preset_file == "": @@ -111,10 +161,13 @@ class ExtractSubsetResources(openpype.api.Extractor): ) # create preset path - preset_path = str(os.path.join( + preset_orig_xml_path = str(os.path.join( preset_dir, preset_file )) + preset_path = opfapi.modify_preset_file( + preset_orig_xml_path, staging_dir, modify_xml_data) + # define kwargs based on preset type if "thumbnail" in unique_name: kwargs["thumb_frame_number"] = in_mark + ( @@ -125,6 +178,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "out_mark": out_mark }) + # get and make export dir paths export_dir_path = str(os.path.join( staging_dir, unique_name )) @@ -135,6 +189,7 @@ class ExtractSubsetResources(openpype.api.Extractor): export_dir_path, duplclip, preset_path, **kwargs) extension = preset_config["ext"] + # create representation data representation_data = { "name": unique_name, @@ -249,3 +304,19 @@ class ExtractSubsetResources(openpype.api.Extractor): ) return new_stage_dir, new_files_list + + def hide_other_tracks(self, sequence_clip, track_name): + """Helper method used only if sequence clip is used + + Args: + sequence_clip (flame.Clip): sequence clip + track_name (str): track name + """ + # create otio tracks and clips + for ver in sequence_clip.versions: + for track in ver.tracks: + if len(track.segments) == 0 and track.hidden: + continue + + if track.name.get_value() != track_name: + track.hidden = True From 78d566b654074935141968e91429dc4be89510d6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 11 Mar 2022 16:40:41 +0100 Subject: [PATCH 194/302] replaced usage of avalon.lib.time with new function get_formatted_current_time --- .../hosts/harmony/plugins/publish/collect_farm_render.py | 3 ++- openpype/hosts/maya/plugins/publish/collect_render.py | 3 ++- openpype/hosts/maya/plugins/publish/collect_vrayscene.py | 3 ++- openpype/lib/__init__.py | 6 +++++- openpype/lib/abstract_collect_render.py | 2 +- openpype/lib/config.py | 6 ++++++ openpype/plugins/publish/collect_time.py | 6 +++--- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py index 85237094e4..35b123f97d 100644 --- a/openpype/hosts/harmony/plugins/publish/collect_farm_render.py +++ b/openpype/hosts/harmony/plugins/publish/collect_farm_render.py @@ -5,6 +5,7 @@ from pathlib import Path import attr from avalon import api +from openpype.lib import get_formatted_current_time import openpype.lib.abstract_collect_render import openpype.hosts.harmony.api as harmony from openpype.lib.abstract_collect_render import RenderInstance @@ -138,7 +139,7 @@ class CollectFarmRender(openpype.lib.abstract_collect_render. render_instance = HarmonyRenderInstance( version=version, - time=api.time(), + time=get_formatted_current_time(), source=context.data["currentFile"], label=node.split("/")[1], subset=subset_name, diff --git a/openpype/hosts/maya/plugins/publish/collect_render.py b/openpype/hosts/maya/plugins/publish/collect_render.py index d99e81573b..a525b562f3 100644 --- a/openpype/hosts/maya/plugins/publish/collect_render.py +++ b/openpype/hosts/maya/plugins/publish/collect_render.py @@ -50,6 +50,7 @@ import maya.app.renderSetup.model.renderSetup as renderSetup import pyblish.api from avalon import api +from openpype.lib import get_formatted_current_time from openpype.hosts.maya.api.lib_renderproducts import get as get_layer_render_products # noqa: E501 from openpype.hosts.maya.api import lib @@ -328,7 +329,7 @@ class CollectMayaRender(pyblish.api.ContextPlugin): "family": "renderlayer", "families": ["renderlayer"], "asset": asset, - "time": api.time(), + "time": get_formatted_current_time(), "author": context.data["user"], # Add source to allow tracing back to the scene from # which was submitted originally diff --git a/openpype/hosts/maya/plugins/publish/collect_vrayscene.py b/openpype/hosts/maya/plugins/publish/collect_vrayscene.py index c1e5d388af..327fc836dc 100644 --- a/openpype/hosts/maya/plugins/publish/collect_vrayscene.py +++ b/openpype/hosts/maya/plugins/publish/collect_vrayscene.py @@ -7,6 +7,7 @@ from maya import cmds import pyblish.api from avalon import api +from openpype.lib import get_formatted_current_time from openpype.hosts.maya.api import lib @@ -117,7 +118,7 @@ class CollectVrayScene(pyblish.api.InstancePlugin): "family": "vrayscene_layer", "families": ["vrayscene_layer"], "asset": api.Session["AVALON_ASSET"], - "time": api.time(), + "time": get_formatted_current_time(), "author": context.data["user"], # Add source to allow tracing back to the scene from # which was submitted originally diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 34b217f690..761ad3e9a0 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -63,7 +63,10 @@ from .anatomy import ( Anatomy ) -from .config import get_datetime_data +from .config import ( + get_datetime_data, + get_formatted_current_time +) from .python_module_tools import ( import_filepath, @@ -309,6 +312,7 @@ __all__ = [ "Anatomy", "get_datetime_data", + "get_formatted_current_time", "PypeLogger", "get_default_components", diff --git a/openpype/lib/abstract_collect_render.py b/openpype/lib/abstract_collect_render.py index 3839aad45d..7c768e280c 100644 --- a/openpype/lib/abstract_collect_render.py +++ b/openpype/lib/abstract_collect_render.py @@ -26,7 +26,7 @@ class RenderInstance(object): # metadata version = attr.ib() # instance version - time = attr.ib() # time of instance creation (avalon.api.time()) + time = attr.ib() # time of instance creation (get_formatted_current_time) source = attr.ib() # path to source scene file label = attr.ib() # label to show in GUI subset = attr.ib() # subset name diff --git a/openpype/lib/config.py b/openpype/lib/config.py index ba394cfd56..57e8efa57d 100644 --- a/openpype/lib/config.py +++ b/openpype/lib/config.py @@ -74,3 +74,9 @@ def get_datetime_data(datetime_obj=None): "S": str(int(seconds)), "SS": str(seconds), } + + +def get_formatted_current_time(): + return datetime.datetime.now().strftime( + "%Y%m%dT%H%M%SZ" + ) diff --git a/openpype/plugins/publish/collect_time.py b/openpype/plugins/publish/collect_time.py index e0adc7dfc3..7a005cc9cb 100644 --- a/openpype/plugins/publish/collect_time.py +++ b/openpype/plugins/publish/collect_time.py @@ -1,12 +1,12 @@ import pyblish.api -from avalon import api +from openpype.lib import get_formatted_current_time class CollectTime(pyblish.api.ContextPlugin): """Store global time at the time of publish""" label = "Collect Current Time" - order = pyblish.api.CollectorOrder + order = pyblish.api.CollectorOrder - 0.499 def process(self, context): - context.data["time"] = api.time() + context.data["time"] = get_formatted_current_time() From cf1453029f14dde78a18d8560ee55e69871bfe46 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 11 Mar 2022 17:13:26 +0100 Subject: [PATCH 195/302] remove obsolete code and set cast --- openpype/hosts/maya/api/plugin.py | 6 +----- .../maya/plugins/publish/extract_maya_scene_raw.py | 12 ++++++------ 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 1f90b3ffbd..48d7c465ec 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -247,10 +247,7 @@ class ReferenceLoader(Loader): self.log.warning("Ignoring file read error:\n%s", exc) - shapes = cmds.ls(content, shapes=True, long=True) - new_nodes = (list(set(content) - set(shapes))) - - self._organize_containers(new_nodes, container["objectName"]) + self._organize_containers(content, container["objectName"]) # Reapply alembic settings. if representation["name"] == "abc" and alembic_data: @@ -289,7 +286,6 @@ class ReferenceLoader(Loader): to remove from scene. """ - from maya import cmds node = container["objectName"] diff --git a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py index 9d73bea7d2..389995d30c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py +++ b/openpype/hosts/maya/plugins/publish/extract_maya_scene_raw.py @@ -94,13 +94,13 @@ class ExtractMayaSceneRaw(openpype.api.Extractor): @staticmethod def _get_loaded_containers(members): # type: (list) -> list - refs_to_include = [ - cmds.referenceQuery(ref, referenceNode=True) - for ref in members - if cmds.referenceQuery(ref, isNodeReferenced=True) - ] + refs_to_include = { + cmds.referenceQuery(node, referenceNode=True) + for node in members + if cmds.referenceQuery(node, isNodeReferenced=True) + } - members_with_refs = set(refs_to_include).union(members) + members_with_refs = refs_to_include.union(members) obj_sets = cmds.ls("*.id", long=True, type="objectSet", recursive=True, objectsOnly=True) From 9b0fba249068179a4608e31b9b5d406f241d63c6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 11 Mar 2022 17:55:00 +0100 Subject: [PATCH 196/302] moved ffprobe_streams into transcoding, seprated and renamed to get_ffprobe_streams --- .../publish/extract_trim_video_audio.py | 7 +- .../publish/collect_published_files.py | 8 ++- openpype/lib/__init__.py | 8 ++- openpype/lib/transcoding.py | 65 ++++++++++++++++++- openpype/lib/vendor_bin_utils.py | 52 --------------- openpype/plugins/publish/extract_review.py | 4 +- .../plugins/publish/extract_review_slate.py | 20 +++--- .../tests/test_lib_restructuralization.py | 2 +- 8 files changed, 95 insertions(+), 71 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/extract_trim_video_audio.py b/openpype/hosts/standalonepublisher/plugins/publish/extract_trim_video_audio.py index c18de5bc1c..f327895b83 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/extract_trim_video_audio.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/extract_trim_video_audio.py @@ -2,6 +2,9 @@ import os import pyblish.api import openpype.api +from openpype.lib import ( + get_ffmpeg_tool_path, +) from pprint import pformat @@ -27,7 +30,7 @@ class ExtractTrimVideoAudio(openpype.api.Extractor): instance.data["representations"] = list() # get ffmpet path - ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg") + ffmpeg_path = get_ffmpeg_tool_path("ffmpeg") # get staging dir staging_dir = self.staging_dir(instance) @@ -44,7 +47,7 @@ class ExtractTrimVideoAudio(openpype.api.Extractor): clip_trimed_path = os.path.join( staging_dir, instance.data["name"] + ext) # # check video file metadata - # input_data = plib.ffprobe_streams(video_file_path)[0] + # input_data = plib.get_ffprobe_streams(video_file_path)[0] # self.log.debug(f"__ input_data: `{input_data}`") start = float(instance.data["clipInH"]) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index afd6f349db..be33e6bb4e 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -14,7 +14,11 @@ import math from avalon import io import pyblish.api -from openpype.lib import prepare_template_data, get_asset, ffprobe_streams +from openpype.lib import ( + prepare_template_data, + get_asset, + get_ffprobe_streams +) from openpype.lib.vendor_bin_utils import get_fps from openpype.lib.plugin_tools import ( parse_json, @@ -265,7 +269,7 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): def _get_number_of_frames(self, file_url): """Return duration in frames""" try: - streams = ffprobe_streams(file_url, self.log) + streams = get_ffprobe_streams(file_url, self.log) except Exception as exc: raise AssertionError(( "FFprobe couldn't read information about input file: \"{}\"." diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 761ad3e9a0..bf4a3781bc 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -21,7 +21,6 @@ from .vendor_bin_utils import ( get_vendor_bin_path, get_oiio_tools_path, get_ffmpeg_tool_path, - ffprobe_streams, is_oiio_supported ) from .env_tools import ( @@ -84,7 +83,9 @@ from .profiles_filtering import ( from .transcoding import ( get_transcode_temp_directory, should_convert_for_ffmpeg, - convert_for_ffmpeg + convert_for_ffmpeg, + get_ffprobe_data, + get_ffprobe_streams, ) from .avalon_context import ( CURRENT_DOC_SCHEMAS, @@ -216,7 +217,6 @@ __all__ = [ "get_vendor_bin_path", "get_oiio_tools_path", "get_ffmpeg_tool_path", - "ffprobe_streams", "is_oiio_supported", "import_filepath", @@ -228,6 +228,8 @@ __all__ = [ "get_transcode_temp_directory", "should_convert_for_ffmpeg", "convert_for_ffmpeg", + "get_ffprobe_data", + "get_ffprobe_streams", "CURRENT_DOC_SCHEMAS", "PROJECT_NAME_ALLOWED_SYMBOLS", diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 462745bcda..8137c4ae11 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -1,15 +1,18 @@ import os import re import logging +import json import collections import tempfile +import subprocess import xml.etree.ElementTree from .execute import run_subprocess from .vendor_bin_utils import ( + get_ffmpeg_tool_path, get_oiio_tools_path, - is_oiio_supported + is_oiio_supported, ) # Max length of string that is supported by ffmpeg @@ -483,3 +486,63 @@ def convert_for_ffmpeg( logger.debug("Conversion command: {}".format(" ".join(oiio_cmd))) run_subprocess(oiio_cmd, logger=logger) + + +# FFMPEG functions +def get_ffprobe_data(path_to_file, logger=None): + """Load data about entered filepath via ffprobe. + + Args: + path_to_file (str): absolute path + logger (logging.Logger): injected logger, if empty new is created + """ + if not logger: + logger = logging.getLogger(__name__) + logger.info( + "Getting information about input \"{}\".".format(path_to_file) + ) + args = [ + get_ffmpeg_tool_path("ffprobe"), + "-hide_banner", + "-loglevel", "fatal", + "-show_error", + "-show_format", + "-show_streams", + "-show_programs", + "-show_chapters", + "-show_private_data", + "-print_format", "json", + path_to_file + ] + + logger.debug("FFprobe command: {}".format( + subprocess.list2cmdline(args) + )) + popen = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + + popen_stdout, popen_stderr = popen.communicate() + if popen_stdout: + logger.debug("FFprobe stdout:\n{}".format( + popen_stdout.decode("utf-8") + )) + + if popen_stderr: + logger.warning("FFprobe stderr:\n{}".format( + popen_stderr.decode("utf-8") + )) + + return json.loads(popen_stdout) + + +def get_ffprobe_streams(path_to_file, logger=None): + """Load streams from entered filepath via ffprobe. + + Args: + path_to_file (str): absolute path + logger (logging.Logger): injected logger, if empty new is created + """ + return get_ffprobe_data(path_to_file, logger)["streams"] diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 4b11f1c046..36510f4238 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -1,8 +1,6 @@ import os import logging -import json import platform -import subprocess log = logging.getLogger("Vendor utils") @@ -138,56 +136,6 @@ def get_ffmpeg_tool_path(tool="ffmpeg"): return find_executable(os.path.join(ffmpeg_dir, tool)) -def ffprobe_streams(path_to_file, logger=None): - """Load streams from entered filepath via ffprobe. - - Args: - path_to_file (str): absolute path - logger (logging.getLogger): injected logger, if empty new is created - - """ - if not logger: - logger = log - logger.info( - "Getting information about input \"{}\".".format(path_to_file) - ) - args = [ - get_ffmpeg_tool_path("ffprobe"), - "-hide_banner", - "-loglevel", "fatal", - "-show_error", - "-show_format", - "-show_streams", - "-show_programs", - "-show_chapters", - "-show_private_data", - "-print_format", "json", - path_to_file - ] - - logger.debug("FFprobe command: {}".format( - subprocess.list2cmdline(args) - )) - popen = subprocess.Popen( - args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - - popen_stdout, popen_stderr = popen.communicate() - if popen_stdout: - logger.debug("FFprobe stdout:\n{}".format( - popen_stdout.decode("utf-8") - )) - - if popen_stderr: - logger.warning("FFprobe stderr:\n{}".format( - popen_stderr.decode("utf-8") - )) - - return json.loads(popen_stdout)["streams"] - - def is_oiio_supported(): """Checks if oiiotool is configured for this platform. diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index b8599454ee..f046194c0d 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -13,7 +13,7 @@ import pyblish.api import openpype.api from openpype.lib import ( get_ffmpeg_tool_path, - ffprobe_streams, + get_ffprobe_streams, path_to_subprocess_arg, @@ -1146,7 +1146,7 @@ class ExtractReview(pyblish.api.InstancePlugin): # NOTE Skipped using instance's resolution full_input_path_single_file = temp_data["full_input_path_single_file"] try: - streams = ffprobe_streams( + streams = get_ffprobe_streams( full_input_path_single_file, self.log ) except Exception as exc: diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index 5442cf2211..e27a0b28bd 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -1,7 +1,11 @@ import os import openpype.api -import openpype.lib import pyblish +from openpype.lib import ( + path_to_subprocess_arg, + get_ffmpeg_tool_path, + get_ffprobe_streams, +) class ExtractReviewSlate(openpype.api.Extractor): @@ -24,9 +28,9 @@ class ExtractReviewSlate(openpype.api.Extractor): suffix = "_slate" slate_path = inst_data.get("slateFrame") - ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg") + ffmpeg_path = get_ffmpeg_tool_path("ffmpeg") - slate_streams = openpype.lib.ffprobe_streams(slate_path, self.log) + slate_streams = get_ffprobe_streams(slate_path, self.log) # Try to find first stream with defined 'width' and 'height' # - this is to avoid order of streams where audio can be as first # - there may be a better way (checking `codec_type`?)+ @@ -66,7 +70,7 @@ class ExtractReviewSlate(openpype.api.Extractor): os.path.normpath(stagingdir), repre["files"]) self.log.debug("__ input_path: {}".format(input_path)) - video_streams = openpype.lib.ffprobe_streams( + video_streams = get_ffprobe_streams( input_path, self.log ) @@ -143,7 +147,7 @@ class ExtractReviewSlate(openpype.api.Extractor): else: input_args.extend(repre["outputDef"].get('input', [])) input_args.append("-loop 1 -i {}".format( - openpype.lib.path_to_subprocess_arg(slate_path) + path_to_subprocess_arg(slate_path) )) input_args.extend([ "-r {}".format(fps), @@ -216,12 +220,12 @@ class ExtractReviewSlate(openpype.api.Extractor): slate_v_path = slate_path.replace(".png", ext) output_args.append( - openpype.lib.path_to_subprocess_arg(slate_v_path) + path_to_subprocess_arg(slate_v_path) ) _remove_at_end.append(slate_v_path) slate_args = [ - openpype.lib.path_to_subprocess_arg(ffmpeg_path), + path_to_subprocess_arg(ffmpeg_path), " ".join(input_args), " ".join(output_args) ] @@ -345,7 +349,7 @@ class ExtractReviewSlate(openpype.api.Extractor): try: # Get information about input file via ffprobe tool - streams = openpype.lib.ffprobe_streams(full_input_path, self.log) + streams = get_ffprobe_streams(full_input_path, self.log) except Exception: self.log.warning( "Could not get codec data from input.", diff --git a/openpype/tests/test_lib_restructuralization.py b/openpype/tests/test_lib_restructuralization.py index d0461e55fb..94080e550d 100644 --- a/openpype/tests/test_lib_restructuralization.py +++ b/openpype/tests/test_lib_restructuralization.py @@ -24,7 +24,7 @@ def test_backward_compatibility(printer): from openpype.lib import get_hierarchy from openpype.lib import get_linked_assets from openpype.lib import get_latest_version - from openpype.lib import ffprobe_streams + from openpype.lib import get_ffprobe_streams from openpype.hosts.fusion.lib import switch_item From fbf57760abe67c0d39b7851fd70f688628ddf767 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 11 Mar 2022 17:55:36 +0100 Subject: [PATCH 197/302] added functions to replicate ffmpeg format and codec based on input data --- openpype/lib/__init__.py | 4 + openpype/lib/transcoding.py | 186 ++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index bf4a3781bc..523ced8022 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -86,6 +86,8 @@ from .transcoding import ( convert_for_ffmpeg, get_ffprobe_data, get_ffprobe_streams, + get_ffmpeg_codec_args, + get_ffmpeg_format_args, ) from .avalon_context import ( CURRENT_DOC_SCHEMAS, @@ -230,6 +232,8 @@ __all__ = [ "convert_for_ffmpeg", "get_ffprobe_data", "get_ffprobe_streams", + "get_ffmpeg_codec_args", + "get_ffmpeg_format_args", "CURRENT_DOC_SCHEMAS", "PROJECT_NAME_ALLOWED_SYMBOLS", diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 8137c4ae11..554fad8813 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -546,3 +546,189 @@ def get_ffprobe_streams(path_to_file, logger=None): logger (logging.Logger): injected logger, if empty new is created """ return get_ffprobe_data(path_to_file, logger)["streams"] + + +def get_ffmpeg_format_args(ffprobe_data, source_ffmpeg_cmd=None): + """Copy format from input metadata for output. + + Args: + ffprobe_data(dict): Data received from ffprobe. + source_ffmpeg_cmd(str): Command that created input if available. + """ + input_format = ffprobe_data.get("format") or {} + if input_format.get("format_name") == "mxf": + return _ffmpeg_mxf_format_args(ffprobe_data, source_ffmpeg_cmd) + return [] + + +def _ffmpeg_mxf_format_args(ffprobe_data, source_ffmpeg_cmd): + input_format = ffprobe_data["format"] + format_tags = input_format.get("tags") or {} + product_name = format_tags.get("product_name") or "" + output = [] + if "opatom" in product_name.lower(): + output.extend(["-f", "mxf_opatom"]) + return output + + +def get_ffmpeg_codec_args(ffprobe_data, source_ffmpeg_cmd=None, logger=None): + """Copy codec from input metadata for output. + + Args: + ffprobe_data(dict): Data received from ffprobe. + source_ffmpeg_cmd(str): Command that created input if available. + """ + if logger is None: + logger = logging.getLogger(__name__) + + video_stream = None + no_audio_stream = None + for stream in ffprobe_data["streams"]: + codec_type = stream["codec_type"] + if codec_type == "video": + video_stream = stream + break + elif no_audio_stream is None and codec_type != "audio": + no_audio_stream = stream + + if video_stream is None: + if no_audio_stream is None: + logger.warning( + "Couldn't find stream that is not an audio file." + ) + return [] + logger.info( + "Didn't find video stream. Using first non audio stream." + ) + video_stream = no_audio_stream + + codec_name = video_stream.get("codec_name") + # Codec "prores" + if codec_name == "prores": + return _ffmpeg_prores_codec_args(video_stream, source_ffmpeg_cmd) + + # Codec "h264" + if codec_name == "h264": + return _ffmpeg_h264_codec_args(video_stream, source_ffmpeg_cmd) + + # Coded DNxHD + if codec_name == "dnxhd": + return _ffmpeg_dnxhd_codec_args(video_stream, source_ffmpeg_cmd) + + output = [] + if codec_name: + output.extend(["-codec:v", codec_name]) + + bit_rate = video_stream.get("bit_rate") + if bit_rate: + output.extend(["-b:v", bit_rate]) + + pix_fmt = video_stream.get("pix_fmt") + if pix_fmt: + output.extend(["-pix_fmt", pix_fmt]) + + output.extend(["-g", "1"]) + + return output + + +def _ffmpeg_prores_codec_args(stream_data, source_ffmpeg_cmd): + output = [] + + tags = stream_data.get("tags") or {} + encoder = tags.get("encoder") or "" + if encoder.endswith("prores_ks"): + codec_name = "prores_ks" + + elif encoder.endswith("prores_aw"): + codec_name = "prores_aw" + + else: + codec_name = "prores" + + output.extend(["-codec:v", codec_name]) + + pix_fmt = stream_data.get("pix_fmt") + if pix_fmt: + output.extend(["-pix_fmt", pix_fmt]) + + # Rest of arguments is prores_kw specific + if codec_name == "prores_ks": + codec_tag_to_profile_map = { + "apco": "proxy", + "apcs": "lt", + "apcn": "standard", + "apch": "hq", + "ap4h": "4444", + "ap4x": "4444xq" + } + codec_tag_str = stream_data.get("codec_tag_string") + if codec_tag_str: + profile = codec_tag_to_profile_map.get(codec_tag_str) + if profile: + output.extend(["-profile:v", profile]) + + return output + + +def _ffmpeg_h264_codec_args(stream_data, source_ffmpeg_cmd): + output = ["-codec:v", "h264"] + + # Use arguments from source if are available source arguments + if source_ffmpeg_cmd: + copy_args = ( + "-crf", + "-b:v", "-vb", + "-minrate", "-minrate:", + "-maxrate", "-maxrate:", + "-bufsize", "-bufsize:" + ) + args = source_ffmpeg_cmd.split(" ") + for idx, arg in enumerate(args): + if arg in copy_args: + output.extend([arg, args[idx + 1]]) + + pix_fmt = stream_data.get("pix_fmt") + if pix_fmt: + output.extend(["-pix_fmt", pix_fmt]) + + output.extend(["-intra"]) + output.extend(["-g", "1"]) + + return output + + +def _ffmpeg_dnxhd_codec_args(stream_data, source_ffmpeg_cmd): + output = ["-codec:v", "dnxhd"] + + # Use source profile (profiles in metadata are not usable in args directly) + profile = stream_data.get("profile") or "" + # Lower profile and replace space with underscore + cleaned_profile = profile.lower().replace(" ", "_") + dnx_profiles = { + "dnxhd", + "dnxhr_lb", + "dnxhr_sq", + "dnxhr_hq", + "dnxhr_hqx", + "dnxhr_444" + } + if cleaned_profile in dnx_profiles: + output.extend(["-profile:v", cleaned_profile]) + + pix_fmt = stream_data.get("pix_fmt") + if pix_fmt: + output.extend(["-pix_fmt", pix_fmt]) + + # Use arguments from source if are available source arguments + if source_ffmpeg_cmd: + copy_args = ( + "-b:v", "-vb", + ) + args = source_ffmpeg_cmd.split(" ") + for idx, arg in enumerate(args): + if arg in copy_args: + output.extend([arg, args[idx + 1]]) + + output.extend(["-g", "1"]) + return output From 50c0580feff4142db32f183452d17a801d2dcf13 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 11 Mar 2022 17:59:52 +0100 Subject: [PATCH 198/302] use new functions in extract review slate and otio burnins --- .../publish/collect_published_files.py | 8 +- openpype/lib/__init__.py | 2 + openpype/lib/transcoding.py | 20 ++ openpype/lib/vendor_bin_utils.py | 20 -- .../plugins/publish/extract_review_slate.py | 52 ++---- openpype/scripts/otio_burnin.py | 172 ++---------------- 6 files changed, 53 insertions(+), 221 deletions(-) diff --git a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py index be33e6bb4e..65cef14703 100644 --- a/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py +++ b/openpype/hosts/webpublisher/plugins/publish/collect_published_files.py @@ -17,9 +17,9 @@ import pyblish.api from openpype.lib import ( prepare_template_data, get_asset, - get_ffprobe_streams + get_ffprobe_streams, + convert_ffprobe_fps_value, ) -from openpype.lib.vendor_bin_utils import get_fps from openpype.lib.plugin_tools import ( parse_json, get_subset_name_with_asset_doc @@ -292,7 +292,9 @@ class CollectPublishedFiles(pyblish.api.ContextPlugin): "nb_frames {} not convertible".format(nb_frames)) duration = stream.get("duration") - frame_rate = get_fps(stream.get("r_frame_rate", '0/0')) + frame_rate = convert_ffprobe_fps_value( + stream.get("r_frame_rate", '0/0') + ) self.log.debug("duration:: {} frame_rate:: {}".format( duration, frame_rate)) try: diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 523ced8022..d5cde3031f 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -88,6 +88,7 @@ from .transcoding import ( get_ffprobe_streams, get_ffmpeg_codec_args, get_ffmpeg_format_args, + convert_ffprobe_fps_value, ) from .avalon_context import ( CURRENT_DOC_SCHEMAS, @@ -234,6 +235,7 @@ __all__ = [ "get_ffprobe_streams", "get_ffmpeg_codec_args", "get_ffmpeg_format_args", + "convert_ffprobe_fps_value", "CURRENT_DOC_SCHEMAS", "PROJECT_NAME_ALLOWED_SYMBOLS", diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 554fad8813..6181ff6d13 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -732,3 +732,23 @@ def _ffmpeg_dnxhd_codec_args(stream_data, source_ffmpeg_cmd): output.extend(["-g", "1"]) return output + + +def convert_ffprobe_fps_value(str_value): + """Returns (str) value of fps from ffprobe frame format (120/1)""" + if str_value == "0/0": + print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".") + return "Unknown" + + items = str_value.split("/") + if len(items) == 1: + fps = float(items[0]) + + elif len(items) == 2: + fps = float(items[0]) / float(items[1]) + + # Check if fps is integer or float number + if int(fps) == fps: + fps = int(fps) + + return str(fps) diff --git a/openpype/lib/vendor_bin_utils.py b/openpype/lib/vendor_bin_utils.py index 36510f4238..23e28ea304 100644 --- a/openpype/lib/vendor_bin_utils.py +++ b/openpype/lib/vendor_bin_utils.py @@ -152,23 +152,3 @@ def is_oiio_supported(): )) return False return True - - -def get_fps(str_value): - """Returns (str) value of fps from ffprobe frame format (120/1)""" - if str_value == "0/0": - print("WARNING: Source has \"r_frame_rate\" value set to \"0/0\".") - return "Unknown" - - items = str_value.split("/") - if len(items) == 1: - fps = float(items[0]) - - elif len(items) == 2: - fps = float(items[0]) / float(items[1]) - - # Check if fps is integer or float number - if int(fps) == fps: - fps = int(fps) - - return str(fps) diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index e27a0b28bd..460d546340 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -4,7 +4,10 @@ import pyblish from openpype.lib import ( path_to_subprocess_arg, get_ffmpeg_tool_path, + get_ffprobe_data, get_ffprobe_streams, + get_ffmpeg_codec_args, + get_ffmpeg_format_args, ) @@ -161,7 +164,7 @@ class ExtractReviewSlate(openpype.api.Extractor): output_args.extend(repre["_profile"].get('output', [])) else: # Codecs are copied from source for whole input - codec_args = self.codec_args(repre) + codec_args = self._get_codec_args(repre) output_args.extend(codec_args) # make sure colors are correct @@ -335,7 +338,7 @@ class ExtractReviewSlate(openpype.api.Extractor): return vf_back - def codec_args(self, repre): + def _get_codec_args(self, repre): """Detect possible codec arguments from representation.""" codec_args = [] @@ -349,7 +352,7 @@ class ExtractReviewSlate(openpype.api.Extractor): try: # Get information about input file via ffprobe tool - streams = get_ffprobe_streams(full_input_path, self.log) + ffprobe_data = get_ffprobe_data(full_input_path, self.log) except Exception: self.log.warning( "Could not get codec data from input.", @@ -357,42 +360,11 @@ class ExtractReviewSlate(openpype.api.Extractor): ) return codec_args - # Try to find first stream that is not an audio - no_audio_stream = None - for stream in streams: - if stream.get("codec_type") != "audio": - no_audio_stream = stream - break + codec_args.extend( + get_ffmpeg_format_args(ffprobe_data) + ) + codec_args.extend( + get_ffmpeg_codec_args(ffprobe_data, logger=self.log) + ) - if no_audio_stream is None: - self.log.warning(( - "Couldn't find stream that is not an audio from file \"{}\"" - ).format(full_input_path)) - return codec_args - - codec_name = no_audio_stream.get("codec_name") - if codec_name: - codec_args.append("-codec:v {}".format(codec_name)) - - profile_name = no_audio_stream.get("profile") - if profile_name: - # Rest of arguments is prores_kw specific - if codec_name == "prores_ks": - codec_tag_to_profile_map = { - "apco": "proxy", - "apcs": "lt", - "apcn": "standard", - "apch": "hq", - "ap4h": "4444", - "ap4x": "4444xq" - } - codec_tag_str = no_audio_stream.get("codec_tag_string") - if codec_tag_str: - profile = codec_tag_to_profile_map.get(codec_tag_str) - if profile: - codec_args.extend(["-profile:v", profile]) - - pix_fmt = no_audio_stream.get("pix_fmt") - if pix_fmt: - codec_args.append("-pix_fmt {}".format(pix_fmt)) return codec_args diff --git a/openpype/scripts/otio_burnin.py b/openpype/scripts/otio_burnin.py index 874c08064a..1f57891b84 100644 --- a/openpype/scripts/otio_burnin.py +++ b/openpype/scripts/otio_burnin.py @@ -5,12 +5,17 @@ import subprocess import platform import json import opentimelineio_contrib.adapters.ffmpeg_burnins as ffmpeg_burnins -import openpype.lib -from openpype.lib.vendor_bin_utils import get_fps + +from openpype.lib import ( + get_ffmpeg_tool_path, + get_ffmpeg_codec_args, + get_ffmpeg_format_args, + convert_ffprobe_fps_value, +) -ffmpeg_path = openpype.lib.get_ffmpeg_tool_path("ffmpeg") -ffprobe_path = openpype.lib.get_ffmpeg_tool_path("ffprobe") +ffmpeg_path = get_ffmpeg_tool_path("ffmpeg") +ffprobe_path = get_ffmpeg_tool_path("ffprobe") FFMPEG = ( @@ -51,157 +56,6 @@ def _get_ffprobe_data(source): return json.loads(out) -def _prores_codec_args(stream_data, source_ffmpeg_cmd): - output = [] - - tags = stream_data.get("tags") or {} - encoder = tags.get("encoder") or "" - if encoder.endswith("prores_ks"): - codec_name = "prores_ks" - - elif encoder.endswith("prores_aw"): - codec_name = "prores_aw" - - else: - codec_name = "prores" - - output.extend(["-codec:v", codec_name]) - - pix_fmt = stream_data.get("pix_fmt") - if pix_fmt: - output.extend(["-pix_fmt", pix_fmt]) - - # Rest of arguments is prores_kw specific - if codec_name == "prores_ks": - codec_tag_to_profile_map = { - "apco": "proxy", - "apcs": "lt", - "apcn": "standard", - "apch": "hq", - "ap4h": "4444", - "ap4x": "4444xq" - } - codec_tag_str = stream_data.get("codec_tag_string") - if codec_tag_str: - profile = codec_tag_to_profile_map.get(codec_tag_str) - if profile: - output.extend(["-profile:v", profile]) - - return output - - -def _h264_codec_args(stream_data, source_ffmpeg_cmd): - output = ["-codec:v", "h264"] - - # Use arguments from source if are available source arguments - if source_ffmpeg_cmd: - copy_args = ( - "-crf", - "-b:v", "-vb", - "-minrate", "-minrate:", - "-maxrate", "-maxrate:", - "-bufsize", "-bufsize:" - ) - args = source_ffmpeg_cmd.split(" ") - for idx, arg in enumerate(args): - if arg in copy_args: - output.extend([arg, args[idx + 1]]) - - pix_fmt = stream_data.get("pix_fmt") - if pix_fmt: - output.extend(["-pix_fmt", pix_fmt]) - - output.extend(["-intra"]) - output.extend(["-g", "1"]) - - return output - - -def _dnxhd_codec_args(stream_data, source_ffmpeg_cmd): - output = ["-codec:v", "dnxhd"] - - # Use source profile (profiles in metadata are not usable in args directly) - profile = stream_data.get("profile") or "" - # Lower profile and replace space with underscore - cleaned_profile = profile.lower().replace(" ", "_") - dnx_profiles = { - "dnxhd", - "dnxhr_lb", - "dnxhr_sq", - "dnxhr_hq", - "dnxhr_hqx", - "dnxhr_444" - } - if cleaned_profile in dnx_profiles: - output.extend(["-profile:v", cleaned_profile]) - - pix_fmt = stream_data.get("pix_fmt") - if pix_fmt: - output.extend(["-pix_fmt", pix_fmt]) - - # Use arguments from source if are available source arguments - if source_ffmpeg_cmd: - copy_args = ( - "-b:v", "-vb", - ) - args = source_ffmpeg_cmd.split(" ") - for idx, arg in enumerate(args): - if arg in copy_args: - output.extend([arg, args[idx + 1]]) - - output.extend(["-g", "1"]) - return output - - -def _mxf_format_args(ffprobe_data, source_ffmpeg_cmd): - input_format = ffprobe_data["format"] - format_tags = input_format.get("tags") or {} - product_name = format_tags.get("product_name") or "" - output = [] - if "opatom" in product_name.lower(): - output.extend(["-f", "mxf_opatom"]) - return output - - -def get_format_args(ffprobe_data, source_ffmpeg_cmd): - input_format = ffprobe_data.get("format") or {} - if input_format.get("format_name") == "mxf": - return _mxf_format_args(ffprobe_data, source_ffmpeg_cmd) - return [] - - -def get_codec_args(ffprobe_data, source_ffmpeg_cmd): - stream_data = ffprobe_data["streams"][0] - codec_name = stream_data.get("codec_name") - # Codec "prores" - if codec_name == "prores": - return _prores_codec_args(stream_data, source_ffmpeg_cmd) - - # Codec "h264" - if codec_name == "h264": - return _h264_codec_args(stream_data, source_ffmpeg_cmd) - - # Coded DNxHD - if codec_name == "dnxhd": - return _dnxhd_codec_args(stream_data, source_ffmpeg_cmd) - - output = [] - if codec_name: - output.extend(["-codec:v", codec_name]) - - bit_rate = stream_data.get("bit_rate") - if bit_rate: - output.extend(["-b:v", bit_rate]) - - pix_fmt = stream_data.get("pix_fmt") - if pix_fmt: - output.extend(["-pix_fmt", pix_fmt]) - - output.extend(["-g", "1"]) - - return output - - class ModifiedBurnins(ffmpeg_burnins.Burnins): ''' This is modification of OTIO FFmpeg Burnin adapter. @@ -592,7 +446,9 @@ def burnins_from_data( data["resolution_height"] = stream.get("height", MISSING_KEY_VALUE) if "fps" not in data: - data["fps"] = get_fps(stream.get("r_frame_rate", "0/0")) + data["fps"] = convert_ffprobe_fps_value( + stream.get("r_frame_rate", "0/0") + ) # Check frame start and add expression if is available if frame_start is not None: @@ -703,10 +559,10 @@ def burnins_from_data( else: ffmpeg_args.extend( - get_format_args(burnin.ffprobe_data, source_ffmpeg_cmd) + get_ffmpeg_format_args(burnin.ffprobe_data, source_ffmpeg_cmd) ) ffmpeg_args.extend( - get_codec_args(burnin.ffprobe_data, source_ffmpeg_cmd) + get_ffmpeg_codec_args(burnin.ffprobe_data, source_ffmpeg_cmd) ) # Use arguments from source if are available source arguments if source_ffmpeg_cmd: From 3ef05cc480598ea1c900f48bd704c4d808647e3d Mon Sep 17 00:00:00 2001 From: OpenPype Date: Sat, 12 Mar 2022 03:37:54 +0000 Subject: [PATCH 199/302] [Automated] Bump version --- CHANGELOG.md | 10 +++++----- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f971c33208..ebc563c90b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,16 @@ # Changelog -## [3.9.0-nightly.8](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0-nightly.9](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) **Deprecated:** - AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845) -- Houdini: Remove unused code [\#2779](https://github.com/pypeclub/OpenPype/pull/2779) **🚀 Enhancements** +- General: Subset name filtering in ExtractReview outpus [\#2872](https://github.com/pypeclub/OpenPype/pull/2872) - NewPublisher: Descriptions and Icons in creator dialog [\#2867](https://github.com/pypeclub/OpenPype/pull/2867) - NewPublisher: Changing task on publishing instance [\#2863](https://github.com/pypeclub/OpenPype/pull/2863) - TrayPublisher: Choose project widget is more clear [\#2859](https://github.com/pypeclub/OpenPype/pull/2859) @@ -22,10 +22,10 @@ - global: letter box calculated on output as last process [\#2812](https://github.com/pypeclub/OpenPype/pull/2812) - Nuke: adding Reformat to baking mov plugin [\#2811](https://github.com/pypeclub/OpenPype/pull/2811) - Manager: Update all to latest button [\#2805](https://github.com/pypeclub/OpenPype/pull/2805) -- General: Set context environments for non host applications [\#2803](https://github.com/pypeclub/OpenPype/pull/2803) **🐛 Bug fixes** +- General: Missing time function [\#2877](https://github.com/pypeclub/OpenPype/pull/2877) - Deadline: Fix plugin name for tile assemble [\#2868](https://github.com/pypeclub/OpenPype/pull/2868) - General: Fix hardlink for windows [\#2864](https://github.com/pypeclub/OpenPype/pull/2864) - General: ffmpeg was crashing on slate merge [\#2860](https://github.com/pypeclub/OpenPype/pull/2860) @@ -47,13 +47,13 @@ - Ftrack: Job killer with missing user [\#2819](https://github.com/pypeclub/OpenPype/pull/2819) - Nuke: Use AVALON\_APP to get value for "app" key [\#2818](https://github.com/pypeclub/OpenPype/pull/2818) - StandalonePublisher: use dynamic groups in subset names [\#2816](https://github.com/pypeclub/OpenPype/pull/2816) -- Settings UI: Search case sensitivity [\#2810](https://github.com/pypeclub/OpenPype/pull/2810) -- Flame Babypublisher optimalization [\#2806](https://github.com/pypeclub/OpenPype/pull/2806) **🔀 Refactored code** +- Refactor: move webserver tool to openpype [\#2876](https://github.com/pypeclub/OpenPype/pull/2876) - General: Move create logic from avalon to OpenPype [\#2854](https://github.com/pypeclub/OpenPype/pull/2854) - General: Add vendors from avalon [\#2848](https://github.com/pypeclub/OpenPype/pull/2848) +- General: Basic event system [\#2846](https://github.com/pypeclub/OpenPype/pull/2846) - General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839) - Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829) - Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823) diff --git a/openpype/version.py b/openpype/version.py index d4af8d760f..39d3037221 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.8" +__version__ = "3.9.0-nightly.9" diff --git a/pyproject.toml b/pyproject.toml index fe681266ca..7a7411fdfd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.8" # OpenPype +version = "3.9.0-nightly.9" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 8d14f5fb7017adac8401137a6815ca49f1bd1a8b Mon Sep 17 00:00:00 2001 From: OpenPype Date: Mon, 14 Mar 2022 08:17:30 +0000 Subject: [PATCH 200/302] [Automated] Release --- CHANGELOG.md | 11 ++++------- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebc563c90b..5acb161bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [3.9.0-nightly.9](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...3.9.0) **Deprecated:** @@ -27,6 +27,7 @@ - General: Missing time function [\#2877](https://github.com/pypeclub/OpenPype/pull/2877) - Deadline: Fix plugin name for tile assemble [\#2868](https://github.com/pypeclub/OpenPype/pull/2868) +- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866) - General: Fix hardlink for windows [\#2864](https://github.com/pypeclub/OpenPype/pull/2864) - General: ffmpeg was crashing on slate merge [\#2860](https://github.com/pypeclub/OpenPype/pull/2860) - WebPublisher: Video file was published with one too many frame [\#2858](https://github.com/pypeclub/OpenPype/pull/2858) @@ -35,6 +36,7 @@ - Nuke: slate resolution to input video resolution [\#2853](https://github.com/pypeclub/OpenPype/pull/2853) - WebPublisher: Fix username stored in DB [\#2852](https://github.com/pypeclub/OpenPype/pull/2852) - WebPublisher: Fix wrong number of frames for video file [\#2851](https://github.com/pypeclub/OpenPype/pull/2851) +- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847) - Nuke: fix multiple baking profile farm publishing [\#2842](https://github.com/pypeclub/OpenPype/pull/2842) - Blender: Fixed parameters for FBX export of the camera [\#2840](https://github.com/pypeclub/OpenPype/pull/2840) - Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832) @@ -59,11 +61,6 @@ - Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823) - General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766) -**Merged pull requests:** - -- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866) -- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847) - ## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.8.2-nightly.3...3.8.2) diff --git a/openpype/version.py b/openpype/version.py index 39d3037221..d2182ac7da 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0-nightly.9" +__version__ = "3.9.0" diff --git a/pyproject.toml b/pyproject.toml index 7a7411fdfd..681702560a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0-nightly.9" # OpenPype +version = "3.9.0" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 3e840894137cbf40f9aff29e59cc4663c5dc29ec Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 14 Mar 2022 10:07:11 +0100 Subject: [PATCH 201/302] update avalon submodule --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index ffe9e910f1..7753d15507 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit ffe9e910f1f382e222d457d8e4a8426c41ed43ae +Subproject commit 7753d15507afadc143b7d49db8fcfaa6a29fed91 From b46a7a538787e733f8d77a7cba89b7166bde133a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 14 Mar 2022 10:49:48 +0100 Subject: [PATCH 202/302] OP-2813 - fix wrong merge --- openpype/lib/delivery.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index f1855d9550..5a69afd5aa 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -83,15 +83,14 @@ def path_from_representation(representation, anatomy): def copy_file(src_path, dst_path): """Hardlink file if possible(to save space), copy if not""" - from avalon.vendor import filelink # safer importing + from openpype.lib import create_hard_link # safer importing if os.path.exists(dst_path): return try: - filelink.create( + create_hard_link( src_path, - dst_path, - filelink.HARDLINK + dst_path ) except OSError: shutil.copyfile(src_path, dst_path) From 4fed92a1e542c88fa6f4c5091efebf280d2fc02b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 10:57:23 +0100 Subject: [PATCH 203/302] copied base of load logic into openpype --- openpype/pipeline/load/__init__.py | 78 ++++ openpype/pipeline/load/plugins.py | 129 ++++++ openpype/pipeline/load/utils.py | 706 +++++++++++++++++++++++++++++ 3 files changed, 913 insertions(+) create mode 100644 openpype/pipeline/load/__init__.py create mode 100644 openpype/pipeline/load/plugins.py create mode 100644 openpype/pipeline/load/utils.py diff --git a/openpype/pipeline/load/__init__.py b/openpype/pipeline/load/__init__.py new file mode 100644 index 0000000000..2af15e8705 --- /dev/null +++ b/openpype/pipeline/load/__init__.py @@ -0,0 +1,78 @@ +from .utils import ( + HeroVersionType, + IncompatibleLoaderError, + + get_repres_contexts, + get_subset_contexts, + get_representation_context, + + load_with_repre_context, + load_with_subset_context, + load_with_subset_contexts, + + load_representation, + remove_container, + update_container, + switch_container, + + get_loader_identifier, + + get_representation_path_from_context, + get_representation_path, + + is_compatible_loader, + + loaders_from_repre_context, + loaders_from_representation, +) + +from .plugins import ( + LoaderPlugin, + SubsetLoaderPlugin, + + discover_loader_plugins, + register_loader_plugin, + deregister_loader_plugins_path, + register_loader_plugins_path, + deregister_loader_plugin, +) + + +__all__ = ( + # utils.py + "HeroVersionType", + "IncompatibleLoaderError", + + "get_repres_contexts", + "get_subset_contexts", + "get_representation_context", + + "load_with_repre_context", + "load_with_subset_context", + "load_with_subset_contexts", + + "load_representation", + "remove_container", + "update_container", + "switch_container", + + "get_loader_identifier", + + "get_representation_path_from_context", + "get_representation_path", + + "is_compatible_loader", + + "loaders_from_repre_context", + "loaders_from_representation", + + # plugins.py + "LoaderPlugin", + "SubsetLoaderPlugin", + + "discover_loader_plugins", + "register_loader_plugin", + "deregister_loader_plugins_path", + "register_loader_plugins_path", + "deregister_loader_plugin", +) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py new file mode 100644 index 0000000000..d7e21e1248 --- /dev/null +++ b/openpype/pipeline/load/plugins.py @@ -0,0 +1,129 @@ +import logging + +from avalon.api import ( + discover, + register_plugin, + deregister_plugin, + register_plugin_path, + deregister_plugin_path, +) + +from .utils import get_representation_path_from_context + + +class LoaderPlugin(list): + """Load representation into host application + + Arguments: + context (dict): avalon-core:context-1.0 + name (str, optional): Use pre-defined name + namespace (str, optional): Use pre-defined namespace + + .. versionadded:: 4.0 + This class was introduced + + """ + + families = list() + representations = list() + order = 0 + is_multiple_contexts_compatible = False + + options = [] + + log = logging.getLogger("SubsetLoader") + log.propagate = True + + def __init__(self, context): + self.fname = self.filepath_from_context(context) + + @classmethod + def get_representations(cls): + return cls.representations + + def filepath_from_context(self, context): + return get_representation_path_from_context(context) + + def load(self, context, name=None, namespace=None, options=None): + """Load asset via database + + Arguments: + context (dict): Full parenthood of representation to load + name (str, optional): Use pre-defined name + namespace (str, optional): Use pre-defined namespace + options (dict, optional): Additional settings dictionary + + """ + raise NotImplementedError("Loader.load() must be " + "implemented by subclass") + + def update(self, container, representation): + """Update `container` to `representation` + + Arguments: + container (avalon-core:container-1.0): Container to update, + from `host.ls()`. + representation (dict): Update the container to this representation. + + """ + raise NotImplementedError("Loader.update() must be " + "implemented by subclass") + + def remove(self, container): + """Remove a container + + Arguments: + container (avalon-core:container-1.0): Container to remove, + from `host.ls()`. + + Returns: + bool: Whether the container was deleted + + """ + + raise NotImplementedError("Loader.remove() must be " + "implemented by subclass") + + @classmethod + def get_options(cls, contexts): + """ + Returns static (cls) options or could collect from 'contexts'. + + Args: + contexts (list): of repre or subset contexts + Returns: + (list) + """ + return cls.options or [] + + +class SubsetLoaderPlugin(LoaderPlugin): + """Load subset into host application + Arguments: + context (dict): avalon-core:context-1.0 + name (str, optional): Use pre-defined name + namespace (str, optional): Use pre-defined namespace + """ + + def __init__(self, context): + pass + + +def discover_loader_plugins(): + return discover(LoaderPlugin) + + +def register_loader_plugin(plugin): + return register_plugin(LoaderPlugin, plugin) + + +def deregister_loader_plugins_path(path): + deregister_plugin_path(LoaderPlugin, path) + + +def register_loader_plugins_path(path): + return register_plugin_path(LoaderPlugin, path) + + +def deregister_loader_plugin(plugin): + deregister_plugin(LoaderPlugin, plugin) diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py new file mode 100644 index 0000000000..4ef0f099d7 --- /dev/null +++ b/openpype/pipeline/load/utils.py @@ -0,0 +1,706 @@ +import os +import platform +import copy +import getpass +import logging +import inspect +import numbers + +import six + +from avalon import io, schema +from avalon.api import Session, registered_root + +from openpype.lib import Anatomy + +log = logging.getLogger(__name__) + + +class HeroVersionType(object): + def __init__(self, version): + assert isinstance(version, numbers.Integral), ( + "Version is not an integer. \"{}\" {}".format( + version, str(type(version)) + ) + ) + self.version = version + + def __str__(self): + return str(self.version) + + def __int__(self): + return int(self.version) + + def __format__(self, format_spec): + return self.version.__format__(format_spec) + + +class IncompatibleLoaderError(ValueError): + """Error when Loader is incompatible with a representation.""" + pass + + +def get_repres_contexts(representation_ids, dbcon=None): + """Return parenthood context for representation. + + Args: + representation_ids (list): The representation ids. + dbcon (AvalonMongoDB): Mongo connection object. `avalon.io` used when + not entered. + + Returns: + dict: The full representation context by representation id. + keys are repre_id, value is dictionary with full: + asset_doc + version_doc + subset_doc + repre_doc + + """ + if not dbcon: + dbcon = io + + contexts = {} + if not representation_ids: + return contexts + + _representation_ids = [] + for repre_id in representation_ids: + if isinstance(repre_id, six.string_types): + repre_id = io.ObjectId(repre_id) + _representation_ids.append(repre_id) + + repre_docs = dbcon.find({ + "type": "representation", + "_id": {"$in": _representation_ids} + }) + repre_docs_by_id = {} + version_ids = set() + for repre_doc in repre_docs: + version_ids.add(repre_doc["parent"]) + repre_docs_by_id[repre_doc["_id"]] = repre_doc + + version_docs = dbcon.find({ + "type": {"$in": ["version", "hero_version"]}, + "_id": {"$in": list(version_ids)} + }) + + version_docs_by_id = {} + hero_version_docs = [] + versions_for_hero = set() + subset_ids = set() + for version_doc in version_docs: + if version_doc["type"] == "hero_version": + hero_version_docs.append(version_doc) + versions_for_hero.add(version_doc["version_id"]) + version_docs_by_id[version_doc["_id"]] = version_doc + subset_ids.add(version_doc["parent"]) + + if versions_for_hero: + _version_docs = dbcon.find({ + "type": "version", + "_id": {"$in": list(versions_for_hero)} + }) + _version_data_by_id = { + version_doc["_id"]: version_doc["data"] + for version_doc in _version_docs + } + + for hero_version_doc in hero_version_docs: + hero_version_id = hero_version_doc["_id"] + version_id = hero_version_doc["version_id"] + version_data = copy.deepcopy(_version_data_by_id[version_id]) + version_docs_by_id[hero_version_id]["data"] = version_data + + subset_docs = dbcon.find({ + "type": "subset", + "_id": {"$in": list(subset_ids)} + }) + subset_docs_by_id = {} + asset_ids = set() + for subset_doc in subset_docs: + subset_docs_by_id[subset_doc["_id"]] = subset_doc + asset_ids.add(subset_doc["parent"]) + + asset_docs = dbcon.find({ + "type": "asset", + "_id": {"$in": list(asset_ids)} + }) + asset_docs_by_id = { + asset_doc["_id"]: asset_doc + for asset_doc in asset_docs + } + + project_doc = dbcon.find_one({"type": "project"}) + + for repre_id, repre_doc in repre_docs_by_id.items(): + version_doc = version_docs_by_id[repre_doc["parent"]] + subset_doc = subset_docs_by_id[version_doc["parent"]] + asset_doc = asset_docs_by_id[subset_doc["parent"]] + context = { + "project": { + "name": project_doc["name"], + "code": project_doc["data"].get("code") + }, + "asset": asset_doc, + "subset": subset_doc, + "version": version_doc, + "representation": repre_doc, + } + contexts[repre_id] = context + + return contexts + + +def get_subset_contexts(subset_ids, dbcon=None): + """Return parenthood context for subset. + + Provides context on subset granularity - less detail than + 'get_repre_contexts'. + Args: + subset_ids (list): The subset ids. + dbcon (AvalonMongoDB): Mongo connection object. `avalon.io` used when + not entered. + Returns: + dict: The full representation context by representation id. + """ + if not dbcon: + dbcon = io + + contexts = {} + if not subset_ids: + return contexts + + _subset_ids = set() + for subset_id in subset_ids: + if isinstance(subset_id, six.string_types): + subset_id = io.ObjectId(subset_id) + _subset_ids.add(subset_id) + + subset_docs = dbcon.find({ + "type": "subset", + "_id": {"$in": list(_subset_ids)} + }) + subset_docs_by_id = {} + asset_ids = set() + for subset_doc in subset_docs: + subset_docs_by_id[subset_doc["_id"]] = subset_doc + asset_ids.add(subset_doc["parent"]) + + asset_docs = dbcon.find({ + "type": "asset", + "_id": {"$in": list(asset_ids)} + }) + asset_docs_by_id = { + asset_doc["_id"]: asset_doc + for asset_doc in asset_docs + } + + project_doc = dbcon.find_one({"type": "project"}) + + for subset_id, subset_doc in subset_docs_by_id.items(): + asset_doc = asset_docs_by_id[subset_doc["parent"]] + context = { + "project": { + "name": project_doc["name"], + "code": project_doc["data"].get("code") + }, + "asset": asset_doc, + "subset": subset_doc + } + contexts[subset_id] = context + + return contexts + + +def get_representation_context(representation): + """Return parenthood context for representation. + + Args: + representation (str or io.ObjectId or dict): The representation id + or full representation as returned by the database. + + Returns: + dict: The full representation context. + + """ + + assert representation is not None, "This is a bug" + + if isinstance(representation, (six.string_types, io.ObjectId)): + representation = io.find_one( + {"_id": io.ObjectId(str(representation))}) + + version, subset, asset, project = io.parenthood(representation) + + assert all([representation, version, subset, asset, project]), ( + "This is a bug" + ) + + context = { + "project": { + "name": project["name"], + "code": project["data"].get("code", '') + }, + "asset": asset, + "subset": subset, + "version": version, + "representation": representation, + } + + return context + + +def load_with_repre_context( + Loader, repre_context, namespace=None, name=None, options=None, **kwargs +): + + # Ensure the Loader is compatible for the representation + if not is_compatible_loader(Loader, repre_context): + raise IncompatibleLoaderError( + "Loader {} is incompatible with {}".format( + Loader.__name__, repre_context["subset"]["name"] + ) + ) + + # Ensure options is a dictionary when no explicit options provided + if options is None: + options = kwargs.get("data", dict()) # "data" for backward compat + + assert isinstance(options, dict), "Options must be a dictionary" + + # Fallback to subset when name is None + if name is None: + name = repre_context["subset"]["name"] + + log.info( + "Running '%s' on '%s'" % ( + Loader.__name__, repre_context["asset"]["name"] + ) + ) + + loader = Loader(repre_context) + return loader.load(repre_context, name, namespace, options) + + +def load_with_subset_context( + Loader, subset_context, namespace=None, name=None, options=None, **kwargs +): + + # Ensure options is a dictionary when no explicit options provided + if options is None: + options = kwargs.get("data", dict()) # "data" for backward compat + + assert isinstance(options, dict), "Options must be a dictionary" + + # Fallback to subset when name is None + if name is None: + name = subset_context["subset"]["name"] + + log.info( + "Running '%s' on '%s'" % ( + Loader.__name__, subset_context["asset"]["name"] + ) + ) + + loader = Loader(subset_context) + return loader.load(subset_context, name, namespace, options) + + +def load_with_subset_contexts( + Loader, subset_contexts, namespace=None, name=None, options=None, **kwargs +): + + # Ensure options is a dictionary when no explicit options provided + if options is None: + options = kwargs.get("data", dict()) # "data" for backward compat + + assert isinstance(options, dict), "Options must be a dictionary" + + # Fallback to subset when name is None + joined_subset_names = " | ".join( + context["subset"]["name"] + for context in subset_contexts + ) + if name is None: + name = joined_subset_names + + log.info( + "Running '{}' on '{}'".format(Loader.__name__, joined_subset_names) + ) + + loader = Loader(subset_contexts) + return loader.load(subset_contexts, name, namespace, options) + + +def load_representation( + Loader, representation, namespace=None, name=None, options=None, **kwargs +): + """Use Loader to load a representation. + + Args: + Loader (Loader): The loader class to trigger. + representation (str or io.ObjectId or dict): The representation id + or full representation as returned by the database. + namespace (str, Optional): The namespace to assign. Defaults to None. + name (str, Optional): The name to assign. Defaults to subset name. + options (dict, Optional): Additional options to pass on to the loader. + + Returns: + The return of the `loader.load()` method. + + Raises: + IncompatibleLoaderError: When the loader is not compatible with + the representation. + + """ + + context = get_representation_context(representation) + return load_with_repre_context( + Loader, + context, + namespace=namespace, + name=name, + options=options, + **kwargs + ) + + +def get_loader_identifier(loader): + """Loader identifier from loader plugin or object. + + Identifier should be stored to container for future management. + """ + if not inspect.isclass(loader): + loader = loader.__class__ + return loader.__name__ + + +def _get_container_loader(container): + """Return the Loader corresponding to the container""" + from .plugins import discover_loader_plugins + + loader = container["loader"] + for Plugin in discover_loader_plugins(): + # TODO: Ensure the loader is valid + if get_loader_identifier(Plugin) == loader: + return Plugin + return None + + +def remove_container(container): + """Remove a container""" + + Loader = _get_container_loader(container) + if not Loader: + raise RuntimeError("Can't remove container. See log for details.") + + loader = Loader(get_representation_context(container["representation"])) + return loader.remove(container) + + +def update_container(container, version=-1): + """Update a container""" + + # Compute the different version from 'representation' + current_representation = io.find_one({ + "_id": io.ObjectId(container["representation"]) + }) + + assert current_representation is not None, "This is a bug" + + current_version, subset, asset, project = io.parenthood( + current_representation) + + if version == -1: + new_version = io.find_one({ + "type": "version", + "parent": subset["_id"] + }, sort=[("name", -1)]) + else: + if isinstance(version, HeroVersionType): + version_query = { + "parent": subset["_id"], + "type": "hero_version" + } + else: + version_query = { + "parent": subset["_id"], + "type": "version", + "name": version + } + new_version = io.find_one(version_query) + + assert new_version is not None, "This is a bug" + + new_representation = io.find_one({ + "type": "representation", + "parent": new_version["_id"], + "name": current_representation["name"] + }) + + assert new_representation is not None, "Representation wasn't found" + + path = get_representation_path(new_representation) + assert os.path.exists(path), "Path {} doesn't exist".format(path) + + # Run update on the Loader for this container + Loader = _get_container_loader(container) + if not Loader: + raise RuntimeError("Can't update container. See log for details.") + + loader = Loader(get_representation_context(container["representation"])) + return loader.update(container, new_representation) + + +def switch_container(container, representation, loader_plugin=None): + """Switch a container to representation + + Args: + container (dict): container information + representation (dict): representation data from document + + Returns: + function call + """ + + # Get the Loader for this container + if loader_plugin is None: + loader_plugin = _get_container_loader(container) + + if not loader_plugin: + raise RuntimeError("Can't switch container. See log for details.") + + if not hasattr(loader_plugin, "switch"): + # Backwards compatibility (classes without switch support + # might be better to just have "switch" raise NotImplementedError + # on the base class of Loader\ + raise RuntimeError("Loader '{}' does not support 'switch'".format( + loader_plugin.label + )) + + # Get the new representation to switch to + new_representation = io.find_one({ + "type": "representation", + "_id": representation["_id"], + }) + + new_context = get_representation_context(new_representation) + if not is_compatible_loader(loader_plugin, new_context): + raise AssertionError("Must be compatible Loader") + + loader = loader_plugin(new_context) + + return loader.switch(container, new_representation) + + +def get_representation_path_from_context(context): + """Preparation wrapper using only context as a argument""" + representation = context['representation'] + project_doc = context.get("project") + root = None + session_project = Session.get("AVALON_PROJECT") + if project_doc and project_doc["name"] != session_project: + anatomy = Anatomy(project_doc["name"]) + root = anatomy.roots_obj + + return get_representation_path(representation, root) + + +def get_representation_path(representation, root=None, dbcon=None): + """Get filename from representation document + + There are three ways of getting the path from representation which are + tried in following sequence until successful. + 1. Get template from representation['data']['template'] and data from + representation['context']. Then format template with the data. + 2. Get template from project['config'] and format it with default data set + 3. Get representation['data']['path'] and use it directly + + Args: + representation(dict): representation document from the database + + Returns: + str: fullpath of the representation + + """ + + from openpype.lib import StringTemplate + + if dbcon is None: + dbcon = io + + if root is None: + root = registered_root() + + def path_from_represenation(): + try: + template = representation["data"]["template"] + except KeyError: + return None + + try: + context = representation["context"] + context["root"] = root + template_obj = StringTemplate(template) + path = str(template_obj.format(context)) + # Force replacing backslashes with forward slashed if not on + # windows + if platform.system().lower() != "windows": + path = path.replace("\\", "/") + except KeyError: + # Template references unavailable data + return None + + if not path: + return path + + normalized_path = os.path.normpath(path) + if os.path.exists(normalized_path): + return normalized_path + return path + + def path_from_config(): + try: + version_, subset, asset, project = dbcon.parenthood(representation) + except ValueError: + log.debug( + "Representation %s wasn't found in database, " + "like a bug" % representation["name"] + ) + return None + + try: + template = project["config"]["template"]["publish"] + except KeyError: + log.debug( + "No template in project %s, " + "likely a bug" % project["name"] + ) + return None + + # default list() in get would not discover missing parents on asset + parents = asset.get("data", {}).get("parents") + if parents is not None: + hierarchy = "/".join(parents) + + # Cannot fail, required members only + data = { + "root": root, + "project": { + "name": project["name"], + "code": project.get("data", {}).get("code") + }, + "asset": asset["name"], + "silo": asset.get("silo"), + "hierarchy": hierarchy, + "subset": subset["name"], + "version": version_["name"], + "representation": representation["name"], + "family": representation.get("context", {}).get("family"), + "user": dbcon.Session.get("AVALON_USER", getpass.getuser()), + "app": dbcon.Session.get("AVALON_APP", ""), + "task": dbcon.Session.get("AVALON_TASK", "") + } + + try: + template_obj = StringTemplate(template) + path = str(template_obj.format(data)) + # Force replacing backslashes with forward slashed if not on + # windows + if platform.system().lower() != "windows": + path = path.replace("\\", "/") + + except KeyError as e: + log.debug("Template references unavailable data: %s" % e) + return None + + normalized_path = os.path.normpath(path) + if os.path.exists(normalized_path): + return normalized_path + return path + + def path_from_data(): + if "path" not in representation["data"]: + return None + + path = representation["data"]["path"] + # Force replacing backslashes with forward slashed if not on + # windows + if platform.system().lower() != "windows": + path = path.replace("\\", "/") + + if os.path.exists(path): + return os.path.normpath(path) + + dir_path, file_name = os.path.split(path) + if not os.path.exists(dir_path): + return + + base_name, ext = os.path.splitext(file_name) + file_name_items = None + if "#" in base_name: + file_name_items = [part for part in base_name.split("#") if part] + elif "%" in base_name: + file_name_items = base_name.split("%") + + if not file_name_items: + return + + filename_start = file_name_items[0] + + for _file in os.listdir(dir_path): + if _file.startswith(filename_start) and _file.endswith(ext): + return os.path.normpath(path) + + return ( + path_from_represenation() or + path_from_config() or + path_from_data() + ) + + +def is_compatible_loader(Loader, context): + """Return whether a loader is compatible with a context. + + This checks the version's families and the representation for the given + Loader. + + Returns: + bool + + """ + maj_version, _ = schema.get_schema_version(context["subset"]["schema"]) + if maj_version < 3: + families = context["version"]["data"].get("families", []) + else: + families = context["subset"]["data"]["families"] + + representation = context["representation"] + has_family = ( + "*" in Loader.families or any( + family in Loader.families for family in families + ) + ) + representations = Loader.get_representations() + has_representation = ( + "*" in representations or representation["name"] in representations + ) + return has_family and has_representation + + +def loaders_from_repre_context(loaders, repre_context): + """Return compatible loaders for by representaiton's context.""" + + return [ + loader + for loader in loaders + if is_compatible_loader(loader, repre_context) + ] + + +def loaders_from_representation(loaders, representation): + """Return all compatible loaders for a representation.""" + + context = get_representation_context(representation) + return loaders_from_repre_context(loaders, context) From 6fe1b5b96554266b1e426fb61016ec5891b1563b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 10:58:43 +0100 Subject: [PATCH 204/302] porpagate some load function in openpype.pipeline --- openpype/pipeline/__init__.py | 45 ++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index 7147e56dd2..d582ef1d07 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -12,6 +12,27 @@ from .create import ( legacy_create, ) +from .load import ( + HeroVersionType, + IncompatibleLoaderError, + LoaderPlugin, + SubsetLoaderPlugin, + + discover_loader_plugins, + register_loader_plugin, + deregister_loader_plugins_path, + register_loader_plugins_path, + deregister_loader_plugin, + + load_representation, + remove_container, + update_container, + switch_container, + + loaders_from_representation, + get_representation_path, +) + from .publish import ( PublishValidationError, PublishXmlValidationError, @@ -23,6 +44,7 @@ from .publish import ( __all__ = ( "attribute_definitions", + # --- Create --- "BaseCreator", "Creator", "AutoCreator", @@ -30,10 +52,31 @@ __all__ = ( "CreatorError", - # Legacy creation + # - legacy creation "LegacyCreator", "legacy_create", + # --- Load --- + "HeroVersionType", + "IncompatibleLoaderError", + "LoaderPlugin", + "SubsetLoaderPlugin", + + "discover_loader_plugins", + "register_loader_plugin", + "deregister_loader_plugins_path", + "register_loader_plugins_path", + "deregister_loader_plugin", + + "load_representation", + "remove_container", + "update_container", + "switch_container", + + "loaders_from_representation", + "get_representation_path", + + # --- Publish --- "PublishValidationError", "PublishXmlValidationError", "KnownPublishError", From 25c7e56613c78a69e22afe49b1c4fd6ea18adabc Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 11:16:02 +0100 Subject: [PATCH 205/302] move import of LegacyCreator to function --- openpype/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 7d046e4ef4..7720c9dfb8 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -5,7 +5,6 @@ import platform import functools import logging -from openpype.pipeline import LegacyCreator from .settings import get_project_settings from .lib import ( Anatomy, @@ -76,6 +75,7 @@ def install(): """Install Pype to Avalon.""" from pyblish.lib import MessageHandler from openpype.modules import load_modules + from openpype.pipeline import LegacyCreator from avalon import pipeline # Make sure modules are loaded From c5ac2290f69566b6b4a52ede5551056270e373e6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 11:36:17 +0100 Subject: [PATCH 206/302] use moved functions in hosts and tools --- openpype/__init__.py | 13 +++-- openpype/hosts/aftereffects/api/pipeline.py | 10 ++-- openpype/hosts/aftereffects/api/plugin.py | 5 +- .../plugins/load/load_background.py | 5 +- .../aftereffects/plugins/load/load_file.py | 4 +- openpype/hosts/blender/api/pipeline.py | 10 ++-- openpype/hosts/blender/api/plugin.py | 10 ++-- .../hosts/blender/plugins/load/load_abc.py | 4 +- .../hosts/blender/plugins/load/load_action.py | 35 ++++++------- .../hosts/blender/plugins/load/load_audio.py | 4 +- .../blender/plugins/load/load_camera_blend.py | 4 +- .../blender/plugins/load/load_camera_fbx.py | 4 +- .../hosts/blender/plugins/load/load_fbx.py | 4 +- .../blender/plugins/load/load_layout_blend.py | 8 +-- .../blender/plugins/load/load_layout_json.py | 18 ++++--- .../hosts/blender/plugins/load/load_look.py | 4 +- .../hosts/blender/plugins/load/load_model.py | 4 +- .../hosts/blender/plugins/load/load_rig.py | 16 +++--- openpype/hosts/flame/api/pipeline.py | 10 ++-- openpype/hosts/flame/api/plugin.py | 8 +-- .../hosts/flame/plugins/load/load_clip.py | 2 +- openpype/hosts/fusion/api/lib.py | 4 +- openpype/hosts/fusion/api/pipeline.py | 10 ++-- openpype/hosts/fusion/plugins/load/actions.py | 6 +-- .../fusion/plugins/load/load_sequence.py | 10 ++-- openpype/hosts/harmony/api/README.md | 2 +- openpype/hosts/harmony/api/pipeline.py | 10 ++-- .../hosts/harmony/plugins/load/load_audio.py | 9 ++-- .../harmony/plugins/load/load_background.py | 11 ++-- .../plugins/load/load_imagesequence.py | 9 ++-- .../harmony/plugins/load/load_palette.py | 9 ++-- .../harmony/plugins/load/load_template.py | 9 ++-- .../plugins/load/load_template_workfile.py | 9 ++-- openpype/hosts/hiero/api/pipeline.py | 10 ++-- openpype/hosts/hiero/api/plugin.py | 6 +-- .../hosts/hiero/plugins/load/load_clip.py | 5 +- openpype/hosts/houdini/api/pipeline.py | 7 ++- .../hosts/houdini/plugins/load/actions.py | 6 +-- .../houdini/plugins/load/load_alembic.py | 10 ++-- .../hosts/houdini/plugins/load/load_ass.py | 12 +++-- .../hosts/houdini/plugins/load/load_camera.py | 9 ++-- .../hosts/houdini/plugins/load/load_hda.py | 12 +++-- .../hosts/houdini/plugins/load/load_image.py | 9 ++-- .../houdini/plugins/load/load_usd_layer.py | 9 ++-- .../plugins/load/load_usd_reference.py | 9 ++-- .../hosts/houdini/plugins/load/load_vdb.py | 9 ++-- .../houdini/plugins/load/show_usdview.py | 4 +- .../plugins/publish/extract_usd_layered.py | 3 +- openpype/hosts/maya/api/lib.py | 16 ++++-- openpype/hosts/maya/api/pipeline.py | 10 ++-- openpype/hosts/maya/api/plugin.py | 11 ++-- openpype/hosts/maya/api/setdress.py | 46 ++++++++++------- .../plugins/inventory/import_modelrender.py | 14 +++-- openpype/hosts/maya/plugins/load/actions.py | 8 +-- openpype/hosts/maya/plugins/load/load_ass.py | 11 ++-- .../hosts/maya/plugins/load/load_assembly.py | 13 ++--- .../hosts/maya/plugins/load/load_audio.py | 10 ++-- .../hosts/maya/plugins/load/load_gpucache.py | 10 ++-- .../maya/plugins/load/load_image_plane.py | 10 ++-- openpype/hosts/maya/plugins/load/load_look.py | 5 +- .../hosts/maya/plugins/load/load_matchmove.py | 4 +- .../maya/plugins/load/load_redshift_proxy.py | 9 ++-- .../maya/plugins/load/load_rendersetup.py | 11 ++-- .../maya/plugins/load/load_vdb_to_redshift.py | 5 +- .../maya/plugins/load/load_vdb_to_vray.py | 10 ++-- .../hosts/maya/plugins/load/load_vrayproxy.py | 15 ++++-- .../hosts/maya/plugins/load/load_vrayscene.py | 9 ++-- .../maya/plugins/load/load_yeti_cache.py | 12 +++-- openpype/hosts/nuke/api/pipeline.py | 10 ++-- openpype/hosts/nuke/api/plugin.py | 9 ++-- openpype/hosts/nuke/plugins/load/actions.py | 6 +-- .../hosts/nuke/plugins/load/load_backdrop.py | 10 ++-- .../nuke/plugins/load/load_camera_abc.py | 10 ++-- openpype/hosts/nuke/plugins/load/load_clip.py | 5 +- .../hosts/nuke/plugins/load/load_effects.py | 10 ++-- .../nuke/plugins/load/load_effects_ip.py | 10 ++-- .../hosts/nuke/plugins/load/load_gizmo.py | 10 ++-- .../hosts/nuke/plugins/load/load_gizmo_ip.py | 10 ++-- .../hosts/nuke/plugins/load/load_image.py | 10 ++-- .../hosts/nuke/plugins/load/load_matchmove.py | 4 +- .../hosts/nuke/plugins/load/load_model.py | 10 ++-- .../nuke/plugins/load/load_script_precomp.py | 10 ++-- .../nuke/plugins/publish/precollect_writes.py | 5 +- .../plugins/publish/validate_read_legacy.py | 12 +++-- openpype/hosts/photoshop/api/README.md | 7 +-- openpype/hosts/photoshop/api/pipeline.py | 10 ++-- openpype/hosts/photoshop/api/plugin.py | 4 +- .../photoshop/plugins/load/load_image.py | 4 +- .../plugins/load/load_image_from_sequence.py | 2 +- .../photoshop/plugins/load/load_reference.py | 5 +- openpype/hosts/resolve/api/pipeline.py | 10 ++-- openpype/hosts/resolve/api/plugin.py | 11 ++-- .../hosts/resolve/plugins/load/load_clip.py | 9 ++-- openpype/hosts/tvpaint/api/pipeline.py | 10 ++-- openpype/hosts/tvpaint/api/plugin.py | 9 ++-- openpype/hosts/unreal/api/pipeline.py | 10 ++-- openpype/hosts/unreal/api/plugin.py | 8 +-- .../load/load_alembic_geometrycache.py | 5 +- .../plugins/load/load_alembic_skeletalmesh.py | 5 +- .../plugins/load/load_alembic_staticmesh.py | 5 +- .../unreal/plugins/load/load_animation.py | 5 +- .../hosts/unreal/plugins/load/load_layout.py | 20 +++++--- .../hosts/unreal/plugins/load/load_rig.py | 5 +- .../unreal/plugins/load/load_staticmeshfbx.py | 5 +- openpype/hosts/webpublisher/api/__init__.py | 7 --- openpype/lib/avalon_context.py | 13 +++-- openpype/lib/path_templates.py | 10 ++++ openpype/lib/plugin_tools.py | 7 +-- .../plugins/publish/submit_publish_job.py | 4 +- .../ftrack/event_handlers_user/action_rv.py | 5 +- openpype/pipeline/create/legacy_create.py | 1 + openpype/plugins/load/add_site.py | 4 +- openpype/plugins/load/copy_file.py | 6 ++- openpype/plugins/load/copy_file_path.py | 4 +- openpype/plugins/load/delete_old_versions.py | 19 +++---- openpype/plugins/load/delivery.py | 4 +- openpype/plugins/load/open_djv.py | 5 +- openpype/plugins/load/open_file.py | 4 +- openpype/plugins/load/remove_site.py | 4 +- openpype/tools/loader/model.py | 2 +- openpype/tools/loader/widgets.py | 51 +++++++++++-------- openpype/tools/mayalookassigner/commands.py | 7 +-- .../tools/mayalookassigner/vray_proxies.py | 14 +++-- openpype/tools/sceneinventory/model.py | 5 +- .../tools/sceneinventory/switch_dialog.py | 13 +++-- openpype/tools/sceneinventory/view.py | 18 ++++--- openpype/tools/utils/delegates.py | 2 +- 127 files changed, 711 insertions(+), 427 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 942112835b..755036168d 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -5,7 +5,12 @@ import platform import functools import logging -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) + from .settings import get_project_settings from .lib import ( Anatomy, @@ -90,7 +95,7 @@ def install(): log.info("Registering global plug-ins..") pyblish.register_plugin_path(PUBLISH_PATH) pyblish.register_discovery_filter(filter_pyblish_plugins) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) project_name = os.environ.get("AVALON_PROJECT") @@ -118,7 +123,7 @@ def install(): continue pyblish.register_plugin_path(path) - avalon.register_plugin_path(avalon.Loader, path) + register_loader_plugins_path(path) avalon.register_plugin_path(LegacyCreator, path) avalon.register_plugin_path(avalon.InventoryAction, path) @@ -141,7 +146,7 @@ def uninstall(): log.info("Deregistering global plug-ins..") pyblish.deregister_plugin_path(PUBLISH_PATH) pyblish.deregister_discovery_filter(filter_pyblish_plugins) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) log.info("Global plug-ins unregistred") # restore original discover diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index ef56e96155..2dc41bd8b9 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -9,7 +9,11 @@ from avalon import io, pipeline from openpype import lib from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) import openpype.hosts.aftereffects from .launch_logic import get_stub @@ -66,7 +70,7 @@ def install(): pyblish.api.register_host("aftereffects") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -79,7 +83,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/aftereffects/api/plugin.py b/openpype/hosts/aftereffects/api/plugin.py index fbe07663dd..29705cc5be 100644 --- a/openpype/hosts/aftereffects/api/plugin.py +++ b/openpype/hosts/aftereffects/api/plugin.py @@ -1,9 +1,8 @@ -import avalon.api +from openpype.pipeline import LoaderPlugin from .launch_logic import get_stub -class AfterEffectsLoader(avalon.api.Loader): +class AfterEffectsLoader(LoaderPlugin): @staticmethod def get_stub(): return get_stub() - diff --git a/openpype/hosts/aftereffects/plugins/load/load_background.py b/openpype/hosts/aftereffects/plugins/load/load_background.py index 1a2d6fc432..be43cae44e 100644 --- a/openpype/hosts/aftereffects/plugins/load/load_background.py +++ b/openpype/hosts/aftereffects/plugins/load/load_background.py @@ -1,11 +1,10 @@ import re -import avalon.api - from openpype.lib import ( get_background_layers, get_unique_layer_name ) +from openpype.pipeline import get_representation_path from openpype.hosts.aftereffects.api import ( AfterEffectsLoader, containerise @@ -78,7 +77,7 @@ class BackgroundLoader(AfterEffectsLoader): else: # switching version - keep same name comp_name = container["namespace"] - path = avalon.api.get_representation_path(representation) + path = get_representation_path(representation) layers = get_background_layers(path) comp = stub.reload_background(container["members"][1], diff --git a/openpype/hosts/aftereffects/plugins/load/load_file.py b/openpype/hosts/aftereffects/plugins/load/load_file.py index 9dbbf7aae1..9eb9e80a2c 100644 --- a/openpype/hosts/aftereffects/plugins/load/load_file.py +++ b/openpype/hosts/aftereffects/plugins/load/load_file.py @@ -1,8 +1,8 @@ import re -import avalon.api from openpype import lib +from openpype.pipeline import get_representation_path from openpype.hosts.aftereffects.api import ( AfterEffectsLoader, containerise @@ -92,7 +92,7 @@ class FileLoader(AfterEffectsLoader): "{}_{}".format(context["asset"], context["subset"])) else: # switching version - keep same name layer_name = container["namespace"] - path = avalon.api.get_representation_path(representation) + path = get_representation_path(representation) # with aftereffects.maintained_selection(): # TODO stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name) stub.imprint( diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 1c9820ff22..f3a5c941eb 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -14,7 +14,11 @@ import avalon.api from avalon import io, schema from avalon.pipeline import AVALON_CONTAINER_ID -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from openpype.api import Logger import openpype.hosts.blender @@ -46,7 +50,7 @@ def install(): pyblish.api.register_host("blender") pyblish.api.register_plugin_path(str(PUBLISH_PATH)) - avalon.api.register_plugin_path(avalon.api.Loader, str(LOAD_PATH)) + register_loader_plugins_path(str(LOAD_PATH)) avalon.api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) lib.append_user_scripts() @@ -67,7 +71,7 @@ def uninstall(): pyblish.api.deregister_host("blender") pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) - avalon.api.deregister_plugin_path(avalon.api.Loader, str(LOAD_PATH)) + deregister_loader_plugins_path(str(LOAD_PATH)) avalon.api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) if not IS_HEADLESS: diff --git a/openpype/hosts/blender/api/plugin.py b/openpype/hosts/blender/api/plugin.py index 20d1e4c8db..3207f543b7 100644 --- a/openpype/hosts/blender/api/plugin.py +++ b/openpype/hosts/blender/api/plugin.py @@ -5,8 +5,10 @@ from typing import Dict, List, Optional import bpy -import avalon.api -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) from .pipeline import AVALON_CONTAINERS from .ops import ( MainThreadItem, @@ -145,13 +147,13 @@ class Creator(LegacyCreator): return collection -class Loader(avalon.api.Loader): +class Loader(LoaderPlugin): """Base class for Loader plug-ins.""" hosts = ["blender"] -class AssetLoader(avalon.api.Loader): +class AssetLoader(LoaderPlugin): """A basic AssetLoader for Blender This will implement the basic logic for linking/appending assets diff --git a/openpype/hosts/blender/plugins/load/load_abc.py b/openpype/hosts/blender/plugins/load/load_abc.py index 07800521c9..3daaeceffe 100644 --- a/openpype/hosts/blender/plugins/load/load_abc.py +++ b/openpype/hosts/blender/plugins/load/load_abc.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, AVALON_PROPERTY, @@ -178,7 +178,7 @@ class CacheModelLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_action.py b/openpype/hosts/blender/plugins/load/load_action.py index a9d8522220..3c8fe988f0 100644 --- a/openpype/hosts/blender/plugins/load/load_action.py +++ b/openpype/hosts/blender/plugins/load/load_action.py @@ -5,9 +5,13 @@ from pathlib import Path from pprint import pformat from typing import Dict, List, Optional -from avalon import api, blender import bpy +from openpype.pipeline import get_representation_path import openpype.hosts.blender.api.plugin +from openpype.hosts.blender.api.pipeline import ( + containerise_existing, + AVALON_PROPERTY, +) logger = logging.getLogger("openpype").getChild("blender").getChild("load_action") @@ -49,7 +53,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): container = bpy.data.collections.new(lib_container) container.name = container_name - blender.pipeline.containerise_existing( + containerise_existing( container, name, namespace, @@ -57,8 +61,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): self.__class__.__name__, ) - container_metadata = container.get( - blender.pipeline.AVALON_PROPERTY) + container_metadata = container.get(AVALON_PROPERTY) container_metadata["libpath"] = libpath container_metadata["lib_container"] = lib_container @@ -90,16 +93,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): anim_data.action.make_local() - if not obj.get(blender.pipeline.AVALON_PROPERTY): + if not obj.get(AVALON_PROPERTY): - obj[blender.pipeline.AVALON_PROPERTY] = dict() + obj[AVALON_PROPERTY] = dict() - avalon_info = obj[blender.pipeline.AVALON_PROPERTY] + avalon_info = obj[AVALON_PROPERTY] avalon_info.update({"container_name": container_name}) objects_list.append(obj) - animation_container.pop(blender.pipeline.AVALON_PROPERTY) + animation_container.pop(AVALON_PROPERTY) # Save the list of objects in the metadata container container_metadata["objects"] = objects_list @@ -128,7 +131,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): container["objectName"] ) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() logger.info( @@ -153,8 +156,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): f"Unsupported file: {libpath}" ) - collection_metadata = collection.get( - blender.pipeline.AVALON_PROPERTY) + collection_metadata = collection.get(AVALON_PROPERTY) collection_libpath = collection_metadata["libpath"] normalized_collection_libpath = ( @@ -225,16 +227,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): strip.action = anim_data.action strip.action_frame_end = anim_data.action.frame_range[1] - if not obj.get(blender.pipeline.AVALON_PROPERTY): + if not obj.get(AVALON_PROPERTY): - obj[blender.pipeline.AVALON_PROPERTY] = dict() + obj[AVALON_PROPERTY] = dict() - avalon_info = obj[blender.pipeline.AVALON_PROPERTY] + avalon_info = obj[AVALON_PROPERTY] avalon_info.update({"container_name": collection.name}) objects_list.append(obj) - anim_container.pop(blender.pipeline.AVALON_PROPERTY) + anim_container.pop(AVALON_PROPERTY) # Save the list of objects in the metadata container collection_metadata["objects"] = objects_list @@ -266,8 +268,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader): "Nested collections are not supported." ) - collection_metadata = collection.get( - blender.pipeline.AVALON_PROPERTY) + collection_metadata = collection.get(AVALON_PROPERTY) objects = collection_metadata["objects"] lib_container = collection_metadata["lib_container"] diff --git a/openpype/hosts/blender/plugins/load/load_audio.py b/openpype/hosts/blender/plugins/load/load_audio.py index e065150c15..b95c5db270 100644 --- a/openpype/hosts/blender/plugins/load/load_audio.py +++ b/openpype/hosts/blender/plugins/load/load_audio.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -102,7 +102,7 @@ class AudioLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) self.log.info( "Container: %s\nRepresentation: %s", diff --git a/openpype/hosts/blender/plugins/load/load_camera_blend.py b/openpype/hosts/blender/plugins/load/load_camera_blend.py index 61955f124d..6ed2e8a575 100644 --- a/openpype/hosts/blender/plugins/load/load_camera_blend.py +++ b/openpype/hosts/blender/plugins/load/load_camera_blend.py @@ -7,7 +7,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -155,7 +155,7 @@ class BlendCameraLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_camera_fbx.py b/openpype/hosts/blender/plugins/load/load_camera_fbx.py index 175ddacf9f..626ed44f08 100644 --- a/openpype/hosts/blender/plugins/load/load_camera_fbx.py +++ b/openpype/hosts/blender/plugins/load/load_camera_fbx.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin, lib from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -143,7 +143,7 @@ class FbxCameraLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_fbx.py b/openpype/hosts/blender/plugins/load/load_fbx.py index c6e6af5592..2d249ef647 100644 --- a/openpype/hosts/blender/plugins/load/load_fbx.py +++ b/openpype/hosts/blender/plugins/load/load_fbx.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin, lib from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -187,7 +187,7 @@ class FbxModelLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_layout_blend.py b/openpype/hosts/blender/plugins/load/load_layout_blend.py index 7f8ae610c6..d87df3c010 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_blend.py +++ b/openpype/hosts/blender/plugins/load/load_layout_blend.py @@ -6,9 +6,11 @@ from typing import Dict, List, Optional import bpy -from avalon import api from openpype import lib -from openpype.pipeline import legacy_create +from openpype.pipeline import ( + legacy_create, + get_representation_path, +) from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -309,7 +311,7 @@ class BlendLayoutLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_layout_json.py b/openpype/hosts/blender/plugins/load/load_layout_json.py index 5b5f9ab83d..499d2c49f3 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_json.py +++ b/openpype/hosts/blender/plugins/load/load_layout_json.py @@ -7,7 +7,13 @@ from typing import Dict, Optional import bpy -from avalon import api +from openpype.pipeline import ( + discover_loader_plugins, + remove_container, + load_representation, + get_representation_path, + loaders_from_representation, +) from openpype.hosts.blender.api.pipeline import ( AVALON_INSTANCES, AVALON_CONTAINERS, @@ -33,7 +39,7 @@ class JsonLayoutLoader(plugin.AssetLoader): objects = list(asset_group.children) for obj in objects: - api.remove(obj.get(AVALON_PROPERTY)) + remove_container(obj.get(AVALON_PROPERTY)) def _remove_animation_instances(self, asset_group): instances = bpy.data.collections.get(AVALON_INSTANCES) @@ -66,13 +72,13 @@ class JsonLayoutLoader(plugin.AssetLoader): with open(libpath, "r") as fp: data = json.load(fp) - all_loaders = api.discover(api.Loader) + all_loaders = discover_loader_plugins() for element in data: reference = element.get('reference') family = element.get('family') - loaders = api.loaders_from_representation(all_loaders, reference) + loaders = loaders_from_representation(all_loaders, reference) loader = self._get_loader(loaders, family) if not loader: @@ -102,7 +108,7 @@ class JsonLayoutLoader(plugin.AssetLoader): # at this time it will not return anything. The assets will be # loaded in the next Blender cycle, so we use the options to # set the transform, parent and assign the action, if there is one. - api.load( + load_representation( loader, reference, namespace=instance_name, @@ -188,7 +194,7 @@ class JsonLayoutLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_look.py b/openpype/hosts/blender/plugins/load/load_look.py index 066ec0101b..70d1b95f02 100644 --- a/openpype/hosts/blender/plugins/load/load_look.py +++ b/openpype/hosts/blender/plugins/load/load_look.py @@ -8,7 +8,7 @@ import os import json import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( containerise_existing, @@ -140,7 +140,7 @@ class BlendLookLoader(plugin.AssetLoader): def update(self, container: Dict, representation: Dict): collection = bpy.data.collections.get(container["objectName"]) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_model.py b/openpype/hosts/blender/plugins/load/load_model.py index 04ece0b338..18d01dcb29 100644 --- a/openpype/hosts/blender/plugins/load/load_model.py +++ b/openpype/hosts/blender/plugins/load/load_model.py @@ -6,7 +6,7 @@ from typing import Dict, List, Optional import bpy -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.blender.api import plugin from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, @@ -195,7 +195,7 @@ class BlendModelLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/blender/plugins/load/load_rig.py b/openpype/hosts/blender/plugins/load/load_rig.py index eacabd3447..cec088076c 100644 --- a/openpype/hosts/blender/plugins/load/load_rig.py +++ b/openpype/hosts/blender/plugins/load/load_rig.py @@ -6,11 +6,15 @@ from typing import Dict, List, Optional import bpy -from avalon import api -from avalon.blender import lib as avalon_lib from openpype import lib -from openpype.pipeline import legacy_create -from openpype.hosts.blender.api import plugin +from openpype.pipeline import ( + legacy_create, + get_representation_path, +) +from openpype.hosts.blender.api import ( + plugin, + get_selection, +) from openpype.hosts.blender.api.pipeline import ( AVALON_CONTAINERS, AVALON_PROPERTY, @@ -263,7 +267,7 @@ class BlendRigLoader(plugin.AssetLoader): if anim_file: bpy.ops.import_scene.fbx(filepath=anim_file, anim_offset=0.0) - imported = avalon_lib.get_selection() + imported = get_selection() armature = [ o for o in asset_group.children if o.type == 'ARMATURE'][0] @@ -307,7 +311,7 @@ class BlendRigLoader(plugin.AssetLoader): """ object_name = container["objectName"] asset_group = bpy.data.objects.get(object_name) - libpath = Path(api.get_representation_path(representation)) + libpath = Path(get_representation_path(representation)) extension = libpath.suffix.lower() self.log.info( diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index f802cf160b..6a045214c3 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -7,7 +7,11 @@ from avalon import api as avalon from avalon.pipeline import AVALON_CONTAINER_ID from pyblish import api as pyblish from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from .lib import ( set_segment_data_marker, set_publish_attribute, @@ -33,7 +37,7 @@ def install(): pyblish.register_host("flame") pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) log.info("OpenPype Flame plug-ins registred ...") @@ -48,7 +52,7 @@ def uninstall(): log.info("Deregistering Flame plug-ins..") pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) diff --git a/openpype/hosts/flame/api/plugin.py b/openpype/hosts/flame/api/plugin.py index 5221701a2f..4c9d3c5383 100644 --- a/openpype/hosts/flame/api/plugin.py +++ b/openpype/hosts/flame/api/plugin.py @@ -7,9 +7,11 @@ import six import qargparse from Qt import QtWidgets, QtCore import openpype.api as openpype -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) from openpype import style -import avalon.api as avalon from . import ( lib as flib, pipeline as fpipeline, @@ -660,7 +662,7 @@ class PublishableClip: # Publishing plugin functions # Loader plugin functions -class ClipLoader(avalon.Loader): +class ClipLoader(LoaderPlugin): """A basic clip loader for Flame This will implement the basic behavior for a loader to inherit from that diff --git a/openpype/hosts/flame/plugins/load/load_clip.py b/openpype/hosts/flame/plugins/load/load_clip.py index 8ba01d6937..8980f72cb8 100644 --- a/openpype/hosts/flame/plugins/load/load_clip.py +++ b/openpype/hosts/flame/plugins/load/load_clip.py @@ -172,7 +172,7 @@ class LoadClip(opfapi.ClipLoader): # version_name = version.get("name", None) # colorspace = version_data.get("colorspace", None) # object_name = "{}_{}".format(name, namespace) - # file = api.get_representation_path(representation).replace("\\", "/") + # file = get_representation_path(representation).replace("\\", "/") # clip = track_item.source() # # reconnect media to new path diff --git a/openpype/hosts/fusion/api/lib.py b/openpype/hosts/fusion/api/lib.py index 5d97f83032..2bb5ea8aae 100644 --- a/openpype/hosts/fusion/api/lib.py +++ b/openpype/hosts/fusion/api/lib.py @@ -5,8 +5,8 @@ import contextlib from Qt import QtGui -import avalon.api from avalon import io +from openpype.pipeline import switch_container from .pipeline import get_current_comp, comp_lock_and_undo_chunk self = sys.modules[__name__] @@ -142,7 +142,7 @@ def switch_item(container, assert representation, ("Could not find representation in the database " "with the name '%s'" % representation_name) - avalon.api.switch(container, representation) + switch_container(container, representation) return representation diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 5ac56fcbed..3f5da7fcc7 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -11,7 +11,11 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) import openpype.hosts.fusion log = Logger().get_logger(__name__) @@ -63,7 +67,7 @@ def install(): pyblish.api.register_plugin_path(PUBLISH_PATH) log.info("Registering Fusion plug-ins..") - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) @@ -87,7 +91,7 @@ def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) log.info("Deregistering Fusion plug-ins..") - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH diff --git a/openpype/hosts/fusion/plugins/load/actions.py b/openpype/hosts/fusion/plugins/load/actions.py index 6af99e4c56..bc59cec77f 100644 --- a/openpype/hosts/fusion/plugins/load/actions.py +++ b/openpype/hosts/fusion/plugins/load/actions.py @@ -2,10 +2,10 @@ """ -from avalon import api +from openpype.pipeline import load -class FusionSetFrameRangeLoader(api.Loader): +class FusionSetFrameRangeLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", @@ -39,7 +39,7 @@ class FusionSetFrameRangeLoader(api.Loader): lib.update_frame_range(start, end) -class FusionSetFrameRangeWithHandlesLoader(api.Loader): +class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", diff --git a/openpype/hosts/fusion/plugins/load/load_sequence.py b/openpype/hosts/fusion/plugins/load/load_sequence.py index ea118585bf..075820de35 100644 --- a/openpype/hosts/fusion/plugins/load/load_sequence.py +++ b/openpype/hosts/fusion/plugins/load/load_sequence.py @@ -1,8 +1,12 @@ import os import contextlib -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.fusion.api import ( imprint_container, get_current_comp, @@ -117,7 +121,7 @@ def loader_shift(loader, frame, relative=True): return int(shift) -class FusionLoadSequence(api.Loader): +class FusionLoadSequence(load.LoaderPlugin): """Load image sequence into Fusion""" families = ["imagesequence", "review", "render"] @@ -204,7 +208,7 @@ class FusionLoadSequence(api.Loader): assert tool.ID == "Loader", "Must be Loader" comp = tool.Comp() - root = os.path.dirname(api.get_representation_path(representation)) + root = os.path.dirname(get_representation_path(representation)) path = self._get_first_image(root) # Get start frame from version data diff --git a/openpype/hosts/harmony/api/README.md b/openpype/hosts/harmony/api/README.md index a8d182736a..e8d354e1e6 100644 --- a/openpype/hosts/harmony/api/README.md +++ b/openpype/hosts/harmony/api/README.md @@ -575,7 +575,7 @@ replace_files = """function %s_replace_files(args) """ % (signature, signature) -class ImageSequenceLoader(api.Loader): +class ImageSequenceLoader(load.LoaderPlugin): """Load images Stores the imported asset in a container named after the asset. """ diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index 6d0f5e9416..be183902a7 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -9,7 +9,11 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from openpype import lib -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) import openpype.hosts.harmony import openpype.hosts.harmony.api as harmony @@ -179,7 +183,7 @@ def install(): pyblish.api.register_host("harmony") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -193,7 +197,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/harmony/plugins/load/load_audio.py b/openpype/hosts/harmony/plugins/load/load_audio.py index 57ea8ae312..e18a6de097 100644 --- a/openpype/hosts/harmony/plugins/load/load_audio.py +++ b/openpype/hosts/harmony/plugins/load/load_audio.py @@ -1,4 +1,7 @@ -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony sig = harmony.signature() @@ -29,7 +32,7 @@ function %s(args) """ % (sig, sig) -class ImportAudioLoader(api.Loader): +class ImportAudioLoader(load.LoaderPlugin): """Import audio.""" families = ["shot", "audio"] @@ -37,7 +40,7 @@ class ImportAudioLoader(api.Loader): label = "Import Audio" def load(self, context, name=None, namespace=None, data=None): - wav_file = api.get_representation_path(context["representation"]) + wav_file = get_representation_path(context["representation"]) harmony.send( {"function": func, "args": [context["subset"]["name"], wav_file]} ) diff --git a/openpype/hosts/harmony/plugins/load/load_background.py b/openpype/hosts/harmony/plugins/load/load_background.py index 686d6b5b7b..9c01fe3cd8 100644 --- a/openpype/hosts/harmony/plugins/load/load_background.py +++ b/openpype/hosts/harmony/plugins/load/load_background.py @@ -1,7 +1,10 @@ import os import json -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony import openpype.lib @@ -226,7 +229,7 @@ replace_files """ -class BackgroundLoader(api.Loader): +class BackgroundLoader(load.LoaderPlugin): """Load images Stores the imported asset in a container named after the asset. """ @@ -278,7 +281,7 @@ class BackgroundLoader(api.Loader): def update(self, container, representation): - path = api.get_representation_path(representation) + path = get_representation_path(representation) with open(path) as json_file: data = json.load(json_file) @@ -297,7 +300,7 @@ class BackgroundLoader(api.Loader): bg_folder = os.path.dirname(path) - path = api.get_representation_path(representation) + path = get_representation_path(representation) print(container) diff --git a/openpype/hosts/harmony/plugins/load/load_imagesequence.py b/openpype/hosts/harmony/plugins/load/load_imagesequence.py index 310f9bdb61..18695438d5 100644 --- a/openpype/hosts/harmony/plugins/load/load_imagesequence.py +++ b/openpype/hosts/harmony/plugins/load/load_imagesequence.py @@ -6,12 +6,15 @@ from pathlib import Path import clique -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony import openpype.lib -class ImageSequenceLoader(api.Loader): +class ImageSequenceLoader(load.LoaderPlugin): """Load image sequences. Stores the imported asset in a container named after the asset. @@ -79,7 +82,7 @@ class ImageSequenceLoader(api.Loader): self_name = self.__class__.__name__ node = container.get("nodes").pop() - path = api.get_representation_path(representation) + path = get_representation_path(representation) collections, remainder = clique.assemble( os.listdir(os.path.dirname(path)) ) diff --git a/openpype/hosts/harmony/plugins/load/load_palette.py b/openpype/hosts/harmony/plugins/load/load_palette.py index 2e0f70d135..1da3e61e1b 100644 --- a/openpype/hosts/harmony/plugins/load/load_palette.py +++ b/openpype/hosts/harmony/plugins/load/load_palette.py @@ -1,11 +1,14 @@ import os import shutil -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony -class ImportPaletteLoader(api.Loader): +class ImportPaletteLoader(load.LoaderPlugin): """Import palettes.""" families = ["palette", "harmony.palette"] @@ -31,7 +34,7 @@ class ImportPaletteLoader(api.Loader): scene_path = harmony.send( {"function": "scene.currentProjectPath"} )["result"] - src = api.get_representation_path(representation) + src = get_representation_path(representation) dst = os.path.join( scene_path, "palette-library", diff --git a/openpype/hosts/harmony/plugins/load/load_template.py b/openpype/hosts/harmony/plugins/load/load_template.py index 112e613ae6..c6dc9d913b 100644 --- a/openpype/hosts/harmony/plugins/load/load_template.py +++ b/openpype/hosts/harmony/plugins/load/load_template.py @@ -6,12 +6,15 @@ import os import shutil import uuid -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony import openpype.lib -class TemplateLoader(api.Loader): +class TemplateLoader(load.LoaderPlugin): """Load Harmony template as container. .. todo:: @@ -38,7 +41,7 @@ class TemplateLoader(api.Loader): # Load template. self_name = self.__class__.__name__ temp_dir = tempfile.mkdtemp() - zip_file = api.get_representation_path(context["representation"]) + zip_file = get_representation_path(context["representation"]) template_path = os.path.join(temp_dir, "temp.tpl") with zipfile.ZipFile(zip_file, "r") as zip_ref: zip_ref.extractall(template_path) diff --git a/openpype/hosts/harmony/plugins/load/load_template_workfile.py b/openpype/hosts/harmony/plugins/load/load_template_workfile.py index c21b8194b1..2b84a43b35 100644 --- a/openpype/hosts/harmony/plugins/load/load_template_workfile.py +++ b/openpype/hosts/harmony/plugins/load/load_template_workfile.py @@ -3,11 +3,14 @@ import zipfile import os import shutil -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) import openpype.hosts.harmony.api as harmony -class ImportTemplateLoader(api.Loader): +class ImportTemplateLoader(load.LoaderPlugin): """Import templates.""" families = ["harmony.template", "workfile"] @@ -17,7 +20,7 @@ class ImportTemplateLoader(api.Loader): def load(self, context, name=None, namespace=None, data=None): # Import template. temp_dir = tempfile.mkdtemp() - zip_file = api.get_representation_path(context["representation"]) + zip_file = get_representation_path(context["representation"]) template_path = os.path.join(temp_dir, "temp.tpl") with zipfile.ZipFile(zip_file, "r") as zip_ref: zip_ref.extractall(template_path) diff --git a/openpype/hosts/hiero/api/pipeline.py b/openpype/hosts/hiero/api/pipeline.py index 5cb23ea355..f27b7a4f81 100644 --- a/openpype/hosts/hiero/api/pipeline.py +++ b/openpype/hosts/hiero/api/pipeline.py @@ -9,7 +9,11 @@ from avalon import api as avalon from avalon import schema from pyblish import api as pyblish from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from openpype.tools.utils import host_tools from . import lib, menu, events @@ -45,7 +49,7 @@ def install(): log.info("Registering Hiero plug-ins..") pyblish.register_host("hiero") pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) @@ -67,7 +71,7 @@ def uninstall(): log.info("Deregistering Hiero plug-ins..") pyblish.deregister_host("hiero") pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) # register callback for switching publishable diff --git a/openpype/hosts/hiero/api/plugin.py b/openpype/hosts/hiero/api/plugin.py index 53928aca41..54e66bf99a 100644 --- a/openpype/hosts/hiero/api/plugin.py +++ b/openpype/hosts/hiero/api/plugin.py @@ -6,9 +6,9 @@ import hiero from Qt import QtWidgets, QtCore import qargparse -import avalon.api as avalon + import openpype.api as openpype -from openpype.pipeline import LegacyCreator +from openpype.pipeline import LoaderPlugin, LegacyCreator from . import lib log = openpype.Logger().get_logger(__name__) @@ -306,7 +306,7 @@ def get_reference_node_parents(ref): return parents -class SequenceLoader(avalon.Loader): +class SequenceLoader(LoaderPlugin): """A basic SequenceLoader for Resolve This will implement the basic behavior for a loader to inherit from that diff --git a/openpype/hosts/hiero/plugins/load/load_clip.py b/openpype/hosts/hiero/plugins/load/load_clip.py index b905dd4431..d3908695a2 100644 --- a/openpype/hosts/hiero/plugins/load/load_clip.py +++ b/openpype/hosts/hiero/plugins/load/load_clip.py @@ -1,4 +1,5 @@ -from avalon import io, api +from avalon import io +from openpype.pipeline import get_representation_path import openpype.hosts.hiero.api as phiero # from openpype.hosts.hiero.api import plugin, lib # reload(lib) @@ -112,7 +113,7 @@ class LoadClip(phiero.SequenceLoader): version_name = version.get("name", None) colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") clip = track_item.source() # reconnect media to new path diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 21027dad2e..66c1c84308 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -11,7 +11,10 @@ import avalon.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon.lib import find_submodule -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugin_path, +) import openpype.hosts.houdini from openpype.hosts.houdini.api import lib @@ -48,7 +51,7 @@ def install(): pyblish.api.register_host("hpython") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info("Installing callbacks ... ") diff --git a/openpype/hosts/houdini/plugins/load/actions.py b/openpype/hosts/houdini/plugins/load/actions.py index acdb998c16..63d74c39a5 100644 --- a/openpype/hosts/houdini/plugins/load/actions.py +++ b/openpype/hosts/houdini/plugins/load/actions.py @@ -2,10 +2,10 @@ """ -from avalon import api +from openpype.pipeline import load -class SetFrameRangeLoader(api.Loader): +class SetFrameRangeLoader(load.LoaderPlugin): """Set Houdini frame range""" families = [ @@ -43,7 +43,7 @@ class SetFrameRangeLoader(api.Loader): hou.playbar.setPlaybackRange(start, end) -class SetFrameRangeWithHandlesLoader(api.Loader): +class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): """Set Maya frame range including pre- and post-handles""" families = [ diff --git a/openpype/hosts/houdini/plugins/load/load_alembic.py b/openpype/hosts/houdini/plugins/load/load_alembic.py index eaab81f396..0214229d5a 100644 --- a/openpype/hosts/houdini/plugins/load/load_alembic.py +++ b/openpype/hosts/houdini/plugins/load/load_alembic.py @@ -1,10 +1,12 @@ import os -from avalon import api - +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import pipeline -class AbcLoader(api.Loader): +class AbcLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["model", "animation", "pointcache", "gpuCache"] @@ -90,7 +92,7 @@ class AbcLoader(api.Loader): return # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") alembic_node.setParms({"fileName": file_path}) diff --git a/openpype/hosts/houdini/plugins/load/load_ass.py b/openpype/hosts/houdini/plugins/load/load_ass.py index 8c272044ec..0144bbaefd 100644 --- a/openpype/hosts/houdini/plugins/load/load_ass.py +++ b/openpype/hosts/houdini/plugins/load/load_ass.py @@ -1,11 +1,15 @@ import os -from avalon import api -from avalon.houdini import pipeline import clique +from openpype.pipeline import ( + load, + get_representation_path, +) + +from openpype.hosts.houdini.api import pipeline -class AssLoader(api.Loader): +class AssLoader(load.LoaderPlugin): """Load .ass with Arnold Procedural""" families = ["ass"] @@ -88,7 +92,7 @@ class AssLoader(api.Loader): def update(self, container, representation): # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") procedural = container["node"] diff --git a/openpype/hosts/houdini/plugins/load/load_camera.py b/openpype/hosts/houdini/plugins/load/load_camera.py index 8916d3b9b7..ef57d115da 100644 --- a/openpype/hosts/houdini/plugins/load/load_camera.py +++ b/openpype/hosts/houdini/plugins/load/load_camera.py @@ -1,4 +1,7 @@ -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import pipeline @@ -74,7 +77,7 @@ def transfer_non_default_values(src, dest, ignore=None): dest_parm.setFromParm(parm) -class CameraLoader(api.Loader): +class CameraLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["camera"] @@ -129,7 +132,7 @@ class CameraLoader(api.Loader): node = container["node"] # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") # Update attributes diff --git a/openpype/hosts/houdini/plugins/load/load_hda.py b/openpype/hosts/houdini/plugins/load/load_hda.py index f5f2fb7481..2438570c6e 100644 --- a/openpype/hosts/houdini/plugins/load/load_hda.py +++ b/openpype/hosts/houdini/plugins/load/load_hda.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- -from avalon import api - +import os +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import pipeline -class HdaLoader(api.Loader): +class HdaLoader(load.LoaderPlugin): """Load Houdini Digital Asset file.""" families = ["hda"] @@ -15,7 +18,6 @@ class HdaLoader(api.Loader): color = "orange" def load(self, context, name=None, namespace=None, data=None): - import os import hou # Format file name, Houdini only wants forward slashes @@ -49,7 +51,7 @@ class HdaLoader(api.Loader): import hou hda_node = container["node"] - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") hou.hda.installFile(file_path) defs = hda_node.type().allInstalledDefinitions() diff --git a/openpype/hosts/houdini/plugins/load/load_image.py b/openpype/hosts/houdini/plugins/load/load_image.py index 39f583677b..bd9ea3eee3 100644 --- a/openpype/hosts/houdini/plugins/load/load_image.py +++ b/openpype/hosts/houdini/plugins/load/load_image.py @@ -1,6 +1,9 @@ import os -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import lib, pipeline import hou @@ -37,7 +40,7 @@ def get_image_avalon_container(): return image_container -class ImageLoader(api.Loader): +class ImageLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.imagesequence"] @@ -87,7 +90,7 @@ class ImageLoader(api.Loader): node = container["node"] # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") file_path = self._get_file_sequence(file_path) diff --git a/openpype/hosts/houdini/plugins/load/load_usd_layer.py b/openpype/hosts/houdini/plugins/load/load_usd_layer.py index 0d4378b480..d803e6abfe 100644 --- a/openpype/hosts/houdini/plugins/load/load_usd_layer.py +++ b/openpype/hosts/houdini/plugins/load/load_usd_layer.py @@ -1,8 +1,11 @@ -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import lib, pipeline -class USDSublayerLoader(api.Loader): +class USDSublayerLoader(load.LoaderPlugin): """Sublayer USD file in Solaris""" families = [ @@ -57,7 +60,7 @@ class USDSublayerLoader(api.Loader): node = container["node"] # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") # Update attributes diff --git a/openpype/hosts/houdini/plugins/load/load_usd_reference.py b/openpype/hosts/houdini/plugins/load/load_usd_reference.py index 0edd8d9af6..fdb443f4cf 100644 --- a/openpype/hosts/houdini/plugins/load/load_usd_reference.py +++ b/openpype/hosts/houdini/plugins/load/load_usd_reference.py @@ -1,8 +1,11 @@ -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import lib, pipeline -class USDReferenceLoader(api.Loader): +class USDReferenceLoader(load.LoaderPlugin): """Reference USD file in Solaris""" families = [ @@ -57,7 +60,7 @@ class USDReferenceLoader(api.Loader): node = container["node"] # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = file_path.replace("\\", "/") # Update attributes diff --git a/openpype/hosts/houdini/plugins/load/load_vdb.py b/openpype/hosts/houdini/plugins/load/load_vdb.py index 40aa7a1d18..06bb9e45e4 100644 --- a/openpype/hosts/houdini/plugins/load/load_vdb.py +++ b/openpype/hosts/houdini/plugins/load/load_vdb.py @@ -1,11 +1,14 @@ import os import re -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.houdini.api import pipeline -class VdbLoader(api.Loader): +class VdbLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["vdbcache"] @@ -96,7 +99,7 @@ class VdbLoader(api.Loader): return # Update the file path - file_path = api.get_representation_path(representation) + file_path = get_representation_path(representation) file_path = self.format_path(file_path) file_node.setParms({"fileName": file_path}) diff --git a/openpype/hosts/houdini/plugins/load/show_usdview.py b/openpype/hosts/houdini/plugins/load/show_usdview.py index f23974094e..8066615181 100644 --- a/openpype/hosts/houdini/plugins/load/show_usdview.py +++ b/openpype/hosts/houdini/plugins/load/show_usdview.py @@ -1,7 +1,7 @@ -from avalon import api +from openpype.pipeline import load -class ShowInUsdview(api.Loader): +class ShowInUsdview(load.LoaderPlugin): """Open USD file in usdview""" families = ["colorbleed.usd"] diff --git a/openpype/hosts/houdini/plugins/publish/extract_usd_layered.py b/openpype/hosts/houdini/plugins/publish/extract_usd_layered.py index 645bd05d4b..3e842ae766 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_usd_layered.py +++ b/openpype/hosts/houdini/plugins/publish/extract_usd_layered.py @@ -7,6 +7,7 @@ from collections import deque import pyblish.api import openpype.api +from openpype.pipeline import get_representation_path import openpype.hosts.houdini.api.usd as hou_usdlib from openpype.hosts.houdini.api.lib import render_rop @@ -308,7 +309,7 @@ class ExtractUSDLayered(openpype.api.Extractor): self.log.debug("No existing representation..") return False - old_file = api.get_representation_path(representation) + old_file = get_representation_path(representation) if not os.path.exists(old_file): return False diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 41c67a6209..94efbb7a07 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -17,10 +17,16 @@ import bson from maya import cmds, mel import maya.api.OpenMaya as om -from avalon import api, io, pipeline +from avalon import api, io from openpype import lib from openpype.api import get_anatomy_settings +from openpype.pipeline import ( + discover_loader_plugins, + loaders_from_representation, + get_representation_path, + load_representation, +) from .commands import reset_frame_range @@ -1580,21 +1586,21 @@ def assign_look_by_version(nodes, version_id): log.info("Using look for the first time ..") # Load file - loaders = api.loaders_from_representation(api.discover(api.Loader), - representation_id) + _loaders = discover_loader_plugins() + loaders = loaders_from_representation(_loaders, representation_id) Loader = next((i for i in loaders if i.__name__ == "LookLoader"), None) if Loader is None: raise RuntimeError("Could not find LookLoader, this is a bug") # Reference the look file with maintained_selection(): - container_node = pipeline.load(Loader, look_representation) + container_node = load_representation(Loader, look_representation) # Get container members shader_nodes = get_container_members(container_node) # Load relationships - shader_relation = api.get_representation_path(json_representation) + shader_relation = get_representation_path(json_representation) with open(shader_relation, "r") as f: relationships = json.load(f) diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 8c3669c5d1..23e21894bd 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -16,7 +16,11 @@ import openpype.hosts.maya from openpype.tools.utils import host_tools from openpype.lib import any_outdated from openpype.lib.path_tools import HostDirmap -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from openpype.hosts.maya.lib import copy_workspace_mel from . import menu, lib @@ -49,7 +53,7 @@ def install(): pyblish.api.register_host("mayapy") pyblish.api.register_host("maya") - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) log.info(PUBLISH_PATH) @@ -175,7 +179,7 @@ def uninstall(): pyblish.api.deregister_host("mayapy") pyblish.api.deregister_host("maya") - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index e0c21645e4..12cbd00257 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -4,9 +4,12 @@ from maya import cmds import qargparse -from avalon import api from avalon.pipeline import AVALON_CONTAINER_ID -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, + get_representation_path, +) from .pipeline import containerise from . import lib @@ -95,7 +98,7 @@ class Creator(LegacyCreator): return instance -class Loader(api.Loader): +class Loader(LoaderPlugin): hosts = ["maya"] @@ -194,7 +197,7 @@ class ReferenceLoader(Loader): node = container["objectName"] - path = api.get_representation_path(representation) + path = get_representation_path(representation) # Get reference node from container members members = get_container_members(node) diff --git a/openpype/hosts/maya/api/setdress.py b/openpype/hosts/maya/api/setdress.py index 1a7c3933a1..74ee292eb2 100644 --- a/openpype/hosts/maya/api/setdress.py +++ b/openpype/hosts/maya/api/setdress.py @@ -8,7 +8,15 @@ import copy import six from maya import cmds -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + discover_loader_plugins, + loaders_from_representation, + load_representation, + update_container, + remove_container, + get_representation_path, +) from openpype.hosts.maya.api.lib import ( matrix_equals, unique_namespace @@ -120,12 +128,13 @@ def load_package(filepath, name, namespace=None): root = "{}:{}".format(namespace, name) containers = [] - all_loaders = api.discover(api.Loader) + all_loaders = discover_loader_plugins() for representation_id, instances in data.items(): # Find the compatible loaders - loaders = api.loaders_from_representation(all_loaders, - representation_id) + loaders = loaders_from_representation( + all_loaders, representation_id + ) for instance in instances: container = _add(instance=instance, @@ -180,9 +189,11 @@ def _add(instance, representation_id, loaders, namespace, root="|"): instance['loader'], instance) raise RuntimeError("Loader is missing.") - container = api.load(Loader, - representation_id, - namespace=instance['namespace']) + container = load_representation( + Loader, + representation_id, + namespace=instance['namespace'] + ) # Get the root from the loaded container loaded_root = get_container_transforms({"objectName": container}, @@ -320,13 +331,13 @@ def update_package(set_container, representation): "type": "representation" }) - current_file = api.get_representation_path(current_representation) + current_file = get_representation_path(current_representation) assert current_file.endswith(".json") with open(current_file, "r") as fp: current_data = json.load(fp) # Load the new package data - new_file = api.get_representation_path(representation) + new_file = get_representation_path(representation) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) @@ -460,12 +471,12 @@ def update_scene(set_container, containers, current_data, new_data, new_file): # considered as new element and added afterwards. processed_containers.pop() processed_namespaces.remove(container_ns) - api.remove(container) + remove_container(container) continue # Check whether the conversion can be done by the Loader. # They *must* use the same asset, subset and Loader for - # `api.update` to make sense. + # `update_container` to make sense. old = io.find_one({ "_id": io.ObjectId(representation_current) }) @@ -479,20 +490,21 @@ def update_scene(set_container, containers, current_data, new_data, new_file): continue new_version = new["context"]["version"] - api.update(container, version=new_version) + update_container(container, version=new_version) else: # Remove this container because it's not in the new data log.warning("Removing content: %s", container_ns) - api.remove(container) + remove_container(container) # Add new assets - all_loaders = api.discover(api.Loader) + all_loaders = discover_loader_plugins() for representation_id, instances in new_data.items(): # Find the compatible loaders - loaders = api.loaders_from_representation(all_loaders, - representation_id) + loaders = loaders_from_representation( + all_loaders, representation_id + ) for instance in instances: # Already processed in update functionality @@ -517,7 +529,7 @@ def update_scene(set_container, containers, current_data, new_data, new_file): def compare_representations(old, new): """Check if the old representation given can be updated - Due to limitations of the `api.update` function we cannot allow + Due to limitations of the `update_container` function we cannot allow differences in the following data: * Representation name (extension) diff --git a/openpype/hosts/maya/plugins/inventory/import_modelrender.py b/openpype/hosts/maya/plugins/inventory/import_modelrender.py index 119edccb7a..c5d3d0c8f4 100644 --- a/openpype/hosts/maya/plugins/inventory/import_modelrender.py +++ b/openpype/hosts/maya/plugins/inventory/import_modelrender.py @@ -1,5 +1,9 @@ import json -from avalon import api, io, pipeline +from avalon import api, io +from openpype.pipeline import ( + get_representation_context, + get_representation_path_from_context, +) from openpype.hosts.maya.api.lib import ( maintained_selection, apply_shaders @@ -73,11 +77,11 @@ class ImportModelRender(api.InventoryAction): "name": self.look_data_type, }) - context = pipeline.get_representation_context(look_repr["_id"]) - maya_file = pipeline.get_representation_path_from_context(context) + context = get_representation_context(look_repr["_id"]) + maya_file = get_representation_path_from_context(context) - context = pipeline.get_representation_context(json_repr["_id"]) - json_file = pipeline.get_representation_path_from_context(context) + context = get_representation_context(json_repr["_id"]) + json_file = get_representation_path_from_context(context) # Import the look file with maintained_selection(): diff --git a/openpype/hosts/maya/plugins/load/actions.py b/openpype/hosts/maya/plugins/load/actions.py index 1cb63c8a7a..483ad32402 100644 --- a/openpype/hosts/maya/plugins/load/actions.py +++ b/openpype/hosts/maya/plugins/load/actions.py @@ -2,14 +2,14 @@ """ -from avalon import api +from openpype.pipeline import load from openpype.hosts.maya.api.lib import ( maintained_selection, unique_namespace ) -class SetFrameRangeLoader(api.Loader): +class SetFrameRangeLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", @@ -43,7 +43,7 @@ class SetFrameRangeLoader(api.Loader): animationEndTime=end) -class SetFrameRangeWithHandlesLoader(api.Loader): +class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", @@ -81,7 +81,7 @@ class SetFrameRangeWithHandlesLoader(api.Loader): animationEndTime=end) -class ImportMayaLoader(api.Loader): +class ImportMayaLoader(load.LoaderPlugin): """Import action for Maya (unmanaged) Warning: diff --git a/openpype/hosts/maya/plugins/load/load_ass.py b/openpype/hosts/maya/plugins/load/load_ass.py index 18b34d2233..18de4df3b1 100644 --- a/openpype/hosts/maya/plugins/load/load_ass.py +++ b/openpype/hosts/maya/plugins/load/load_ass.py @@ -1,8 +1,11 @@ import os import clique -from avalon import api from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api.plugin import get_reference_node from openpype.hosts.maya.api.lib import ( @@ -106,7 +109,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): node = container["objectName"] representation["context"].pop("frame", None) - path = api.get_representation_path(representation) + path = get_representation_path(representation) print(path) # path = self.fname print(self.fname) @@ -164,7 +167,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): type="string") -class AssStandinLoader(api.Loader): +class AssStandinLoader(load.LoaderPlugin): """Load .ASS file as standin""" families = ["ass"] @@ -240,7 +243,7 @@ class AssStandinLoader(api.Loader): import pymel.core as pm - path = api.get_representation_path(representation) + path = get_representation_path(representation) files_in_path = os.listdir(os.path.split(path)[0]) sequence = 0 diff --git a/openpype/hosts/maya/plugins/load/load_assembly.py b/openpype/hosts/maya/plugins/load/load_assembly.py index 0151da7253..902f38695c 100644 --- a/openpype/hosts/maya/plugins/load/load_assembly.py +++ b/openpype/hosts/maya/plugins/load/load_assembly.py @@ -1,7 +1,10 @@ -from avalon import api +from openpype.pipeline import ( + load, + remove_container +) -class AssemblyLoader(api.Loader): +class AssemblyLoader(load.LoaderPlugin): families = ["assembly"] representations = ["json"] @@ -48,13 +51,11 @@ class AssemblyLoader(api.Loader): def update(self, container, representation): from openpype import setdress - return setdress.update_package(container, - representation) + return setdress.update_package(container, representation) def remove(self, container): """Remove all sub containers""" - from avalon import api from openpype import setdress import maya.cmds as cmds @@ -63,7 +64,7 @@ class AssemblyLoader(api.Loader): for member_container in member_containers: self.log.info("Removing container %s", member_container['objectName']) - api.remove(member_container) + remove_container(member_container) # Remove alembic hierarchy reference # TODO: Check whether removing all contained references is safe enough diff --git a/openpype/hosts/maya/plugins/load/load_audio.py b/openpype/hosts/maya/plugins/load/load_audio.py index 99f1f7c172..d8844ffea6 100644 --- a/openpype/hosts/maya/plugins/load/load_audio.py +++ b/openpype/hosts/maya/plugins/load/load_audio.py @@ -1,10 +1,14 @@ from maya import cmds, mel -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api.pipeline import containerise from openpype.hosts.maya.api.lib import unique_namespace -class AudioLoader(api.Loader): +class AudioLoader(load.LoaderPlugin): """Specific loader of audio.""" families = ["audio"] @@ -51,7 +55,7 @@ class AudioLoader(api.Loader): assert audio_node is not None, "Audio node not found." - path = api.get_representation_path(representation) + path = get_representation_path(representation) audio_node.filename.set(path) cmds.setAttr( container["objectName"] + ".representation", diff --git a/openpype/hosts/maya/plugins/load/load_gpucache.py b/openpype/hosts/maya/plugins/load/load_gpucache.py index 2e0b7bb810..591e568e4c 100644 --- a/openpype/hosts/maya/plugins/load/load_gpucache.py +++ b/openpype/hosts/maya/plugins/load/load_gpucache.py @@ -1,9 +1,13 @@ import os -from avalon import api + +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.api import get_project_settings -class GpuCacheLoader(api.Loader): +class GpuCacheLoader(load.LoaderPlugin): """Load model Alembic as gpuCache""" families = ["model"] @@ -73,7 +77,7 @@ class GpuCacheLoader(api.Loader): import maya.cmds as cmds - path = api.get_representation_path(representation) + path = get_representation_path(representation) # Update the cache members = cmds.sets(container['objectName'], query=True) diff --git a/openpype/hosts/maya/plugins/load/load_image_plane.py b/openpype/hosts/maya/plugins/load/load_image_plane.py index 8e33f51389..b250986489 100644 --- a/openpype/hosts/maya/plugins/load/load_image_plane.py +++ b/openpype/hosts/maya/plugins/load/load_image_plane.py @@ -1,6 +1,10 @@ from Qt import QtWidgets, QtCore -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api.pipeline import containerise from openpype.hosts.maya.api.lib import unique_namespace @@ -74,7 +78,7 @@ class CameraWindow(QtWidgets.QDialog): self.close() -class ImagePlaneLoader(api.Loader): +class ImagePlaneLoader(load.LoaderPlugin): """Specific loader of plate for image planes on selected camera.""" families = ["image", "plate", "render"] @@ -203,7 +207,7 @@ class ImagePlaneLoader(api.Loader): assert image_plane_shape is not None, "Image plane not found." - path = api.get_representation_path(representation) + path = get_representation_path(representation) image_plane_shape.imageName.set(path) cmds.setAttr( container["objectName"] + ".representation", diff --git a/openpype/hosts/maya/plugins/load/load_look.py b/openpype/hosts/maya/plugins/load/load_look.py index 96c1ecbb20..8f02ed59b8 100644 --- a/openpype/hosts/maya/plugins/load/load_look.py +++ b/openpype/hosts/maya/plugins/load/load_look.py @@ -5,7 +5,8 @@ from collections import defaultdict from Qt import QtWidgets -from avalon import api, io +from avalon import io +from openpype.pipeline import get_representation_path import openpype.hosts.maya.api.plugin from openpype.hosts.maya.api import lib from openpype.widgets.message_window import ScrollMessageBox @@ -77,7 +78,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): }) # Load relationships - shader_relation = api.get_representation_path(json_representation) + shader_relation = get_representation_path(json_representation) with open(shader_relation, "r") as f: json_data = json.load(f) diff --git a/openpype/hosts/maya/plugins/load/load_matchmove.py b/openpype/hosts/maya/plugins/load/load_matchmove.py index abc702cde8..ee3332bd09 100644 --- a/openpype/hosts/maya/plugins/load/load_matchmove.py +++ b/openpype/hosts/maya/plugins/load/load_matchmove.py @@ -1,8 +1,8 @@ -from avalon import api from maya import mel +from openpype.pipeline import load -class MatchmoveLoader(api.Loader): +class MatchmoveLoader(load.LoaderPlugin): """ This will run matchmove script to create track in scene. diff --git a/openpype/hosts/maya/plugins/load/load_redshift_proxy.py b/openpype/hosts/maya/plugins/load/load_redshift_proxy.py index fd2ae0f1d3..d93a9f02a2 100644 --- a/openpype/hosts/maya/plugins/load/load_redshift_proxy.py +++ b/openpype/hosts/maya/plugins/load/load_redshift_proxy.py @@ -5,8 +5,11 @@ import clique import maya.cmds as cmds -from avalon import api from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api.lib import ( namespaced, maintained_selection, @@ -15,7 +18,7 @@ from openpype.hosts.maya.api.lib import ( from openpype.hosts.maya.api.pipeline import containerise -class RedshiftProxyLoader(api.Loader): +class RedshiftProxyLoader(load.LoaderPlugin): """Load Redshift proxy""" families = ["redshiftproxy"] @@ -78,7 +81,7 @@ class RedshiftProxyLoader(api.Loader): rs_meshes = cmds.ls(members, type="RedshiftProxyMesh") assert rs_meshes, "Cannot find RedshiftProxyMesh in container" - filename = api.get_representation_path(representation) + filename = get_representation_path(representation) for rs_mesh in rs_meshes: cmds.setAttr("{}.fileName".format(rs_mesh), diff --git a/openpype/hosts/maya/plugins/load/load_rendersetup.py b/openpype/hosts/maya/plugins/load/load_rendersetup.py index efeff2f193..7a2d8b1002 100644 --- a/openpype/hosts/maya/plugins/load/load_rendersetup.py +++ b/openpype/hosts/maya/plugins/load/load_rendersetup.py @@ -7,10 +7,13 @@ instance. """ import json -import six import sys +import six -from avalon import api +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api import lib from openpype.hosts.maya.api.pipeline import containerise @@ -18,7 +21,7 @@ from maya import cmds import maya.app.renderSetup.model.renderSetup as renderSetup -class RenderSetupLoader(api.Loader): +class RenderSetupLoader(load.LoaderPlugin): """Load json preset for RenderSetup overwriting current one.""" families = ["rendersetup"] @@ -87,7 +90,7 @@ class RenderSetupLoader(api.Loader): "Render setup setting will be overwritten by new version. All " "setting specified by user not included in loaded version " "will be lost.") - path = api.get_representation_path(representation) + path = get_representation_path(representation) with open(path, "r") as file: try: renderSetup.instance().decode( diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py b/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py index 3e1d67ae9a..70bd9d22e2 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_redshift.py @@ -1,9 +1,10 @@ import os -from avalon import api + from openpype.api import get_project_settings +from openpype.pipeline import load -class LoadVDBtoRedShift(api.Loader): +class LoadVDBtoRedShift(load.LoaderPlugin): """Load OpenVDB in a Redshift Volume Shape""" families = ["vdbcache"] diff --git a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py index 6d5544103d..4f14235bfb 100644 --- a/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py +++ b/openpype/hosts/maya/plugins/load/load_vdb_to_vray.py @@ -1,6 +1,10 @@ import os -from avalon import api + from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) from maya import cmds @@ -69,7 +73,7 @@ def _fix_duplicate_vvg_callbacks(): matched.add(callback) -class LoadVDBtoVRay(api.Loader): +class LoadVDBtoVRay(load.LoaderPlugin): families = ["vdbcache"] representations = ["vdb"] @@ -252,7 +256,7 @@ class LoadVDBtoVRay(api.Loader): def update(self, container, representation): - path = api.get_representation_path(representation) + path = get_representation_path(representation) # Find VRayVolumeGrid members = cmds.sets(container['objectName'], query=True) diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index ac2fe635b3..5b79b1efb3 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -9,8 +9,12 @@ import os import maya.cmds as cmds -from avalon import api, io +from avalon import io from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api.lib import ( maintained_selection, namespaced, @@ -19,7 +23,7 @@ from openpype.hosts.maya.api.lib import ( from openpype.hosts.maya.api.pipeline import containerise -class VRayProxyLoader(api.Loader): +class VRayProxyLoader(load.LoaderPlugin): """Load VRay Proxy with Alembic or VrayMesh.""" families = ["vrayproxy", "model", "pointcache", "animation"] @@ -100,7 +104,10 @@ class VRayProxyLoader(api.Loader): assert vraymeshes, "Cannot find VRayMesh in container" # get all representations for this version - filename = self._get_abc(representation["parent"]) or api.get_representation_path(representation) # noqa: E501 + filename = ( + self._get_abc(representation["parent"]) + or get_representation_path(representation) + ) for vray_mesh in vraymeshes: cmds.setAttr("{}.fileName".format(vray_mesh), @@ -185,7 +192,7 @@ class VRayProxyLoader(api.Loader): if abc_rep: self.log.debug("Found, we'll link alembic to vray proxy.") - file_name = api.get_representation_path(abc_rep) + file_name = get_representation_path(abc_rep) self.log.debug("File: {}".format(self.fname)) return file_name diff --git a/openpype/hosts/maya/plugins/load/load_vrayscene.py b/openpype/hosts/maya/plugins/load/load_vrayscene.py index dfe2b85edc..61132088cc 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayscene.py +++ b/openpype/hosts/maya/plugins/load/load_vrayscene.py @@ -1,8 +1,11 @@ # -*- coding: utf-8 -*- import os import maya.cmds as cmds # noqa -from avalon import api from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api.lib import ( maintained_selection, namespaced, @@ -11,7 +14,7 @@ from openpype.hosts.maya.api.lib import ( from openpype.hosts.maya.api.pipeline import containerise -class VRaySceneLoader(api.Loader): +class VRaySceneLoader(load.LoaderPlugin): """Load Vray scene""" families = ["vrayscene_layer"] @@ -78,7 +81,7 @@ class VRaySceneLoader(api.Loader): vraymeshes = cmds.ls(members, type="VRayScene") assert vraymeshes, "Cannot find VRayScene in container" - filename = api.get_representation_path(representation) + filename = get_representation_path(representation) for vray_mesh in vraymeshes: cmds.setAttr("{}.FilePath".format(vray_mesh), diff --git a/openpype/hosts/maya/plugins/load/load_yeti_cache.py b/openpype/hosts/maya/plugins/load/load_yeti_cache.py index dfe75173ac..c64e1c540b 100644 --- a/openpype/hosts/maya/plugins/load/load_yeti_cache.py +++ b/openpype/hosts/maya/plugins/load/load_yeti_cache.py @@ -7,13 +7,17 @@ from pprint import pprint from maya import cmds -from avalon import api, io +from avalon import io from openpype.api import get_project_settings +from openpype.pipeline import ( + load, + get_representation_path +) from openpype.hosts.maya.api import lib from openpype.hosts.maya.api.pipeline import containerise -class YetiCacheLoader(api.Loader): +class YetiCacheLoader(load.LoaderPlugin): families = ["yeticache", "yetiRig"] representations = ["fur"] @@ -121,8 +125,8 @@ class YetiCacheLoader(api.Loader): "cannot find fursettings representation" ) - settings_fname = api.get_representation_path(fur_settings) - path = api.get_representation_path(representation) + settings_fname = get_representation_path(fur_settings) + path = get_representation_path(representation) # Get all node data with open(settings_fname, "r") as fp: settings = json.load(fp) diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index d98a951491..7011b3bed1 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -14,7 +14,11 @@ from openpype.api import ( BuildWorkfile, get_current_project_settings ) -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from openpype.tools.utils import host_tools from .command import viewer_update_and_undo_stop @@ -98,7 +102,7 @@ def install(): log.info("Registering Nuke plug-ins..") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) @@ -124,7 +128,7 @@ def uninstall(): log.info("Deregistering Nuke plug-ins..") pyblish.deregister_host("nuke") pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) pyblish.api.deregister_callback( diff --git a/openpype/hosts/nuke/api/plugin.py b/openpype/hosts/nuke/api/plugin.py index ff186cd685..d0bb45a05d 100644 --- a/openpype/hosts/nuke/api/plugin.py +++ b/openpype/hosts/nuke/api/plugin.py @@ -4,10 +4,11 @@ import string import nuke -import avalon.api - from openpype.api import get_current_project_settings -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) from .lib import ( Knobby, check_subsetname_exists, @@ -85,7 +86,7 @@ def get_review_presets_config(): return [str(name) for name, _prop in outputs.items()] -class NukeLoader(avalon.api.Loader): +class NukeLoader(LoaderPlugin): container_id_knob = "containerId" container_id = None diff --git a/openpype/hosts/nuke/plugins/load/actions.py b/openpype/hosts/nuke/plugins/load/actions.py index 07dcf2d8e1..81840b3a38 100644 --- a/openpype/hosts/nuke/plugins/load/actions.py +++ b/openpype/hosts/nuke/plugins/load/actions.py @@ -2,13 +2,13 @@ """ -from avalon import api from openpype.api import Logger +from openpype.pipeline import load log = Logger().get_logger(__name__) -class SetFrameRangeLoader(api.Loader): +class SetFrameRangeLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", @@ -42,7 +42,7 @@ class SetFrameRangeLoader(api.Loader): lib.update_frame_range(start, end) -class SetFrameRangeWithHandlesLoader(api.Loader): +class SetFrameRangeWithHandlesLoader(load.LoaderPlugin): """Specific loader of Alembic for the avalon.animation family""" families = ["animation", diff --git a/openpype/hosts/nuke/plugins/load/load_backdrop.py b/openpype/hosts/nuke/plugins/load/load_backdrop.py index 6619cfb414..05ce4d08d3 100644 --- a/openpype/hosts/nuke/plugins/load/load_backdrop.py +++ b/openpype/hosts/nuke/plugins/load/load_backdrop.py @@ -1,7 +1,11 @@ -from avalon import api, style, io +from avalon import style, io import nuke import nukescripts +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import ( find_free_space_to_paste_nodes, maintained_selection, @@ -14,7 +18,7 @@ from openpype.hosts.nuke.api.commands import viewer_update_and_undo_stop from openpype.hosts.nuke.api import containerise, update_container -class LoadBackdropNodes(api.Loader): +class LoadBackdropNodes(load.LoaderPlugin): """Loading Published Backdrop nodes (workfile, nukenodes)""" representations = ["nk"] @@ -191,7 +195,7 @@ class LoadBackdropNodes(api.Loader): # get corresponding node GN = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") context = representation["context"] name = container['name'] version_data = version.get("data", {}) diff --git a/openpype/hosts/nuke/plugins/load/load_camera_abc.py b/openpype/hosts/nuke/plugins/load/load_camera_abc.py index 9610940619..fb5f7f8ede 100644 --- a/openpype/hosts/nuke/plugins/load/load_camera_abc.py +++ b/openpype/hosts/nuke/plugins/load/load_camera_abc.py @@ -1,6 +1,10 @@ import nuke -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api import ( containerise, update_container, @@ -11,7 +15,7 @@ from openpype.hosts.nuke.api.lib import ( ) -class AlembicCameraLoader(api.Loader): +class AlembicCameraLoader(load.LoaderPlugin): """ This will load alembic camera into script. """ @@ -127,7 +131,7 @@ class AlembicCameraLoader(api.Loader): data_imprint.update({k: version_data[k]}) # getting file path - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") with maintained_selection(): camera_node = nuke.toNode(object_name) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index a253ba4a9d..563a325a83 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -1,7 +1,8 @@ import nuke import qargparse -from avalon import api, io +from avalon import io +from openpype.pipeline import get_representation_path from openpype.hosts.nuke.api.lib import ( get_imageio_input_colorspace, maintained_selection @@ -186,7 +187,7 @@ class LoadClip(plugin.NukeLoader): is_sequence = len(representation["files"]) > 1 read_node = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") start_at_workfile = bool("start at" in read_node['frame_mode'].value()) diff --git a/openpype/hosts/nuke/plugins/load/load_effects.py b/openpype/hosts/nuke/plugins/load/load_effects.py index f636c6b510..2f8333e4f2 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects.py +++ b/openpype/hosts/nuke/plugins/load/load_effects.py @@ -1,7 +1,11 @@ import json from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import style, io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api import ( containerise, update_container, @@ -9,7 +13,7 @@ from openpype.hosts.nuke.api import ( ) -class LoadEffects(api.Loader): +class LoadEffects(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" representations = ["effectJson"] @@ -149,7 +153,7 @@ class LoadEffects(api.Loader): # get corresponding node GN = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") name = container['name'] version_data = version.get("data", {}) vname = version.get("name", None) diff --git a/openpype/hosts/nuke/plugins/load/load_effects_ip.py b/openpype/hosts/nuke/plugins/load/load_effects_ip.py index 990bce54f1..b998eda69b 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_effects_ip.py @@ -3,7 +3,11 @@ from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import style, io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api import lib from openpype.hosts.nuke.api import ( containerise, @@ -12,7 +16,7 @@ from openpype.hosts.nuke.api import ( ) -class LoadEffectsInputProcess(api.Loader): +class LoadEffectsInputProcess(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" representations = ["effectJson"] @@ -156,7 +160,7 @@ class LoadEffectsInputProcess(api.Loader): # get corresponding node GN = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") name = container['name'] version_data = version.get("data", {}) vname = version.get("name", None) diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo.py b/openpype/hosts/nuke/plugins/load/load_gizmo.py index 659977d789..0eea6f784b 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo.py @@ -1,5 +1,9 @@ import nuke -from avalon import api, style, io +from avalon import style, io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import ( maintained_selection, get_avalon_knob_data, @@ -12,7 +16,7 @@ from openpype.hosts.nuke.api import ( ) -class LoadGizmo(api.Loader): +class LoadGizmo(load.LoaderPlugin): """Loading nuke Gizmo""" representations = ["gizmo"] @@ -103,7 +107,7 @@ class LoadGizmo(api.Loader): # get corresponding node GN = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") name = container['name'] version_data = version.get("data", {}) vname = version.get("name", None) diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py index 240bfd467d..8b3f35a29a 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -1,5 +1,9 @@ -from avalon import api, style, io +from avalon import style, io import nuke +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import ( maintained_selection, create_backdrop, @@ -13,7 +17,7 @@ from openpype.hosts.nuke.api import ( ) -class LoadGizmoInputProcess(api.Loader): +class LoadGizmoInputProcess(load.LoaderPlugin): """Loading colorspace soft effect exported from nukestudio""" representations = ["gizmo"] @@ -109,7 +113,7 @@ class LoadGizmoInputProcess(api.Loader): # get corresponding node GN = nuke.toNode(container['objectName']) - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") name = container['name'] version_data = version.get("data", {}) vname = version.get("name", None) diff --git a/openpype/hosts/nuke/plugins/load/load_image.py b/openpype/hosts/nuke/plugins/load/load_image.py index 27c634ec57..e04ccf3bf1 100644 --- a/openpype/hosts/nuke/plugins/load/load_image.py +++ b/openpype/hosts/nuke/plugins/load/load_image.py @@ -1,8 +1,12 @@ import nuke import qargparse -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import ( get_imageio_input_colorspace ) @@ -13,7 +17,7 @@ from openpype.hosts.nuke.api import ( ) -class LoadImage(api.Loader): +class LoadImage(load.LoaderPlugin): """Load still image into Nuke""" families = [ @@ -161,7 +165,7 @@ class LoadImage(api.Loader): repr_cont = representation["context"] - file = api.get_representation_path(representation) + file = get_representation_path(representation) if not file: repr_id = representation["_id"] diff --git a/openpype/hosts/nuke/plugins/load/load_matchmove.py b/openpype/hosts/nuke/plugins/load/load_matchmove.py index 60d5dc026f..f5a90706c7 100644 --- a/openpype/hosts/nuke/plugins/load/load_matchmove.py +++ b/openpype/hosts/nuke/plugins/load/load_matchmove.py @@ -1,8 +1,8 @@ -from avalon import api import nuke +from openpype.pipeline import load -class MatchmoveLoader(api.Loader): +class MatchmoveLoader(load.LoaderPlugin): """ This will run matchmove script to create track in script. """ diff --git a/openpype/hosts/nuke/plugins/load/load_model.py b/openpype/hosts/nuke/plugins/load/load_model.py index 2b52bbf00f..e445beca05 100644 --- a/openpype/hosts/nuke/plugins/load/load_model.py +++ b/openpype/hosts/nuke/plugins/load/load_model.py @@ -1,5 +1,9 @@ import nuke -from avalon import api, io +from avalon import io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import maintained_selection from openpype.hosts.nuke.api import ( containerise, @@ -8,7 +12,7 @@ from openpype.hosts.nuke.api import ( ) -class AlembicModelLoader(api.Loader): +class AlembicModelLoader(load.LoaderPlugin): """ This will load alembic model into script. """ @@ -124,7 +128,7 @@ class AlembicModelLoader(api.Loader): data_imprint.update({k: version_data[k]}) # getting file path - file = api.get_representation_path(representation).replace("\\", "/") + file = get_representation_path(representation).replace("\\", "/") with maintained_selection(): model_node = nuke.toNode(object_name) diff --git a/openpype/hosts/nuke/plugins/load/load_script_precomp.py b/openpype/hosts/nuke/plugins/load/load_script_precomp.py index aa48b631c5..cd47a840ae 100644 --- a/openpype/hosts/nuke/plugins/load/load_script_precomp.py +++ b/openpype/hosts/nuke/plugins/load/load_script_precomp.py @@ -1,5 +1,9 @@ import nuke -from avalon import api, style, io +from avalon import style, io +from openpype.pipeline import ( + load, + get_representation_path, +) from openpype.hosts.nuke.api.lib import get_avalon_knob_data from openpype.hosts.nuke.api import ( containerise, @@ -8,7 +12,7 @@ from openpype.hosts.nuke.api import ( ) -class LinkAsGroup(api.Loader): +class LinkAsGroup(load.LoaderPlugin): """Copy the published file to be pasted at the desired location""" representations = ["nk"] @@ -108,7 +112,7 @@ class LinkAsGroup(api.Loader): """ node = nuke.toNode(container['objectName']) - root = api.get_representation_path(representation).replace("\\", "/") + root = get_representation_path(representation).replace("\\", "/") # Get start frame from version data version = io.find_one({ diff --git a/openpype/hosts/nuke/plugins/publish/precollect_writes.py b/openpype/hosts/nuke/plugins/publish/precollect_writes.py index 189f28f7c6..85e98db7ed 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_writes.py @@ -3,8 +3,9 @@ import re from pprint import pformat import nuke import pyblish.api +from avalon import io import openpype.api as pype -from avalon import io, api +from openpype.pipeline import get_representation_path @pyblish.api.log @@ -182,7 +183,7 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): if repre_doc: instance.data["audio"] = [{ "offset": 0, - "filename": api.get_representation_path(repre_doc) + "filename": get_representation_path(repre_doc) }] self.log.debug("instance.data: {}".format(pformat(instance.data))) diff --git a/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py b/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py index 22a9b3678e..39fe011d85 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py +++ b/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py @@ -1,12 +1,16 @@ import os -import toml import nuke +import toml import pyblish.api -from avalon import api from bson.objectid import ObjectId +from openpype.pipeline import ( + discover_loader_plugins, + load_representation, +) + class RepairReadLegacyAction(pyblish.api.Action): @@ -49,13 +53,13 @@ class RepairReadLegacyAction(pyblish.api.Action): loader_name = "LoadMov" loader_plugin = None - for Loader in api.discover(api.Loader): + for Loader in discover_loader_plugins(): if Loader.__name__ != loader_name: continue loader_plugin = Loader - api.load( + load_representation( Loader=loader_plugin, representation=ObjectId(data["representation"]) ) diff --git a/openpype/hosts/photoshop/api/README.md b/openpype/hosts/photoshop/api/README.md index b958f53803..80792a4da0 100644 --- a/openpype/hosts/photoshop/api/README.md +++ b/openpype/hosts/photoshop/api/README.md @@ -195,11 +195,12 @@ class ExtractImage(openpype.api.Extractor): #### Loader Plugin ```python from avalon import api, photoshop +from openpype.pipeline import load, get_representation_path stub = photoshop.stub() -class ImageLoader(api.Loader): +class ImageLoader(load.LoaderPlugin): """Load images Stores the imported asset in a container named after the asset. @@ -227,7 +228,7 @@ class ImageLoader(api.Loader): with photoshop.maintained_selection(): stub.replace_smart_object( - layer, api.get_representation_path(representation) + layer, get_representation_path(representation) ) stub.imprint( @@ -245,7 +246,7 @@ https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/ Add --enable-blink-features=ShadowDOMV0,CustomElementsV0 when starting Chrome then localhost:8078 (port set in `photoshop\extension\.debug`) -Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01 +Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01 Or install CEF client from https://github.com/Adobe-CEP/CEP-Resources/tree/master/CEP_9.x ## Resources diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 662e9dbebc..a7bd64585d 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -6,7 +6,11 @@ import avalon.api from avalon import pipeline, io from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) import openpype.hosts.photoshop from . import lib @@ -67,7 +71,7 @@ def install(): pyblish.api.register_host("photoshop") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -80,7 +84,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/photoshop/api/plugin.py b/openpype/hosts/photoshop/api/plugin.py index c577c67d82..c80e6bbd06 100644 --- a/openpype/hosts/photoshop/api/plugin.py +++ b/openpype/hosts/photoshop/api/plugin.py @@ -1,6 +1,6 @@ import re -import avalon.api +from openpype.pipeline import LoaderPlugin from .launch_logic import stub @@ -29,7 +29,7 @@ def get_unique_layer_name(layers, asset_name, subset_name): return "{}_{:0>3d}".format(name, occurrences + 1) -class PhotoshopLoader(avalon.api.Loader): +class PhotoshopLoader(LoaderPlugin): @staticmethod def get_stub(): return stub() diff --git a/openpype/hosts/photoshop/plugins/load/load_image.py b/openpype/hosts/photoshop/plugins/load/load_image.py index 3b1cfe9636..0a9421b8f2 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image.py +++ b/openpype/hosts/photoshop/plugins/load/load_image.py @@ -1,6 +1,6 @@ import re -from avalon import api +from openpype.pipeline import get_representation_path from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name @@ -54,7 +54,7 @@ class ImageLoader(photoshop.PhotoshopLoader): else: # switching version - keep same name layer_name = container["namespace"] - path = api.get_representation_path(representation) + path = get_representation_path(representation) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name diff --git a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py index 12e0503dfc..5f39121ae1 100644 --- a/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py +++ b/openpype/hosts/photoshop/plugins/load/load_image_from_sequence.py @@ -1,8 +1,8 @@ import os import qargparse -from avalon.pipeline import get_representation_path_from_context +from openpype.pipeline import get_representation_path_from_context from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name diff --git a/openpype/hosts/photoshop/plugins/load/load_reference.py b/openpype/hosts/photoshop/plugins/load/load_reference.py index 60142d4a1f..f5f0545d39 100644 --- a/openpype/hosts/photoshop/plugins/load/load_reference.py +++ b/openpype/hosts/photoshop/plugins/load/load_reference.py @@ -1,7 +1,6 @@ import re -from avalon import api - +from openpype.pipeline import get_representation_path from openpype.hosts.photoshop import api as photoshop from openpype.hosts.photoshop.api import get_unique_layer_name @@ -55,7 +54,7 @@ class ReferenceLoader(photoshop.PhotoshopLoader): else: # switching version - keep same name layer_name = container["namespace"] - path = api.get_representation_path(representation) + path = get_representation_path(representation) with photoshop.maintained_selection(): stub.replace_smart_object( layer, path, layer_name diff --git a/openpype/hosts/resolve/api/pipeline.py b/openpype/hosts/resolve/api/pipeline.py index c82545268b..829794dd41 100644 --- a/openpype/hosts/resolve/api/pipeline.py +++ b/openpype/hosts/resolve/api/pipeline.py @@ -9,7 +9,11 @@ from avalon import schema from avalon.pipeline import AVALON_CONTAINER_ID from pyblish import api as pyblish from openpype.api import Logger -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from . import lib from . import PLUGINS_DIR from openpype.tools.utils import host_tools @@ -42,7 +46,7 @@ def install(): pyblish.register_plugin_path(PUBLISH_PATH) log.info("Registering DaVinci Resovle plug-ins..") - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) @@ -67,7 +71,7 @@ def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) log.info("Deregistering DaVinci Resovle plug-ins..") - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index e7793d6e95..8e1436021c 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -4,14 +4,15 @@ import uuid import qargparse from Qt import QtWidgets, QtCore -from avalon import api import openpype.api as pype -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) from openpype.hosts import resolve from . import lib - class CreatorWidget(QtWidgets.QDialog): # output items @@ -292,7 +293,7 @@ class ClipLoader: """ Initialize object Arguments: - cls (avalon.api.Loader): plugin object + cls (openpype.pipeline.load.LoaderPlugin): plugin object context (dict): loader plugin context options (dict)[optional]: possible keys: projectBinPath: "path/to/binItem" @@ -448,7 +449,7 @@ class ClipLoader: return timeline_item -class TimelineItemLoader(api.Loader): +class TimelineItemLoader(LoaderPlugin): """A basic SequenceLoader for Resolve This will implement the basic behavior for a loader to inherit from that diff --git a/openpype/hosts/resolve/plugins/load/load_clip.py b/openpype/hosts/resolve/plugins/load/load_clip.py index e20384ee6c..71850d95f6 100644 --- a/openpype/hosts/resolve/plugins/load/load_clip.py +++ b/openpype/hosts/resolve/plugins/load/load_clip.py @@ -1,11 +1,14 @@ -from avalon import io, api -from openpype.hosts import resolve from copy import deepcopy from importlib import reload + +from avalon import io +from openpype.hosts import resolve +from openpype.pipeline import get_representation_path from openpype.hosts.resolve.api import lib, plugin reload(plugin) reload(lib) + class LoadClip(resolve.TimelineItemLoader): """Load a subset to timeline as clip @@ -99,7 +102,7 @@ class LoadClip(resolve.TimelineItemLoader): version_name = version.get("name", None) colorspace = version_data.get("colorspace", None) object_name = "{}_{}".format(name, namespace) - self.fname = api.get_representation_path(representation) + self.fname = get_representation_path(representation) context["version"] = {"data": version_data} loader = resolve.ClipLoader(self, context) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index f4599047b4..6a26446226 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -14,7 +14,11 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.hosts import tvpaint from openpype.api import get_current_project_settings -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from .lib import ( execute_george, @@ -76,7 +80,7 @@ def install(): pyblish.api.register_host("tvpaint") pyblish.api.register_plugin_path(PUBLISH_PATH) - avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) registered_callbacks = ( @@ -98,7 +102,7 @@ def uninstall(): log.info("OpenPype - Uninstalling TVPaint integration") pyblish.api.deregister_host("tvpaint") pyblish.api.deregister_plugin_path(PUBLISH_PATH) - avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH) + deregister_loader_plugins_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/tvpaint/api/plugin.py b/openpype/hosts/tvpaint/api/plugin.py index 8510794f06..15ad8905e0 100644 --- a/openpype/hosts/tvpaint/api/plugin.py +++ b/openpype/hosts/tvpaint/api/plugin.py @@ -1,9 +1,10 @@ import re import uuid -import avalon.api - -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) from openpype.hosts.tvpaint.api import ( pipeline, lib @@ -74,7 +75,7 @@ class Creator(LegacyCreator): self.write_instances(data) -class Loader(avalon.api.Loader): +class Loader(LoaderPlugin): hosts = ["tvpaint"] @staticmethod diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 8ab19bd697..7100ff3a83 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -7,7 +7,11 @@ import pyblish.api from avalon.pipeline import AVALON_CONTAINER_ID from avalon import api -from openpype.pipeline import LegacyCreator +from openpype.pipeline import ( + LegacyCreator, + register_loader_plugins_path, + deregister_loader_plugins_path, +) from openpype.tools.utils import host_tools import openpype.hosts.unreal @@ -44,7 +48,7 @@ def install(): print("-=" * 40) logger.info("installing OpenPype for Unreal") pyblish.api.register_plugin_path(str(PUBLISH_PATH)) - api.register_plugin_path(api.Loader, str(LOAD_PATH)) + register_loader_plugins_path(str(LOAD_PATH)) api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) _register_callbacks() _register_events() @@ -53,7 +57,7 @@ def install(): def uninstall(): """Uninstall Unreal configuration for Avalon.""" pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) - api.deregister_plugin_path(api.Loader, str(LOAD_PATH)) + deregister_loader_plugins_path(str(LOAD_PATH)) api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) diff --git a/openpype/hosts/unreal/api/plugin.py b/openpype/hosts/unreal/api/plugin.py index dd2e7750f0..b24bab831d 100644 --- a/openpype/hosts/unreal/api/plugin.py +++ b/openpype/hosts/unreal/api/plugin.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from abc import ABC -from openpype.pipeline import LegacyCreator -import avalon.api +from openpype.pipeline import ( + LegacyCreator, + LoaderPlugin, +) class Creator(LegacyCreator): @@ -10,6 +12,6 @@ class Creator(LegacyCreator): defaults = ['Main'] -class Loader(avalon.api.Loader, ABC): +class Loader(LoaderPlugin, ABC): """This serves as skeleton for future OpenPype specific functionality""" pass diff --git a/openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py b/openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py index 027e9f4cd3..3508fe5ed7 100644 --- a/openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py +++ b/openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py @@ -2,7 +2,8 @@ """Loader for published alembics.""" import os -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline @@ -140,7 +141,7 @@ class PointCacheAlembicLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = self.get_task(source_path, destination_path, name, True) diff --git a/openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py b/openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py index 0236bab138..180942de51 100644 --- a/openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py +++ b/openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py @@ -2,7 +2,8 @@ """Load Skeletal Mesh alembics.""" import os -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline import unreal # noqa @@ -104,7 +105,7 @@ class SkeletalMeshAlembicLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = unreal.AssetImportTask() diff --git a/openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py b/openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py index 3bcc8b476f..4e00af1d97 100644 --- a/openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py +++ b/openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py @@ -2,7 +2,8 @@ """Loader for Static Mesh alembics.""" import os -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline import unreal # noqa @@ -123,7 +124,7 @@ class StaticMeshAlembicLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = self.get_task(source_path, destination_path, name, True) diff --git a/openpype/hosts/unreal/plugins/load/load_animation.py b/openpype/hosts/unreal/plugins/load/load_animation.py index 63c734b969..8ef81f7851 100644 --- a/openpype/hosts/unreal/plugins/load/load_animation.py +++ b/openpype/hosts/unreal/plugins/load/load_animation.py @@ -3,7 +3,8 @@ import os import json -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline import unreal # noqa @@ -173,7 +174,7 @@ class AnimationFBXLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = unreal.AssetImportTask() diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py index b802f5940a..b987a32a61 100644 --- a/openpype/hosts/unreal/plugins/load/load_layout.py +++ b/openpype/hosts/unreal/plugins/load/load_layout.py @@ -11,7 +11,13 @@ from unreal import AssetToolsHelpers from unreal import FBXImportType from unreal import MathLibrary as umath -from avalon import api, pipeline +from avalon.pipeline import AVALON_CONTAINER_ID +from openpype.pipeline import ( + discover_loader_plugins, + loaders_from_representation, + load_representation, + get_representation_path, +) from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline @@ -205,7 +211,7 @@ class LayoutLoader(plugin.Loader): with open(lib_path, "r") as fp: data = json.load(fp) - all_loaders = api.discover(api.Loader) + all_loaders = discover_loader_plugins() if not loaded: loaded = [] @@ -235,7 +241,7 @@ class LayoutLoader(plugin.Loader): loaded.append(reference) family = element.get('family') - loaders = api.loaders_from_representation( + loaders = loaders_from_representation( all_loaders, reference) loader = None @@ -252,7 +258,7 @@ class LayoutLoader(plugin.Loader): "asset_dir": asset_dir } - assets = api.load( + assets = load_representation( loader, reference, namespace=instance_name, @@ -387,7 +393,7 @@ class LayoutLoader(plugin.Loader): data = { "schema": "openpype:container-2.0", - "id": pipeline.AVALON_CONTAINER_ID, + "id": AVALON_CONTAINER_ID, "asset": asset, "namespace": asset_dir, "container_name": container_name, @@ -411,9 +417,9 @@ class LayoutLoader(plugin.Loader): def update(self, container, representation): ar = unreal.AssetRegistryHelpers.get_asset_registry() - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] - lib_path = Path(api.get_representation_path(representation)) + lib_path = Path(get_representation_path(representation)) self._remove_actors(destination_path) diff --git a/openpype/hosts/unreal/plugins/load/load_rig.py b/openpype/hosts/unreal/plugins/load/load_rig.py index a7ecb0ef7d..3d5616364c 100644 --- a/openpype/hosts/unreal/plugins/load/load_rig.py +++ b/openpype/hosts/unreal/plugins/load/load_rig.py @@ -2,7 +2,8 @@ """Load Skeletal Meshes form FBX.""" import os -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline import unreal # noqa @@ -124,7 +125,7 @@ class SkeletalMeshFBXLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = unreal.AssetImportTask() diff --git a/openpype/hosts/unreal/plugins/load/load_staticmeshfbx.py b/openpype/hosts/unreal/plugins/load/load_staticmeshfbx.py index c8a6964ffb..587fc83a77 100644 --- a/openpype/hosts/unreal/plugins/load/load_staticmeshfbx.py +++ b/openpype/hosts/unreal/plugins/load/load_staticmeshfbx.py @@ -2,7 +2,8 @@ """Load Static meshes form FBX.""" import os -from avalon import api, pipeline +from avalon import pipeline +from openpype.pipeline import get_representation_path from openpype.hosts.unreal.api import plugin from openpype.hosts.unreal.api import pipeline as unreal_pipeline import unreal # noqa @@ -118,7 +119,7 @@ class StaticMeshFBXLoader(plugin.Loader): def update(self, container, representation): name = container["asset_name"] - source_path = api.get_representation_path(representation) + source_path = get_representation_path(representation) destination_path = container["namespace"] task = self.get_task(source_path, destination_path, name, True) diff --git a/openpype/hosts/webpublisher/api/__init__.py b/openpype/hosts/webpublisher/api/__init__.py index 6ce8a58fc2..4542ddbba4 100644 --- a/openpype/hosts/webpublisher/api/__init__.py +++ b/openpype/hosts/webpublisher/api/__init__.py @@ -5,7 +5,6 @@ from avalon import api as avalon from avalon import io from pyblish import api as pyblish import openpype.hosts.webpublisher -from openpype.pipeline import LegacyCreator log = logging.getLogger("openpype.hosts.webpublisher") @@ -13,8 +12,6 @@ HOST_DIR = os.path.dirname(os.path.abspath( openpype.hosts.webpublisher.__file__)) PLUGINS_DIR = os.path.join(HOST_DIR, "plugins") PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish") -LOAD_PATH = os.path.join(PLUGINS_DIR, "load") -CREATE_PATH = os.path.join(PLUGINS_DIR, "create") def application_launch(): @@ -25,8 +22,6 @@ def install(): print("Installing Pype config...") pyblish.register_plugin_path(PUBLISH_PATH) - avalon.register_plugin_path(avalon.Loader, LOAD_PATH) - avalon.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) io.install() @@ -35,8 +30,6 @@ def install(): def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) - avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH) - avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) # to have required methods for interface diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 0bfd3f6de0..bd3fcba950 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -980,6 +980,8 @@ class BuildWorkfile: ... }] """ + from openpype.pipeline import discover_loader_plugins + # Get current asset name and entity current_asset_name = avalon.io.Session["AVALON_ASSET"] current_asset_entity = avalon.io.find_one({ @@ -996,7 +998,7 @@ class BuildWorkfile: # Prepare available loaders loaders_by_name = {} - for loader in avalon.api.discover(avalon.api.Loader): + for loader in discover_loader_plugins(): loader_name = loader.__name__ if loader_name in loaders_by_name: raise KeyError( @@ -1390,6 +1392,11 @@ class BuildWorkfile: Returns: (list) Objects of loaded containers. """ + from openpype.pipeline import ( + IncompatibleLoaderError, + load_representation, + ) + loaded_containers = [] # Get subset id order from build presets. @@ -1451,7 +1458,7 @@ class BuildWorkfile: if not loader: continue try: - container = avalon.api.load( + container = load_representation( loader, repre["_id"], name=subset_name @@ -1460,7 +1467,7 @@ class BuildWorkfile: is_loaded = True except Exception as exc: - if exc == avalon.pipeline.IncompatibleLoaderError: + if exc == IncompatibleLoaderError: self.log.info(( "Loader `{}` is not compatible with" " representation `{}`" diff --git a/openpype/lib/path_templates.py b/openpype/lib/path_templates.py index 62bfdf774a..14e5fe59f8 100644 --- a/openpype/lib/path_templates.py +++ b/openpype/lib/path_templates.py @@ -187,6 +187,16 @@ class StringTemplate(object): result.validate() return result + @classmethod + def format_template(cls, template, data): + objected_template = cls(template) + return objected_template.format(data) + + @classmethod + def format_strict_template(cls, template, data): + objected_template = cls(template) + return objected_template.format_strict(data) + @staticmethod def find_optional_parts(parts): new_parts = [] diff --git a/openpype/lib/plugin_tools.py b/openpype/lib/plugin_tools.py index 19765a6f4a..f11ba56865 100644 --- a/openpype/lib/plugin_tools.py +++ b/openpype/lib/plugin_tools.py @@ -280,6 +280,7 @@ def set_plugin_attributes_from_settings( project_name (str): Name of project for which settings will be loaded. Value from environment `AVALON_PROJECT` is used if not entered. """ + from openpype.pipeline import LegacyCreator, LoaderPlugin # determine host application to use for finding presets if host_name is None: @@ -289,11 +290,11 @@ def set_plugin_attributes_from_settings( project_name = os.environ.get("AVALON_PROJECT") # map plugin superclass to preset json. Currently supported is load and - # create (avalon.api.Loader and avalon.api.Creator) + # create (LoaderPlugin and LegacyCreator) plugin_type = None - if superclass.__name__.split(".")[-1] in ("Loader", "SubsetLoader"): + if superclass is LoaderPlugin or issubclass(superclass, LoaderPlugin): plugin_type = "load" - elif superclass.__name__.split(".")[-1] in ("Creator", "LegacyCreator"): + elif superclass is LegacyCreator or issubclass(superclass, LegacyCreator): plugin_type = "create" if not host_name or not project_name or plugin_type is None: diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 1de1c37575..19d504b6c9 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -13,6 +13,8 @@ from avalon import api, io import pyblish.api +from openpype.pipeline import get_representation_path + def get_resources(version, extension=None): """Get the files from the specific version.""" @@ -23,7 +25,7 @@ def get_resources(version, extension=None): representation = io.find_one(query) assert representation, "This is a bug" - directory = api.get_representation_path(representation) + directory = get_representation_path(representation) print("Source: ", directory) resources = sorted( [ diff --git a/openpype/modules/ftrack/event_handlers_user/action_rv.py b/openpype/modules/ftrack/event_handlers_user/action_rv.py index 71d790f7e7..bdb0eaf250 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_rv.py +++ b/openpype/modules/ftrack/event_handlers_user/action_rv.py @@ -3,9 +3,10 @@ import subprocess import traceback import json -from openpype_modules.ftrack.lib import BaseAction, statics_icon import ftrack_api from avalon import io, api +from openpype.pipeline import get_representation_path +from openpype_modules.ftrack.lib import BaseAction, statics_icon class RVAction(BaseAction): @@ -307,7 +308,7 @@ class RVAction(BaseAction): "name": "preview" } ) - paths.append(api.get_representation_path(representation)) + paths.append(get_representation_path(representation)) return paths diff --git a/openpype/pipeline/create/legacy_create.py b/openpype/pipeline/create/legacy_create.py index d05cdff689..cf6629047e 100644 --- a/openpype/pipeline/create/legacy_create.py +++ b/openpype/pipeline/create/legacy_create.py @@ -21,6 +21,7 @@ class LegacyCreator(object): dynamic_subset_keys = [] log = logging.getLogger("LegacyCreator") + log.propagate = True def __init__(self, name, asset, options=None, data=None): self.name = name # For backwards compatibility diff --git a/openpype/plugins/load/add_site.py b/openpype/plugins/load/add_site.py index 09448d553c..95001691e2 100644 --- a/openpype/plugins/load/add_site.py +++ b/openpype/plugins/load/add_site.py @@ -1,8 +1,8 @@ -from avalon import api from openpype.modules import ModulesManager +from openpype.pipeline import load -class AddSyncSite(api.Loader): +class AddSyncSite(load.LoaderPlugin): """Add sync site to representation""" representations = ["*"] families = ["*"] diff --git a/openpype/plugins/load/copy_file.py b/openpype/plugins/load/copy_file.py index eaf5853035..c3c8e132d4 100644 --- a/openpype/plugins/load/copy_file.py +++ b/openpype/plugins/load/copy_file.py @@ -1,7 +1,9 @@ -from avalon import api, style +from avalon import style + +from openpype.pipeline import load -class CopyFile(api.Loader): +class CopyFile(load.LoaderPlugin): """Copy the published file to be pasted at the desired location""" representations = ["*"] diff --git a/openpype/plugins/load/copy_file_path.py b/openpype/plugins/load/copy_file_path.py index 2041c79f6d..565d8d1ff1 100644 --- a/openpype/plugins/load/copy_file_path.py +++ b/openpype/plugins/load/copy_file_path.py @@ -1,9 +1,9 @@ import os -from avalon import api +from openpype.pipeline import load -class CopyFilePath(api.Loader): +class CopyFilePath(load.LoaderPlugin): """Copy published file path to clipboard""" representations = ["*"] families = ["*"] diff --git a/openpype/plugins/load/delete_old_versions.py b/openpype/plugins/load/delete_old_versions.py index e8612745fb..7cadb8bb14 100644 --- a/openpype/plugins/load/delete_old_versions.py +++ b/openpype/plugins/load/delete_old_versions.py @@ -8,13 +8,14 @@ import ftrack_api import qargparse from Qt import QtWidgets, QtCore -from avalon import api, style +from avalon import style from avalon.api import AvalonMongoDB -import avalon.pipeline +from openpype.pipeline import load +from openpype.lib import StringTemplate from openpype.api import Anatomy -class DeleteOldVersions(api.SubsetLoader): +class DeleteOldVersions(load.SubsetLoaderPlugin): """Deletes specific number of old version""" is_multiple_contexts_compatible = True @@ -89,16 +90,12 @@ class DeleteOldVersions(api.SubsetLoader): try: context = representation["context"] context["root"] = anatomy.roots - path = avalon.pipeline.format_template_with_optional_keys( - context, template - ) + path = str(StringTemplate.format_template(template, context)) if "frame" in context: context["frame"] = self.sequence_splitter - sequence_path = os.path.normpath( - avalon.pipeline.format_template_with_optional_keys( - context, template - ) - ) + sequence_path = os.path.normpath(str( + StringTemplate.format_template(template, context) + )) except KeyError: # Template references unavailable data diff --git a/openpype/plugins/load/delivery.py b/openpype/plugins/load/delivery.py index 1037d6dc16..04080053e3 100644 --- a/openpype/plugins/load/delivery.py +++ b/openpype/plugins/load/delivery.py @@ -3,9 +3,9 @@ from collections import defaultdict from Qt import QtWidgets, QtCore, QtGui -from avalon import api from avalon.api import AvalonMongoDB +from openpype.pipeline import load from openpype.api import Anatomy, config from openpype import resources, style @@ -20,7 +20,7 @@ from openpype.lib.delivery import ( ) -class Delivery(api.SubsetLoader): +class Delivery(load.SubsetLoaderPlugin): """Export selected versions to folder structure from Template""" is_multiple_contexts_compatible = True diff --git a/openpype/plugins/load/open_djv.py b/openpype/plugins/load/open_djv.py index 4b0e8411c8..273c77c93f 100644 --- a/openpype/plugins/load/open_djv.py +++ b/openpype/plugins/load/open_djv.py @@ -1,6 +1,6 @@ import os -from avalon import api from openpype.api import ApplicationManager +from openpype.pipeline import load def existing_djv_path(): @@ -13,7 +13,8 @@ def existing_djv_path(): return djv_list -class OpenInDJV(api.Loader): + +class OpenInDJV(load.LoaderPlugin): """Open Image Sequence with system default""" djv_list = existing_djv_path() diff --git a/openpype/plugins/load/open_file.py b/openpype/plugins/load/open_file.py index 4133a64eb3..f21cd07c7f 100644 --- a/openpype/plugins/load/open_file.py +++ b/openpype/plugins/load/open_file.py @@ -2,7 +2,7 @@ import sys import os import subprocess -from avalon import api +from openpype.pipeline import load def open(filepath): @@ -15,7 +15,7 @@ def open(filepath): subprocess.call(('xdg-open', filepath)) -class Openfile(api.Loader): +class Openfile(load.LoaderPlugin): """Open Image Sequence with system default""" families = ["render2d"] diff --git a/openpype/plugins/load/remove_site.py b/openpype/plugins/load/remove_site.py index aedb5d1f2f..adffec9986 100644 --- a/openpype/plugins/load/remove_site.py +++ b/openpype/plugins/load/remove_site.py @@ -1,8 +1,8 @@ -from avalon import api from openpype.modules import ModulesManager +from openpype.pipeline import load -class RemoveSyncSite(api.Loader): +class RemoveSyncSite(load.LoaderPlugin): """Remove sync site and its files on representation""" representations = ["*"] families = ["*"] diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index baee569239..dc3cda1725 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -10,7 +10,7 @@ from avalon import ( from Qt import QtCore, QtGui import qtawesome -from avalon.lib import HeroVersionType +from openpype.pipeline import HeroVersionType from openpype.tools.utils.models import TreeModel, Item from openpype.tools.utils import lib diff --git a/openpype/tools/loader/widgets.py b/openpype/tools/loader/widgets.py index f145756cc5..b14bdd0e93 100644 --- a/openpype/tools/loader/widgets.py +++ b/openpype/tools/loader/widgets.py @@ -1,6 +1,5 @@ import os import sys -import inspect import datetime import pprint import traceback @@ -9,8 +8,19 @@ import collections from Qt import QtWidgets, QtCore, QtGui from avalon import api, pipeline -from avalon.lib import HeroVersionType +from openpype.pipeline import HeroVersionType +from openpype.pipeline.load import ( + discover_loader_plugins, + SubsetLoaderPlugin, + loaders_from_repre_context, + get_repres_contexts, + get_subset_contexts, + load_with_repre_context, + load_with_subset_context, + load_with_subset_contexts, + IncompatibleLoaderError, +) from openpype.tools.utils import ( ErrorMessageBox, lib as tools_lib @@ -425,7 +435,7 @@ class SubsetWidget(QtWidgets.QWidget): # Get all representation->loader combinations available for the # index under the cursor, so we can list the user the options. - available_loaders = api.discover(api.Loader) + available_loaders = discover_loader_plugins() if self.tool_name: available_loaders = lib.remove_tool_name_from_loaders( available_loaders, self.tool_name @@ -435,7 +445,7 @@ class SubsetWidget(QtWidgets.QWidget): subset_loaders = [] for loader in available_loaders: # Skip if its a SubsetLoader. - if api.SubsetLoader in inspect.getmro(loader): + if issubclass(loader, SubsetLoaderPlugin): subset_loaders.append(loader) else: repre_loaders.append(loader) @@ -459,7 +469,7 @@ class SubsetWidget(QtWidgets.QWidget): repre_docs = repre_docs_by_version_id[version_id] for repre_doc in repre_docs: repre_context = repre_context_by_id[repre_doc["_id"]] - for loader in pipeline.loaders_from_repre_context( + for loader in loaders_from_repre_context( repre_loaders, repre_context ): @@ -515,7 +525,7 @@ class SubsetWidget(QtWidgets.QWidget): action = lib.get_no_loader_action(menu, one_item_selected) menu.addAction(action) else: - repre_contexts = pipeline.get_repres_contexts( + repre_contexts = get_repres_contexts( repre_context_by_id.keys(), self.dbcon) menu = lib.add_representation_loaders_to_menu( @@ -532,7 +542,7 @@ class SubsetWidget(QtWidgets.QWidget): self.load_started.emit() - if api.SubsetLoader in inspect.getmro(loader): + if issubclass(loader, SubsetLoaderPlugin): subset_ids = [] subset_version_docs = {} for item in items: @@ -541,8 +551,7 @@ class SubsetWidget(QtWidgets.QWidget): subset_version_docs[subset_id] = item["version_document"] # get contexts only for selected menu option - subset_contexts_by_id = pipeline.get_subset_contexts(subset_ids, - self.dbcon) + subset_contexts_by_id = get_subset_contexts(subset_ids, self.dbcon) subset_contexts = list(subset_contexts_by_id.values()) options = lib.get_options(action, loader, self, subset_contexts) @@ -575,8 +584,7 @@ class SubsetWidget(QtWidgets.QWidget): repre_ids.append(representation["_id"]) # get contexts only for selected menu option - repre_contexts = pipeline.get_repres_contexts(repre_ids, - self.dbcon) + repre_contexts = get_repres_contexts(repre_ids, self.dbcon) options = lib.get_options(action, loader, self, list(repre_contexts.values())) @@ -1339,12 +1347,12 @@ class RepresentationWidget(QtWidgets.QWidget): selected_side = self._get_selected_side(point_index, rows) # Get all representation->loader combinations available for the # index under the cursor, so we can list the user the options. - available_loaders = api.discover(api.Loader) + available_loaders = discover_loader_plugins() filtered_loaders = [] for loader in available_loaders: # Skip subset loaders - if api.SubsetLoader in inspect.getmro(loader): + if issubclass(loader, SubsetLoaderPlugin): continue if ( @@ -1370,7 +1378,7 @@ class RepresentationWidget(QtWidgets.QWidget): for item in items: repre_context = repre_context_by_id[item["_id"]] - for loader in pipeline.loaders_from_repre_context( + for loader in loaders_from_repre_context( filtered_loaders, repre_context ): @@ -1426,7 +1434,7 @@ class RepresentationWidget(QtWidgets.QWidget): action = lib.get_no_loader_action(menu) menu.addAction(action) else: - repre_contexts = pipeline.get_repres_contexts( + repre_contexts = get_repres_contexts( repre_context_by_id.keys(), self.dbcon) menu = lib.add_representation_loaders_to_menu(loaders, menu, repre_contexts) @@ -1472,8 +1480,7 @@ class RepresentationWidget(QtWidgets.QWidget): repre_ids.append(item.get("_id")) - repre_contexts = pipeline.get_repres_contexts(repre_ids, - self.dbcon) + repre_contexts = get_repres_contexts(repre_ids, self.dbcon) options = lib.get_options(action, loader, self, list(repre_contexts.values())) @@ -1540,7 +1547,7 @@ def _load_representations_by_loader(loader, repre_contexts, """Loops through list of repre_contexts and loads them with one loader Args: - loader (cls of api.Loader) - not initialized yet + loader (cls of LoaderPlugin) - not initialized yet repre_contexts (dicts) - full info about selected representations (containing repre_doc, version_doc, subset_doc, project info) options (dict) - qargparse arguments to fill OptionDialog @@ -1558,12 +1565,12 @@ def _load_representations_by_loader(loader, repre_contexts, _id = repre_context["representation"]["_id"] data = data_by_repre_id.get(_id) options.update(data) - pipeline.load_with_repre_context( + load_with_repre_context( loader, repre_context, options=options ) - except pipeline.IncompatibleLoaderError as exc: + except IncompatibleLoaderError as exc: print(exc) error_info.append(( "Incompatible Loader", @@ -1612,7 +1619,7 @@ def _load_subsets_by_loader(loader, subset_contexts, options, context["version"] = subset_version_docs[context["subset"]["_id"]] try: - pipeline.load_with_subset_contexts( + load_with_subset_contexts( loader, subset_contexts, options=options @@ -1638,7 +1645,7 @@ def _load_subsets_by_loader(loader, subset_contexts, options, version_doc = subset_version_docs[subset_context["subset"]["_id"]] subset_context["version"] = version_doc try: - pipeline.load_with_subset_context( + load_with_subset_context( loader, subset_context, options=options diff --git a/openpype/tools/mayalookassigner/commands.py b/openpype/tools/mayalookassigner/commands.py index 96fc28243b..df72e41354 100644 --- a/openpype/tools/mayalookassigner/commands.py +++ b/openpype/tools/mayalookassigner/commands.py @@ -4,10 +4,11 @@ import os import maya.cmds as cmds -from openpype.hosts.maya.api import lib - from avalon import io, api +from openpype.pipeline import remove_container +from openpype.hosts.maya.api import lib + from .vray_proxies import get_alembic_ids_cache log = logging.getLogger(__name__) @@ -206,6 +207,6 @@ def remove_unused_looks(): for container in unused: log.info("Removing unused look container: %s", container['objectName']) - api.remove(container) + remove_container(container) log.info("Finished removing unused looks. (see log for details)") diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index b22ec95a4d..3179ba1445 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -12,6 +12,12 @@ from maya import cmds from avalon import io, api +from openpype.pipeline import ( + load_representation, + loaders_from_representation, + discover_loader_plugins, + get_representation_path, +) from openpype.hosts.maya.api import lib @@ -155,7 +161,7 @@ def get_look_relationships(version_id): "name": "json"}) # Load relationships - shader_relation = api.get_representation_path(json_representation) + shader_relation = get_representation_path(json_representation) with open(shader_relation, "r") as f: relationships = json.load(f) @@ -193,8 +199,8 @@ def load_look(version_id): log.info("Using look for the first time ...") # Load file - loaders = api.loaders_from_representation(api.discover(api.Loader), - representation_id) + all_loaders = discover_loader_plugins() + loaders = loaders_from_representation(all_loaders, representation_id) loader = next( (i for i in loaders if i.__name__ == "LookLoader"), None) if loader is None: @@ -202,7 +208,7 @@ def load_look(version_id): # Reference the look file with lib.maintained_selection(): - container_node = api.load(loader, look_representation) + container_node = load_representation(loader, look_representation) # Get container members shader_nodes = lib.get_container_members(container_node) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index cba60be355..85bf18fbb7 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -7,8 +7,9 @@ from Qt import QtCore, QtGui import qtawesome from avalon import api, io, style, schema -from avalon.lib import HeroVersionType +from openpype.pipeline import HeroVersionType from openpype.tools.utils.models import TreeModel, Item +from openpype.modules import ModulesManager from .lib import ( get_site_icons, @@ -16,8 +17,6 @@ from .lib import ( get_progress_for_repre ) -from openpype.modules import ModulesManager - class InventoryModel(TreeModel): """The model for the inventory""" diff --git a/openpype/tools/sceneinventory/switch_dialog.py b/openpype/tools/sceneinventory/switch_dialog.py index 93ea68beb4..0e7b1b759a 100644 --- a/openpype/tools/sceneinventory/switch_dialog.py +++ b/openpype/tools/sceneinventory/switch_dialog.py @@ -3,7 +3,12 @@ import logging from Qt import QtWidgets, QtCore import qtawesome -from avalon import io, api, pipeline +from avalon import io, pipeline +from openpype.pipeline import ( + discover_loader_plugins, + switch_container, + get_repres_contexts, +) from .widgets import ( ButtonWithMenu, @@ -343,13 +348,13 @@ class SwitchAssetDialog(QtWidgets.QDialog): def _get_loaders(self, repre_ids): repre_contexts = None if repre_ids: - repre_contexts = pipeline.get_repres_contexts(repre_ids) + repre_contexts = get_repres_contexts(repre_ids) if not repre_contexts: return list() available_loaders = [] - for loader_plugin in api.discover(api.Loader): + for loader_plugin in discover_loader_plugins(): # Skip loaders without switch method if not hasattr(loader_plugin, "switch"): continue @@ -1352,7 +1357,7 @@ class SwitchAssetDialog(QtWidgets.QDialog): repre_doc = repres_by_name[container_repre_name] try: - api.switch(container, repre_doc, loader) + switch_container(container, repre_doc, loader) except Exception: msg = ( "Couldn't switch asset." diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 32c1883de6..fa61ec4384 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -6,8 +6,12 @@ from Qt import QtWidgets, QtCore import qtawesome from avalon import io, api, style -from avalon.lib import HeroVersionType +from openpype.pipeline import ( + HeroVersionType, + update_container, + remove_container, +) from openpype.modules import ModulesManager from openpype.tools.utils.lib import ( get_progress_for_repre, @@ -195,7 +199,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version_name = version_name_by_id.get(version_id) if version_name is not None: try: - api.update(item, version_name) + update_container(item, version_name) except AssertionError: self._show_version_error_dialog( version_name, [item] @@ -223,7 +227,7 @@ class SceneInventoryView(QtWidgets.QTreeView): def _on_update_to_latest(items): for item in items: try: - api.update(item, -1) + update_container(item, -1) except AssertionError: self._show_version_error_dialog(None, [item]) log.warning("Update failed", exc_info=True) @@ -248,7 +252,7 @@ class SceneInventoryView(QtWidgets.QTreeView): def _on_update_to_hero(items): for item in items: try: - api.update(item, HeroVersionType(-1)) + update_container(item, HeroVersionType(-1)) except AssertionError: self._show_version_error_dialog('hero', [item]) log.warning("Update failed", exc_info=True) @@ -727,7 +731,7 @@ class SceneInventoryView(QtWidgets.QTreeView): version = versions_by_label[label] for item in items: try: - api.update(item, version) + update_container(item, version) except AssertionError: self._show_version_error_dialog(version, [item]) log.warning("Update failed", exc_info=True) @@ -758,7 +762,7 @@ class SceneInventoryView(QtWidgets.QTreeView): return for item in items: - api.remove(item) + remove_container(item) self.data_changed.emit() def _show_version_error_dialog(self, version, items): @@ -828,7 +832,7 @@ class SceneInventoryView(QtWidgets.QTreeView): # Trigger update to latest for item in outdated_items: try: - api.update(item, -1) + update_container(item, -1) except AssertionError: self._show_version_error_dialog(None, [item]) log.warning("Update failed", exc_info=True) diff --git a/openpype/tools/utils/delegates.py b/openpype/tools/utils/delegates.py index 4ec6079bb7..d3718b1734 100644 --- a/openpype/tools/utils/delegates.py +++ b/openpype/tools/utils/delegates.py @@ -6,7 +6,7 @@ import numbers import Qt from Qt import QtWidgets, QtGui, QtCore -from avalon.lib import HeroVersionType +from openpype.pipeline import HeroVersionType from .models import TreeModel from . import lib From d4434fd1b71e5e492119132e2b329f4f3e9dac52 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 11:42:02 +0100 Subject: [PATCH 207/302] use 'LegacyCreator' instead of 'avalon.api.Creator' --- openpype/lib/avalon_context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 67a5515100..03ad69a5e6 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1594,11 +1594,13 @@ def get_creator_by_name(creator_name, case_sensitive=False): Returns: Creator: Return first matching plugin or `None`. """ + from openpype.pipeline import LegacyCreator + # Lower input creator name if is not case sensitive if not case_sensitive: creator_name = creator_name.lower() - for creator_plugin in avalon.api.discover(avalon.api.Creator): + for creator_plugin in avalon.api.discover(LegacyCreator): _creator_name = creator_plugin.__name__ # Lower creator plugin name if is not case sensitive From a6fb84f3e185b90a8daeef56a344d99a97ddf475 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 11:45:29 +0100 Subject: [PATCH 208/302] flame: single file in representation if mov or thumb --- .../hosts/flame/plugins/publish/extract_subset_resources.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index bfd723f5d8..2d1cbb951d 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -217,7 +217,11 @@ class ExtractSubsetResources(openpype.api.Extractor): # add files to represetation but add # imagesequence as list if ( - "movie_file" in preset_path + # first check if path in files is not mov extension + next([ + f for f in files if ".mov" in os.path.splitext(f)[-1] + ], None) + # then try if thumbnail is not in unique name or unique_name == "thumbnail" ): representation_data["files"] = files.pop() From 8adc26c27804a8cd200e72537e70a6014ee2fd27 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 11:51:54 +0100 Subject: [PATCH 209/302] flame: fix multiple video files in list of repre files --- .../plugins/publish/extract_subset_resources.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 2d1cbb951d..5c3aed9672 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -218,9 +218,16 @@ class ExtractSubsetResources(openpype.api.Extractor): # imagesequence as list if ( # first check if path in files is not mov extension - next([ - f for f in files if ".mov" in os.path.splitext(f)[-1] - ], None) + next( + # iter all paths in files + # return only .mov positive test + iter([ + f for f in files + if ".mov" in os.path.splitext(f)[-1] + ]), + # if nothing return default + None + ) # then try if thumbnail is not in unique name or unique_name == "thumbnail" ): From 392963032732c8248b5c66d03b731d2ef5468237 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 14 Mar 2022 12:06:59 +0100 Subject: [PATCH 210/302] OP-2813 - fix hardcoded value Updated regular expression to match version substring to be more generic. --- openpype/lib/delivery.py | 12 ++++--- tests/unit/openpype/lib/test_delivery.py | 40 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index 5a69afd5aa..ee21b01854 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -4,13 +4,18 @@ import shutil import glob import clique import collections +import re def collect_frames(files): """ Returns dict of source path and its frame, if from sequence - Uses clique as most precise solution + Uses clique as most precise solution, used when anatomy template that + created files is not known. + + Depends that version substring starts with 'v' with any number of + numeric characters after. Args: files(list) or (set with single value): list of source paths @@ -29,9 +34,8 @@ def collect_frames(files): src_head = collection.head src_tail = collection.tail - if src_head.endswith("_v"): - # print("Collection gathered incorrectly, not a sequence " - # "just a version found in {}".format(files)) + # version recognized as a collection + if re.match(".*([^a-zA-Z0-9]v%[0-9]+d).*", collection.format()): if len(collections) > 1: continue else: diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py index 7c2c92c101..1787286032 100644 --- a/tests/unit/openpype/lib/test_delivery.py +++ b/tests/unit/openpype/lib/test_delivery.py @@ -19,6 +19,22 @@ def test_collect_frames_multi_sequence(): assert ret == expected, "Not matching" +def test_collect_frames_multi_sequence_different_format(): + files = ["Asset.v001.renderCompositingMain.0000.png", + "Asset.v001.renderCompositingMain.0001.png", + "Asset.v001.renderCompositingMain.0002.png"] + ret = collect_frames(files) + + expected = { + "Asset.v001.renderCompositingMain.0000.png": "0000", + "Asset.v001.renderCompositingMain.0001.png": "0001", + "Asset.v001.renderCompositingMain.0002.png": "0002" + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence(): files = ["Asset_renderCompositingMain_v001.0000.png"] ret = collect_frames(files) @@ -31,6 +47,30 @@ def test_collect_frames_single_sequence(): assert ret == expected, "Not matching" +def test_collect_frames_single_sequence_different_format(): + files = ["Asset.v001.renderCompositingMain_0000.png"] + ret = collect_frames(files) + + expected = { + "Asset.v001.renderCompositingMain_0000.png": "0000" + } + + print(ret) + assert ret == expected, "Not matching" + + +def test_collect_frames_single_sequence_withhout_version(): + files = ["pngv001.renderCompositingMain_0000.png"] + ret = collect_frames(files) + + expected = { + "pngv001.renderCompositingMain_0000.png": "0000" + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence_as_dict(): files = {"Asset_renderCompositingMain_v001.0000.png"} ret = collect_frames(files) From 3d01ba27113eb82901b4a7aa5a550a55932125fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 13:00:27 +0100 Subject: [PATCH 211/302] define class attribute '_representations' loaded from settings --- openpype/hosts/nuke/plugins/load/load_clip.py | 3 +++ openpype/hosts/nuke/plugins/load/load_image.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 563a325a83..2b4315a830 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -42,6 +42,9 @@ class LoadClip(plugin.NukeLoader): icon = "file-video-o" color = "white" + # Loaded from settings + _representations = [] + script_start = int(nuke.root()["first_frame"].value()) # option gui diff --git a/openpype/hosts/nuke/plugins/load/load_image.py b/openpype/hosts/nuke/plugins/load/load_image.py index e04ccf3bf1..9a175a0cba 100644 --- a/openpype/hosts/nuke/plugins/load/load_image.py +++ b/openpype/hosts/nuke/plugins/load/load_image.py @@ -36,6 +36,9 @@ class LoadImage(load.LoaderPlugin): icon = "image" color = "white" + # Loaded from settings + _representations = [] + node_name_template = "{class_name}_{ext}" options = [ From 4e83ff6c27940fac9ca38d6c6a9df8d8b8712eed Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 14:03:51 +0100 Subject: [PATCH 212/302] moved avalon.api imports into functions using them --- openpype/pipeline/load/plugins.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index d7e21e1248..ea92cf962b 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -1,13 +1,5 @@ import logging -from avalon.api import ( - discover, - register_plugin, - deregister_plugin, - register_plugin_path, - deregister_plugin_path, -) - from .utils import get_representation_path_from_context @@ -110,20 +102,29 @@ class SubsetLoaderPlugin(LoaderPlugin): def discover_loader_plugins(): - return discover(LoaderPlugin) + import avalon.api + + return avalon.api.discover(LoaderPlugin) def register_loader_plugin(plugin): - return register_plugin(LoaderPlugin, plugin) + import avalon.api + + return avalon.api.register_plugin(LoaderPlugin, plugin) def deregister_loader_plugins_path(path): - deregister_plugin_path(LoaderPlugin, path) + import avalon.api + + avalon.api.deregister_plugin_path(LoaderPlugin, path) def register_loader_plugins_path(path): - return register_plugin_path(LoaderPlugin, path) + import avalon.api + + return avalon.apiregister_plugin_path(LoaderPlugin, path) def deregister_loader_plugin(plugin): - deregister_plugin(LoaderPlugin, plugin) + import avalon.api + avalon.api.deregister_plugin(LoaderPlugin, plugin) From 105d794358e97dbba654b38383c91dc366ddc0d1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 14:04:37 +0100 Subject: [PATCH 213/302] fix typo --- openpype/pipeline/load/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index ea92cf962b..5648236739 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -122,7 +122,7 @@ def deregister_loader_plugins_path(path): def register_loader_plugins_path(path): import avalon.api - return avalon.apiregister_plugin_path(LoaderPlugin, path) + return avalon.api.register_plugin_path(LoaderPlugin, path) def deregister_loader_plugin(plugin): From d5c35d18730ca35eee0d01e7d5e235380adde6c3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 15:20:20 +0100 Subject: [PATCH 214/302] use color hex instead of constans in nuke plugins --- openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py | 4 ++-- openpype/hosts/nuke/plugins/load/load_backdrop.py | 4 ++-- openpype/hosts/nuke/plugins/load/load_effects.py | 5 +++-- openpype/hosts/nuke/plugins/load/load_effects_ip.py | 5 +++-- openpype/hosts/nuke/plugins/load/load_gizmo.py | 5 +++-- openpype/hosts/nuke/plugins/load/load_gizmo_ip.py | 5 +++-- openpype/hosts/nuke/plugins/load/load_script_precomp.py | 5 +++-- 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py index 49405fd213..5f834be557 100644 --- a/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py +++ b/openpype/hosts/nuke/plugins/inventory/repair_old_loaders.py @@ -1,4 +1,4 @@ -from avalon import api, style +from avalon import api from openpype.api import Logger from openpype.hosts.nuke.api.lib import set_avalon_knob_data @@ -7,7 +7,7 @@ class RepairOldLoaders(api.InventoryAction): label = "Repair Old Loaders" icon = "gears" - color = style.colors.alert + color = "#cc0000" log = Logger.get_logger(__name__) diff --git a/openpype/hosts/nuke/plugins/load/load_backdrop.py b/openpype/hosts/nuke/plugins/load/load_backdrop.py index 6619cfb414..58ebcc7d49 100644 --- a/openpype/hosts/nuke/plugins/load/load_backdrop.py +++ b/openpype/hosts/nuke/plugins/load/load_backdrop.py @@ -1,4 +1,4 @@ -from avalon import api, style, io +from avalon import api, io import nuke import nukescripts @@ -23,7 +23,7 @@ class LoadBackdropNodes(api.Loader): label = "Iport Nuke Nodes" order = 0 icon = "eye" - color = style.colors.light + color = "white" node_color = "0x7533c1ff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_effects.py b/openpype/hosts/nuke/plugins/load/load_effects.py index f636c6b510..4d83da1a78 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects.py +++ b/openpype/hosts/nuke/plugins/load/load_effects.py @@ -1,7 +1,8 @@ import json from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api import ( containerise, update_container, @@ -18,7 +19,7 @@ class LoadEffects(api.Loader): label = "Load Effects - nodes" order = 0 icon = "cc" - color = style.colors.light + color = "white" ignore_attr = ["useLifetime"] diff --git a/openpype/hosts/nuke/plugins/load/load_effects_ip.py b/openpype/hosts/nuke/plugins/load/load_effects_ip.py index 990bce54f1..4d30e0f93c 100644 --- a/openpype/hosts/nuke/plugins/load/load_effects_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_effects_ip.py @@ -3,7 +3,8 @@ from collections import OrderedDict import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api import lib from openpype.hosts.nuke.api import ( containerise, @@ -21,7 +22,7 @@ class LoadEffectsInputProcess(api.Loader): label = "Load Effects - Input Process" order = 0 icon = "eye" - color = style.colors.alert + color = "#cc0000" ignore_attr = ["useLifetime"] def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo.py b/openpype/hosts/nuke/plugins/load/load_gizmo.py index 659977d789..9c726d8fe6 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo.py @@ -1,5 +1,6 @@ import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api.lib import ( maintained_selection, get_avalon_knob_data, @@ -21,7 +22,7 @@ class LoadGizmo(api.Loader): label = "Load Gizmo" order = 0 icon = "dropbox" - color = style.colors.light + color = "white" node_color = "0x75338eff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py index 240bfd467d..78d2625758 100644 --- a/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py +++ b/openpype/hosts/nuke/plugins/load/load_gizmo_ip.py @@ -1,5 +1,6 @@ -from avalon import api, style, io +from avalon import api, io import nuke + from openpype.hosts.nuke.api.lib import ( maintained_selection, create_backdrop, @@ -22,7 +23,7 @@ class LoadGizmoInputProcess(api.Loader): label = "Load Gizmo - Input Process" order = 0 icon = "eye" - color = style.colors.alert + color = "#cc0000" node_color = "0x7533c1ff" def load(self, context, name, namespace, data): diff --git a/openpype/hosts/nuke/plugins/load/load_script_precomp.py b/openpype/hosts/nuke/plugins/load/load_script_precomp.py index aa48b631c5..48bf0b889f 100644 --- a/openpype/hosts/nuke/plugins/load/load_script_precomp.py +++ b/openpype/hosts/nuke/plugins/load/load_script_precomp.py @@ -1,5 +1,6 @@ import nuke -from avalon import api, style, io +from avalon import api, io + from openpype.hosts.nuke.api.lib import get_avalon_knob_data from openpype.hosts.nuke.api import ( containerise, @@ -17,7 +18,7 @@ class LinkAsGroup(api.Loader): label = "Load Precomp" order = 0 icon = "file" - color = style.colors.alert + color = "#cc0000" def load(self, context, name, namespace, data): # for k, v in context.items(): From 239badf4d32f9d1b2a8144957c9e47514562d833 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 15:24:40 +0100 Subject: [PATCH 215/302] added functions to get colors from style data --- openpype/style/__init__.py | 83 ++++++++++++++++++++++++++++++-------- openpype/style/data.json | 6 +++ 2 files changed, 73 insertions(+), 16 deletions(-) diff --git a/openpype/style/__init__.py b/openpype/style/__init__.py index ea88b342ee..d92e18c0cd 100644 --- a/openpype/style/__init__.py +++ b/openpype/style/__init__.py @@ -7,13 +7,30 @@ from openpype import resources from .color_defs import parse_color - -_STYLESHEET_CACHE = None -_FONT_IDS = None - current_dir = os.path.dirname(os.path.abspath(__file__)) +# Default colors +# - default color used in tool icons +_TOOLS_ICON_COLOR = "#ffffff" +# - entities icon color - use 'get_default_asset_icon_color' +_DEFAULT_ENTITY_ICON_COLOR = "#fb9c15" +# - disabled entitie +_DISABLED_ENTITY_ICON_ICON_COLOR = "#808080" +# - deprecated entity font color +_DEPRECATED_ENTITY_FONT_COLOR = "#666666" + + +class _Cache: + stylesheet = None + font_ids = None + + tools_icon_color = None + default_entity_icon_color = None + disabled_entity_icon_color = None + deprecated_entity_font_color = None + + def get_style_image_path(image_name): # All filenames are lowered image_name = image_name.lower() @@ -125,21 +142,19 @@ def _load_font(): """Load and register fonts into Qt application.""" from Qt import QtGui - global _FONT_IDS - # Check if font ids are still loaded - if _FONT_IDS is not None: - for font_id in tuple(_FONT_IDS): + if _Cache.font_ids is not None: + for font_id in tuple(_Cache.font_ids): font_families = QtGui.QFontDatabase.applicationFontFamilies( font_id ) # Reset font if font id is not available if not font_families: - _FONT_IDS = None + _Cache.font_ids = None break - if _FONT_IDS is None: - _FONT_IDS = [] + if _Cache.font_ids is None: + _Cache.font_ids = [] fonts_dirpath = os.path.join(current_dir, "fonts") font_dirs = [] font_dirs.append(os.path.join(fonts_dirpath, "Noto_Sans")) @@ -157,7 +172,7 @@ def _load_font(): continue full_path = os.path.join(font_dir, filename) font_id = QtGui.QFontDatabase.addApplicationFont(full_path) - _FONT_IDS.append(font_id) + _Cache.font_ids.append(font_id) font_families = QtGui.QFontDatabase.applicationFontFamilies( font_id ) @@ -167,11 +182,11 @@ def _load_font(): def load_stylesheet(): """Load and return OpenPype Qt stylesheet.""" - global _STYLESHEET_CACHE - if _STYLESHEET_CACHE is None: - _STYLESHEET_CACHE = _load_stylesheet() + + if _Cache.stylesheet is None: + _Cache.stylesheet = _load_stylesheet() _load_font() - return _STYLESHEET_CACHE + return _Cache.stylesheet def get_app_icon_path(): @@ -182,3 +197,39 @@ def get_app_icon_path(): def app_icon_path(): # Backwards compatibility return get_app_icon_path() + + +def get_default_tools_icon_color(): + if _Cache.tools_icon_color is None: + color_data = get_colors_data() + color = color_data.get("icon-tools") + _Cache.tools_icon_color = color or _TOOLS_ICON_COLOR + return _Cache.tools_icon_color + + +def get_default_entity_icon_color(): + if _Cache.default_entity_icon_color is None: + color_data = get_colors_data() + color = color_data.get("icon-entity-default") + _Cache.default_entity_icon_color = color or _DEFAULT_ENTITY_ICON_COLOR + return _Cache.default_entity_icon_color + + +def get_disabled_entity_icon_color(): + if _Cache.disabled_entity_icon_color is None: + color_data = get_colors_data() + color = color_data.get("icon-entity-disabled") + _Cache.disabled_entity_icon_color = ( + color or _DISABLED_ENTITY_ICON_ICON_COLOR + ) + return _Cache.disabled_entity_icon_color + + +def get_deprecated_entity_font_color(): + if _Cache.deprecated_entity_font_color is None: + color_data = get_colors_data() + color = color_data.get("font-entity-deprecated") + _Cache.deprecated_entity_font_color = ( + color or _DEPRECATED_ENTITY_FONT_COLOR + ) + return _Cache.deprecated_entity_font_color diff --git a/openpype/style/data.json b/openpype/style/data.json index b8ccef8bbd..2af23acd0d 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -56,6 +56,12 @@ "delete-btn-bg": "rgb(201, 54, 54)", "delete-btn-bg-disabled": "rgba(201, 54, 54, 64)", + "icon-tools": "#ffffff", + "icon-alert-tools": "#AA5050", + "icon-entity-default": "#fb9c15", + "icon-entity-disabled": "#808080", + "font-entity-deprecated": "#666666", + "tab-widget": { "bg": "#21252B", "bg-selected": "#434a56", From 8d81a91c1cce37c85f0e39f091f30013f2d2db34 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 15:24:56 +0100 Subject: [PATCH 216/302] use openpype style in delete old versions --- openpype/plugins/load/delete_old_versions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/load/delete_old_versions.py b/openpype/plugins/load/delete_old_versions.py index e8612745fb..fb8be0ed33 100644 --- a/openpype/plugins/load/delete_old_versions.py +++ b/openpype/plugins/load/delete_old_versions.py @@ -8,9 +8,10 @@ import ftrack_api import qargparse from Qt import QtWidgets, QtCore -from avalon import api, style +from avalon import api from avalon.api import AvalonMongoDB import avalon.pipeline +from openpype import style from openpype.api import Anatomy From 9bbaf42a3bb10cb56360f6f9a223c8428f855ce3 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 15:26:08 +0100 Subject: [PATCH 217/302] use functions for colors in tools --- openpype/plugins/load/copy_file.py | 5 ++-- openpype/tools/loader/model.py | 19 ++++++++------- openpype/tools/mayalookassigner/models.py | 13 ++++++++--- openpype/tools/sceneinventory/model.py | 7 ++++-- .../standalonepublish/widgets/model_asset.py | 23 +++++++++++++++---- .../widgets/model_tasks_template.py | 6 +++-- .../standalonepublish/widgets/widget_asset.py | 6 +++-- openpype/tools/utils/assets_widget.py | 13 +++++++---- openpype/tools/utils/lib.py | 13 +++++++---- openpype/tools/utils/tasks_widget.py | 14 +++++++---- openpype/tools/workfiles/model.py | 16 ++++++++++--- 11 files changed, 93 insertions(+), 42 deletions(-) diff --git a/openpype/plugins/load/copy_file.py b/openpype/plugins/load/copy_file.py index eaf5853035..bdcb4fec79 100644 --- a/openpype/plugins/load/copy_file.py +++ b/openpype/plugins/load/copy_file.py @@ -1,4 +1,5 @@ -from avalon import api, style +from avalon import api +from openpype.style import get_default_entity_icon_color class CopyFile(api.Loader): @@ -10,7 +11,7 @@ class CopyFile(api.Loader): label = "Copy File" order = 10 icon = "copy" - color = style.colors.default + color = get_default_entity_icon_color() def load(self, context, name=None, namespace=None, data=None): self.log.info("Added copy to clipboard: {0}".format(self.fname)) diff --git a/openpype/tools/loader/model.py b/openpype/tools/loader/model.py index baee569239..1007355989 100644 --- a/openpype/tools/loader/model.py +++ b/openpype/tools/loader/model.py @@ -3,15 +3,13 @@ import re import math from uuid import uuid4 -from avalon import ( - style, - schema -) from Qt import QtCore, QtGui import qtawesome +from avalon import schema from avalon.lib import HeroVersionType +from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from openpype.tools.utils import lib @@ -180,7 +178,10 @@ class SubsetsModel(TreeModel, BaseRepresentationModel): self._sorter = None self._grouping = grouping self._icons = { - "subset": qtawesome.icon("fa.file-o", color=style.colors.default) + "subset": qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) } self._items_by_id = {} @@ -1066,8 +1067,10 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): self._docs = {} self._icons = lib.get_repre_icons() - self._icons["repre"] = qtawesome.icon("fa.file-o", - color=style.colors.default) + self._icons["repre"] = qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) self._items_by_id = {} def set_version_ids(self, version_ids): @@ -1165,7 +1168,7 @@ class RepresentationModel(TreeModel, BaseRepresentationModel): "remote_site_name": self.remote_site, "icon": qtawesome.icon( "fa.folder", - color=style.colors.default + color=get_default_entity_icon_color() ) }) self._items_by_id[item_id] = group_item diff --git a/openpype/tools/mayalookassigner/models.py b/openpype/tools/mayalookassigner/models.py index 386b7d7e1e..77a3c8a590 100644 --- a/openpype/tools/mayalookassigner/models.py +++ b/openpype/tools/mayalookassigner/models.py @@ -3,14 +3,19 @@ from collections import defaultdict from Qt import QtCore import qtawesome -from avalon.style import colors from openpype.tools.utils import models +from openpype.style import get_default_entity_icon_color class AssetModel(models.TreeModel): Columns = ["label"] + def __init__(self, *args, **kwargs): + super(AssetModel, self).__init__(*args, **kwargs) + + self._icon_color = get_default_entity_icon_color() + def add_items(self, items): """ Add items to model with needed data @@ -65,8 +70,10 @@ class AssetModel(models.TreeModel): node = index.internalPointer() icon = node.get("icon") if icon: - return qtawesome.icon("fa.{0}".format(icon), - color=colors.default) + return qtawesome.icon( + "fa.{0}".format(icon), + color=self._icon_color + ) return super(AssetModel, self).data(index, role) diff --git a/openpype/tools/sceneinventory/model.py b/openpype/tools/sceneinventory/model.py index cba60be355..6ec3601705 100644 --- a/openpype/tools/sceneinventory/model.py +++ b/openpype/tools/sceneinventory/model.py @@ -6,8 +6,9 @@ from collections import defaultdict from Qt import QtCore, QtGui import qtawesome -from avalon import api, io, style, schema +from avalon import api, io, schema from avalon.lib import HeroVersionType +from openpype.style import get_default_entity_icon_color from openpype.tools.utils.models import TreeModel, Item from .lib import ( @@ -38,6 +39,8 @@ class InventoryModel(TreeModel): self._hierarchy_view = False + self._default_icon_color = get_default_entity_icon_color() + manager = ModulesManager() sync_server = manager.modules_by_name["sync_server"] self.sync_enabled = sync_server.enabled @@ -131,7 +134,7 @@ class InventoryModel(TreeModel): if role == QtCore.Qt.DecorationRole: if index.column() == 0: # Override color - color = item.get("color", style.colors.default) + color = item.get("color", self._default_icon_color) if item.get("isGroupNode"): # group-item return qtawesome.icon("fa.folder", color=color) if item.get("isNotSet"): diff --git a/openpype/tools/standalonepublish/widgets/model_asset.py b/openpype/tools/standalonepublish/widgets/model_asset.py index 6d764eff9f..7d93e7a943 100644 --- a/openpype/tools/standalonepublish/widgets/model_asset.py +++ b/openpype/tools/standalonepublish/widgets/model_asset.py @@ -1,10 +1,15 @@ import logging import collections + from Qt import QtCore, QtGui import qtawesome -from . import TreeModel, Node -from avalon import style +from openpype.style import ( + get_default_entity_icon_color, + get_deprecated_entity_font_color, +) + +from . import TreeModel, Node log = logging.getLogger(__name__) @@ -49,6 +54,14 @@ class AssetModel(TreeModel): def __init__(self, dbcon, parent=None): super(AssetModel, self).__init__(parent=parent) self.dbcon = dbcon + + self._default_asset_icon_color = QtGui.QColor( + get_default_entity_icon_color() + ) + self._deprecated_asset_font_color = QtGui.QColor( + get_deprecated_entity_font_color() + ) + self.refresh() def _add_hierarchy(self, assets, parent=None, silos=None): @@ -163,7 +176,7 @@ class AssetModel(TreeModel): icon = data.get("icon", None) if icon is None and node.get("type") == "silo": icon = "database" - color = data.get("color", style.colors.default) + color = data.get("color", self._default_asset_icon_color) if icon is None: # Use default icons if no custom one is specified. @@ -188,8 +201,8 @@ class AssetModel(TreeModel): return if role == QtCore.Qt.ForegroundRole: # font color - if "deprecated" in node.get("tags", []): - return QtGui.QColor(style.colors.light).darker(250) + # if "deprecated" in node.get("tags", []): + return QtGui.QColor(self._deprecated_asset_font_color) if role == self.ObjectIdRole: return node.get("_id", None) diff --git a/openpype/tools/standalonepublish/widgets/model_tasks_template.py b/openpype/tools/standalonepublish/widgets/model_tasks_template.py index 1f36eaa39d..648f7ed479 100644 --- a/openpype/tools/standalonepublish/widgets/model_tasks_template.py +++ b/openpype/tools/standalonepublish/widgets/model_tasks_template.py @@ -1,7 +1,9 @@ from Qt import QtCore import qtawesome + +from openpype.style import get_default_entity_icon_color + from . import Node, TreeModel -from avalon import style class TasksTemplateModel(TreeModel): @@ -14,7 +16,7 @@ class TasksTemplateModel(TreeModel): self.selectable = selectable self.icon = qtawesome.icon( 'fa.calendar-check-o', - color=style.colors.default + color=get_default_entity_icon_color() ) def set_tasks(self, tasks): diff --git a/openpype/tools/standalonepublish/widgets/widget_asset.py b/openpype/tools/standalonepublish/widgets/widget_asset.py index d929f227f9..e6b74f8f82 100644 --- a/openpype/tools/standalonepublish/widgets/widget_asset.py +++ b/openpype/tools/standalonepublish/widgets/widget_asset.py @@ -4,7 +4,7 @@ import qtawesome from openpype.tools.utils import PlaceholderLineEdit -from avalon import style +from openpype.style import get_default_tools_icon_color from . import RecursiveSortFilterProxyModel, AssetModel from . import TasksTemplateModel, DeselectableTreeView @@ -165,7 +165,9 @@ class AssetWidget(QtWidgets.QWidget): # Header header = QtWidgets.QHBoxLayout() - icon = qtawesome.icon("fa.refresh", color=style.colors.light) + icon = qtawesome.icon( + "fa.refresh", color=get_default_tools_icon_color() + ) refresh = QtWidgets.QPushButton(icon, "") refresh.setToolTip("Refresh items") diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 4c77b81c0e..9beca69f12 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -5,9 +5,10 @@ import Qt from Qt import QtWidgets, QtCore, QtGui import qtawesome -from avalon import style - -from openpype.style import get_objected_colors +from openpype.style import ( + get_objected_colors, + get_default_tools_icon_color, +) from openpype.tools.flickcharm import FlickCharm from .views import ( @@ -589,7 +590,7 @@ class AssetsWidget(QtWidgets.QWidget): view.setModel(proxy) current_asset_icon = qtawesome.icon( - "fa.arrow-down", color=style.colors.light + "fa.arrow-down", color=get_default_tools_icon_color() ) current_asset_btn = QtWidgets.QPushButton(self) current_asset_btn.setIcon(current_asset_icon) @@ -597,7 +598,9 @@ class AssetsWidget(QtWidgets.QWidget): # Hide by default current_asset_btn.setVisible(False) - refresh_icon = qtawesome.icon("fa.refresh", color=style.colors.light) + refresh_icon = qtawesome.icon( + "fa.refresh", color=get_default_tools_icon_color() + ) refresh_btn = QtWidgets.QPushButton(self) refresh_btn.setIcon(refresh_icon) refresh_btn.setToolTip("Refresh items") diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 042ceaab88..829725dcf2 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -7,8 +7,8 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome import avalon.api -from avalon import style +from openpype.style import get_default_entity_icon_color from openpype.api import ( get_project_settings, Logger @@ -128,7 +128,7 @@ def get_qta_icon_by_name_and_color(icon_name, icon_color): def get_asset_icon(asset_doc, has_children=False): asset_data = asset_doc.get("data") or {} - icon_color = asset_data.get("color") or style.colors.default + icon_color = asset_data.get("color") or get_default_entity_icon_color() icon_name = asset_data.get("icon") if not icon_name: # Use default icons if no custom one is specified. @@ -149,7 +149,9 @@ def get_task_icon(): Icon should be defined by task type which is stored on project. """ - return get_qta_icon_by_name_and_color("fa.male", style.colors.default) + return get_qta_icon_by_name_and_color( + "fa.male", get_default_entity_icon_color() + ) def schedule(func, time, channel="default"): @@ -412,6 +414,7 @@ class GroupsConfig: def __init__(self, dbcon): self.dbcon = dbcon self.groups = {} + self._default_group_color = get_default_entity_icon_color() @classmethod def default_group_config(cls): @@ -419,7 +422,7 @@ class GroupsConfig: cls._default_group_config = { "icon": qtawesome.icon( "fa.object-group", - color=style.colors.default + color=get_default_entity_icon_color() ), "order": 0 } @@ -453,7 +456,7 @@ class GroupsConfig: for config in group_configs: name = config["name"] icon = "fa." + config.get("icon", "object-group") - color = config.get("color", style.colors.default) + color = config.get("color", self._default_group_color) order = float(config.get("order", 0)) self.groups[name] = { diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 7619f59974..2c92b7228a 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,7 +1,10 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome -from avalon import style +from openpype.style import ( + get_default_entity_icon_color, + get_disabled_entity_icon_color, +) from .views import DeselectableTreeView @@ -21,13 +24,14 @@ class TasksModel(QtGui.QStandardItemModel): self.setHeaderData( 0, QtCore.Qt.Horizontal, "Tasks", QtCore.Qt.DisplayRole ) + default_color = get_default_entity_icon_color() + self._default_color = default_color self._default_icon = qtawesome.icon( - "fa.male", - color=style.colors.default + "fa.male", color=default_color ) self._no_tasks_icon = qtawesome.icon( "fa.exclamation-circle", - color=style.colors.mid + color=get_disabled_entity_icon_color() ) self._cached_icons = {} self._project_task_types = {} @@ -62,7 +66,7 @@ class TasksModel(QtGui.QStandardItemModel): try: icon = qtawesome.icon( "fa.{}".format(icon_name), - color=style.colors.default + color=self._default_color ) except Exception: diff --git a/openpype/tools/workfiles/model.py b/openpype/tools/workfiles/model.py index b3cf5063e7..e9184842fc 100644 --- a/openpype/tools/workfiles/model.py +++ b/openpype/tools/workfiles/model.py @@ -4,7 +4,11 @@ import logging from Qt import QtCore import qtawesome -from avalon import style +from openpype.style import ( + get_default_entity_icon_color, + get_disabled_entity_icon_color, +) + from openpype.tools.utils.models import TreeModel, Item log = logging.getLogger(__name__) @@ -25,7 +29,10 @@ class FilesModel(TreeModel): self._root = None self._file_extensions = file_extensions self._icons = { - "file": qtawesome.icon("fa.file-o", color=style.colors.default) + "file": qtawesome.icon( + "fa.file-o", + color=get_default_entity_icon_color() + ) } def set_root(self, root): @@ -64,7 +71,10 @@ class FilesModel(TreeModel): "date": None, "filepath": None, "enabled": False, - "icon": qtawesome.icon("fa.times", color=style.colors.mid) + "icon": qtawesome.icon( + "fa.times", + color=get_disabled_entity_icon_color() + ) }) self.add_child(item) self.endResetModel() From 7c63ef68df3a10d31655dd9d8a0b3a8de21c1d3a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 15:45:03 +0100 Subject: [PATCH 218/302] change asset colorchange entity icon color --- openpype/style/data.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/style/data.json b/openpype/style/data.json index 2af23acd0d..a76a77015b 100644 --- a/openpype/style/data.json +++ b/openpype/style/data.json @@ -58,7 +58,7 @@ "icon-tools": "#ffffff", "icon-alert-tools": "#AA5050", - "icon-entity-default": "#fb9c15", + "icon-entity-default": "#bfccd6", "icon-entity-disabled": "#808080", "font-entity-deprecated": "#666666", From a864b80862d91ace2d46e23aa1fbb10b8a6a7481 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 15:53:41 +0100 Subject: [PATCH 219/302] flame: convert segment comment to attributes wip --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 6424bce3bc..54ff543f21 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -16,6 +16,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): audio_track_items = [] + def _get_comment_attributes(self, segment): + comment = segment.comment.get_value() + def process(self, context): project = context.data["flameProject"] sequence = context.data["flameSequence"] @@ -26,6 +29,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # process all sellected with opfapi.maintained_segment_selection(sequence) as segments: for segment in segments: + comment_attributes = self._get_comment_attributes(segment) clip_data = opfapi.get_segment_attributes(segment) clip_name = clip_data["segment_name"] self.log.debug("clip_name: {}".format(clip_name)) From bf3b9407cb8c8b05abde6a2df2145b4f86198a07 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 16:18:23 +0100 Subject: [PATCH 220/302] remove avalon style from maya look assigner --- openpype/tools/mayalookassigner/__init__.py | 4 +- openpype/tools/mayalookassigner/app.py | 55 ++++++++++++--------- openpype/tools/mayalookassigner/views.py | 3 -- openpype/tools/mayalookassigner/widgets.py | 36 +++++++------- openpype/tools/utils/host_tools.py | 6 +-- 5 files changed, 55 insertions(+), 49 deletions(-) diff --git a/openpype/tools/mayalookassigner/__init__.py b/openpype/tools/mayalookassigner/__init__.py index 616a3e94d0..5e40777741 100644 --- a/openpype/tools/mayalookassigner/__init__.py +++ b/openpype/tools/mayalookassigner/__init__.py @@ -1,9 +1,9 @@ from .app import ( - App, + MayaLookAssignerWindow, show ) __all__ = [ - "App", + "MayaLookAssignerWindow", "show"] diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index 31bb455f95..da9f06f3f0 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -4,10 +4,10 @@ import logging from Qt import QtWidgets, QtCore -from openpype.hosts.maya.api.lib import assign_look_by_version - -from avalon import style, io +from avalon import io +from openpype import style from openpype.tools.utils.lib import qt_app_context +from openpype.hosts.maya.api.lib import assign_look_by_version from maya import cmds # old api for MFileIO @@ -28,10 +28,10 @@ module = sys.modules[__name__] module.window = None -class App(QtWidgets.QWidget): +class MayaLookAssignerWindow(QtWidgets.QWidget): def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent=parent) + super(MayaLookAssignerWindow, self).__init__(parent=parent) self.log = logging.getLogger(__name__) @@ -56,30 +56,41 @@ class App(QtWidgets.QWidget): def setup_ui(self): """Build the UI""" + main_splitter = QtWidgets.QSplitter(self) + # Assets (left) - asset_outliner = AssetOutliner() + asset_outliner = AssetOutliner(main_splitter) # Looks (right) - looks_widget = QtWidgets.QWidget() - looks_layout = QtWidgets.QVBoxLayout(looks_widget) + looks_widget = QtWidgets.QWidget(main_splitter) - look_outliner = LookOutliner() # Database look overview + look_outliner = LookOutliner(looks_widget) # Database look overview - assign_selected = QtWidgets.QCheckBox("Assign to selected only") + assign_selected = QtWidgets.QCheckBox( + "Assign to selected only", looks_widget + ) assign_selected.setToolTip("Whether to assign only to selected nodes " "or to the full asset") - remove_unused_btn = QtWidgets.QPushButton("Remove Unused Looks") + remove_unused_btn = QtWidgets.QPushButton( + "Remove Unused Looks", looks_widget + ) + looks_layout = QtWidgets.QVBoxLayout(looks_widget) looks_layout.addWidget(look_outliner) looks_layout.addWidget(assign_selected) looks_layout.addWidget(remove_unused_btn) + main_splitter.addWidget(asset_outliner) + main_splitter.addWidget(looks_widget) + main_splitter.setSizes([350, 200]) + # Footer - status = QtWidgets.QStatusBar() + status = QtWidgets.QStatusBar(self) status.setSizeGripEnabled(False) status.setFixedHeight(25) - warn_layer = QtWidgets.QLabel("Current Layer is not " - "defaultRenderLayer") + warn_layer = QtWidgets.QLabel( + "Current Layer is not defaultRenderLayer", self + ) warn_layer.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) warn_layer.setStyleSheet("color: #DD5555; font-weight: bold;") warn_layer.setFixedHeight(25) @@ -92,11 +103,6 @@ class App(QtWidgets.QWidget): # Build up widgets main_layout = QtWidgets.QVBoxLayout(self) main_layout.setSpacing(0) - main_splitter = QtWidgets.QSplitter() - main_splitter.setStyleSheet("QSplitter{ border: 0px; }") - main_splitter.addWidget(asset_outliner) - main_splitter.addWidget(looks_widget) - main_splitter.setSizes([350, 200]) main_layout.addWidget(main_splitter) main_layout.addLayout(footer) @@ -124,6 +130,8 @@ class App(QtWidgets.QWidget): self.remove_unused = remove_unused_btn self.assign_selected = assign_selected + self._first_show = True + def setup_connections(self): """Connect interactive widgets with actions""" if self._connections_set_up: @@ -147,11 +155,14 @@ class App(QtWidgets.QWidget): def showEvent(self, event): self.setup_connections() - super(App, self).showEvent(event) + super(MayaLookAssignerWindow, self).showEvent(event) + if self._first_show: + self._first_show = False + self.setStyleSheet(style.load_stylesheet()) def closeEvent(self, event): self.remove_connection() - super(App, self).closeEvent(event) + super(MayaLookAssignerWindow, self).closeEvent(event) def _on_renderlayer_switch(self, *args): """Callback that updates on Maya renderlayer switch""" @@ -267,7 +278,7 @@ def show(): if widget.objectName() == "MayaWindow") with qt_app_context(): - window = App(parent=mainwindow) + window = MayaLookAssignerWindow(parent=mainwindow) window.setStyleSheet(style.load_stylesheet()) window.show() diff --git a/openpype/tools/mayalookassigner/views.py b/openpype/tools/mayalookassigner/views.py index 993023bb45..8e676ebc7f 100644 --- a/openpype/tools/mayalookassigner/views.py +++ b/openpype/tools/mayalookassigner/views.py @@ -1,9 +1,6 @@ from Qt import QtWidgets, QtCore -DEFAULT_COLOR = "#fb9c15" - - class View(QtWidgets.QTreeView): data_changed = QtCore.Signal() diff --git a/openpype/tools/mayalookassigner/widgets.py b/openpype/tools/mayalookassigner/widgets.py index e546ee705d..10e573342a 100644 --- a/openpype/tools/mayalookassigner/widgets.py +++ b/openpype/tools/mayalookassigner/widgets.py @@ -14,7 +14,7 @@ from .models import ( LookModel ) from . import commands -from . import views +from .views import View from maya import cmds @@ -24,25 +24,28 @@ class AssetOutliner(QtWidgets.QWidget): selection_changed = QtCore.Signal() def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent) + super(AssetOutliner, self).__init__(parent) - layout = QtWidgets.QVBoxLayout() - - title = QtWidgets.QLabel("Assets") + title = QtWidgets.QLabel("Assets", self) title.setAlignment(QtCore.Qt.AlignCenter) title.setStyleSheet("font-weight: bold; font-size: 12px") model = AssetModel() - view = views.View() + view = View(self) view.setModel(model) view.customContextMenuRequested.connect(self.right_mouse_menu) view.setSortingEnabled(False) view.setHeaderHidden(True) view.setIndentation(10) - from_all_asset_btn = QtWidgets.QPushButton("Get All Assets") - from_selection_btn = QtWidgets.QPushButton("Get Assets From Selection") + from_all_asset_btn = QtWidgets.QPushButton( + "Get All Assets", self + ) + from_selection_btn = QtWidgets.QPushButton( + "Get Assets From Selection", self + ) + layout = QtWidgets.QVBoxLayout(self) layout.addWidget(title) layout.addWidget(from_all_asset_btn) layout.addWidget(from_selection_btn) @@ -58,8 +61,6 @@ class AssetOutliner(QtWidgets.QWidget): self.view = view self.model = model - self.setLayout(layout) - self.log = logging.getLogger(__name__) def clear(self): @@ -188,15 +189,10 @@ class LookOutliner(QtWidgets.QWidget): menu_apply_action = QtCore.Signal() def __init__(self, parent=None): - QtWidgets.QWidget.__init__(self, parent) - - # look manager layout - layout = QtWidgets.QVBoxLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - layout.setSpacing(10) + super(LookOutliner, self).__init__(parent) # Looks from database - title = QtWidgets.QLabel("Looks") + title = QtWidgets.QLabel("Looks", self) title.setAlignment(QtCore.Qt.AlignCenter) title.setStyleSheet("font-weight: bold; font-size: 12px") title.setAlignment(QtCore.Qt.AlignCenter) @@ -207,13 +203,17 @@ class LookOutliner(QtWidgets.QWidget): proxy = QtCore.QSortFilterProxyModel() proxy.setSourceModel(model) - view = views.View() + view = View(self) view.setModel(proxy) view.setMinimumHeight(180) view.setToolTip("Use right mouse button menu for direct actions") view.customContextMenuRequested.connect(self.right_mouse_menu) view.sortByColumn(0, QtCore.Qt.AscendingOrder) + # look manager layout + layout = QtWidgets.QVBoxLayout(self) + layout.setContentsMargins(0, 0, 0, 0) + layout.setSpacing(10) layout.addWidget(title) layout.addWidget(view) diff --git a/openpype/tools/utils/host_tools.py b/openpype/tools/utils/host_tools.py index 6ce9e818d9..2d9733ec94 100644 --- a/openpype/tools/utils/host_tools.py +++ b/openpype/tools/utils/host_tools.py @@ -224,20 +224,18 @@ class HostToolsHelper: def get_look_assigner_tool(self, parent): """Create, cache and return look assigner tool window.""" if self._look_assigner_tool is None: - import mayalookassigner + from openpype.tools.mayalookassigner import MayaLookAssignerWindow - mayalookassigner_window = mayalookassigner.App(parent) + mayalookassigner_window = MayaLookAssignerWindow(parent) self._look_assigner_tool = mayalookassigner_window return self._look_assigner_tool def show_look_assigner(self, parent=None): """Look manager is Maya specific tool for look management.""" - from avalon import style with qt_app_context(): look_assigner_tool = self.get_look_assigner_tool(parent) look_assigner_tool.show() - look_assigner_tool.setStyleSheet(style.load_stylesheet()) def get_experimental_tools_dialog(self, parent=None): """Dialog of experimental tools. From cd1e764da6299d328fe5d9895d4b6552f48fdc2e Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 16:19:08 +0100 Subject: [PATCH 221/302] cleanup --- .../project_manager/project_manager/multiselection_combobox.py | 2 +- openpype/tools/utils/__init__.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/tools/project_manager/project_manager/multiselection_combobox.py b/openpype/tools/project_manager/project_manager/multiselection_combobox.py index 890567de6d..f776831298 100644 --- a/openpype/tools/project_manager/project_manager/multiselection_combobox.py +++ b/openpype/tools/project_manager/project_manager/multiselection_combobox.py @@ -1,4 +1,4 @@ -from Qt import QtCore, QtGui, QtWidgets +from Qt import QtCore, QtWidgets class ComboItemDelegate(QtWidgets.QStyledItemDelegate): diff --git a/openpype/tools/utils/__init__.py b/openpype/tools/utils/__init__.py index 6ab9e75b52..ea1133c442 100644 --- a/openpype/tools/utils/__init__.py +++ b/openpype/tools/utils/__init__.py @@ -42,6 +42,7 @@ __all__ = ( "set_style_property", "DynamicQThread", "qt_app_context", + "get_asset_icon", "RecursiveSortFilterProxyModel", ) From 4f8129acfae2061211f00c4d8061dc8bb8626199 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 16:40:05 +0100 Subject: [PATCH 222/302] move spinner svg into openpype --- openpype/resources/images/spinner-200.svg | 6 ++++++ openpype/tools/utils/views.py | 9 +++------ 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 openpype/resources/images/spinner-200.svg diff --git a/openpype/resources/images/spinner-200.svg b/openpype/resources/images/spinner-200.svg new file mode 100644 index 0000000000..73d8ae2890 --- /dev/null +++ b/openpype/resources/images/spinner-200.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/openpype/tools/utils/views.py b/openpype/tools/utils/views.py index 97aaf622a4..a2f1f15b95 100644 --- a/openpype/tools/utils/views.py +++ b/openpype/tools/utils/views.py @@ -1,5 +1,5 @@ import os -from avalon import style +from openpype.resources import get_image_path from Qt import QtWidgets, QtCore, QtGui, QtSvg @@ -24,11 +24,8 @@ class TreeViewSpinner(QtWidgets.QTreeView): def __init__(self, parent=None): super(TreeViewSpinner, self).__init__(parent=parent) - loading_image_path = os.path.join( - os.path.dirname(os.path.abspath(style.__file__)), - "svg", - "spinner-200.svg" - ) + loading_image_path = get_image_path("spinner-200.svg") + self.spinner = QtSvg.QSvgRenderer(loading_image_path) self.is_loading = False From e1dd07c936c65a4e34c68135425e0ffd54ab94db Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 16:40:32 +0100 Subject: [PATCH 223/302] remove backwards compatibility of 'application' context function --- openpype/tools/utils/lib.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 829725dcf2..3ad2d12883 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -92,10 +92,6 @@ def qt_app_context(): yield app -# Backwards compatibility -application = qt_app_context - - class SharedObjects: jobs = {} icons = {} From 2b062e98a0a3076d56d3f91756570db67ea17e0c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 16:51:37 +0100 Subject: [PATCH 224/302] added icon option for projects --- openpype/tools/launcher/models.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 9036c9cbd5..08bf43a451 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -14,7 +14,10 @@ from openpype.lib.applications import ( CUSTOM_LAUNCH_APP_GROUPS, ApplicationManager ) -from openpype.tools.utils.lib import DynamicQThread +from openpype.tools.utils.lib import ( + DynamicQThread, + get_qta_icon_by_name_and_color, +) from openpype.tools.utils.assets_widget import ( AssetModel, ASSET_NAME_ROLE @@ -400,6 +403,7 @@ class LauncherModel(QtCore.QObject): self._dbcon = dbcon # Available project names self._project_names = set() + self._project_icons_by_name = {} # Context data self._asset_docs = [] @@ -460,6 +464,9 @@ class LauncherModel(QtCore.QObject): """Available project names.""" return self._project_names + def get_icon_for_project(self, project_name): + return self._project_icons_by_name.get(project_name) + @property def asset_filter_data_by_id(self): """Prepared filter data by asset id.""" @@ -516,9 +523,15 @@ class LauncherModel(QtCore.QObject): """Refresh projects.""" current_project = self.project_name project_names = set() + project_icons_by_name = {} for project_doc in self._dbcon.projects(only_active=True): - project_names.add(project_doc["name"]) + project_name = project_doc["name"] + project_names.add(project_name) + project_icons_by_name[project_name] = ( + project_doc.get("data", {}).get("icon") + ) + self._project_icons_by_name = project_icons_by_name self._project_names = project_names self.projects_refreshed.emit() if ( @@ -718,7 +731,6 @@ class AssetRecursiveSortFilterModel(QtCore.QSortFilterProxyModel): self._assignee_filter = self._launcher_model.assignee_filters self.invalidateFilter() - """Filters to the regex if any of the children matches allow parent""" def filterAcceptsRow(self, row, parent): if ( not self._name_filter @@ -863,7 +875,14 @@ class ProjectModel(QtGui.QStandardItemModel): for row in reversed(sorted(row_counts.keys())): items = [] for project_name in row_counts[row]: - item = QtGui.QStandardItem(self.project_icon, project_name) + icon_name = self._launcher_model.get_icon_for_project( + project_name + ) + icon = get_qta_icon_by_name_and_color(icon_name, "white") + if not icon: + icon = self.project_icon + + item = QtGui.QStandardItem(icon, project_name) items.append(item) self.invisibleRootItem().insertRows(row, items) From b48460d579e275dae09248168ae3748e2eb77716 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 17:17:33 +0100 Subject: [PATCH 225/302] added function for getting a project icon --- openpype/tools/launcher/models.py | 24 ++++++---------- openpype/tools/utils/lib.py | 46 +++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 26 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 08bf43a451..8dbc45aadb 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -16,7 +16,7 @@ from openpype.lib.applications import ( ) from openpype.tools.utils.lib import ( DynamicQThread, - get_qta_icon_by_name_and_color, + get_project_icon, ) from openpype.tools.utils.assets_widget import ( AssetModel, @@ -403,7 +403,7 @@ class LauncherModel(QtCore.QObject): self._dbcon = dbcon # Available project names self._project_names = set() - self._project_icons_by_name = {} + self._project_docs_by_name = {} # Context data self._asset_docs = [] @@ -464,8 +464,8 @@ class LauncherModel(QtCore.QObject): """Available project names.""" return self._project_names - def get_icon_for_project(self, project_name): - return self._project_icons_by_name.get(project_name) + def get_project_doc(self, project_name): + return self._project_docs_by_name.get(project_name) @property def asset_filter_data_by_id(self): @@ -523,15 +523,13 @@ class LauncherModel(QtCore.QObject): """Refresh projects.""" current_project = self.project_name project_names = set() - project_icons_by_name = {} + project_docs_by_name = {} for project_doc in self._dbcon.projects(only_active=True): project_name = project_doc["name"] project_names.add(project_name) - project_icons_by_name[project_name] = ( - project_doc.get("data", {}).get("icon") - ) + project_docs_by_name[project_name] = project_doc - self._project_icons_by_name = project_icons_by_name + self._project_docs_by_name = project_docs_by_name self._project_names = project_names self.projects_refreshed.emit() if ( @@ -830,7 +828,6 @@ class ProjectModel(QtGui.QStandardItemModel): super(ProjectModel, self).__init__(parent=parent) self._launcher_model = launcher_model - self.project_icon = qtawesome.icon("fa.map", color="white") self._project_names = set() launcher_model.projects_refreshed.connect(self._on_refresh) @@ -875,13 +872,10 @@ class ProjectModel(QtGui.QStandardItemModel): for row in reversed(sorted(row_counts.keys())): items = [] for project_name in row_counts[row]: - icon_name = self._launcher_model.get_icon_for_project( + project_doc = self._launcher_model.get_project_doc( project_name ) - icon = get_qta_icon_by_name_and_color(icon_name, "white") - if not icon: - icon = self.project_icon - + icon = get_project_icon(project_doc) item = QtGui.QStandardItem(icon, project_name) items.append(item) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 3ad2d12883..4754a85bf1 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -122,18 +122,42 @@ def get_qta_icon_by_name_and_color(icon_name, icon_color): return icon +def get_project_icon(project_doc): + if project_doc: + icon_name = project_doc.get("data", {}).get("icon") + icon = get_qta_icon_by_name_and_color(icon_name, "white") + if icon: + return icon + + return get_qta_icon_by_name_and_color( + "fa.map", get_default_entity_icon_color() + ) + + +def get_asset_icon_name(asset_doc, has_children=True): + if asset_doc: + asset_data = asset_doc.get("data") or {} + icon_name = asset_data.get("icon") + if icon_name: + return icon_name + + if has_children: + return "folder" + return "folder-o" + + +def get_asset_icon_color(asset_doc): + if asset_doc: + asset_data = asset_doc.get("data") or {} + icon_color = asset_data.get("color") + if icon_color: + return icon_color + return get_default_entity_icon_color() + + def get_asset_icon(asset_doc, has_children=False): - asset_data = asset_doc.get("data") or {} - icon_color = asset_data.get("color") or get_default_entity_icon_color() - icon_name = asset_data.get("icon") - if not icon_name: - # Use default icons if no custom one is specified. - # If it has children show a full folder, otherwise - # show an open folder - if has_children: - icon_name = "folder" - else: - icon_name = "folder-o" + icon_name = get_asset_icon_name(asset_doc, has_children) + icon_color = get_asset_icon_color(asset_doc) return get_qta_icon_by_name_and_color(icon_name, icon_color) From 8f998f06e88fa410c1603b40616911f83a3ac7b1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Mon, 14 Mar 2022 17:24:57 +0100 Subject: [PATCH 226/302] use openpype style on scene inventory error dialog --- openpype/tools/sceneinventory/view.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/tools/sceneinventory/view.py b/openpype/tools/sceneinventory/view.py index 32c1883de6..fb93faefd6 100644 --- a/openpype/tools/sceneinventory/view.py +++ b/openpype/tools/sceneinventory/view.py @@ -5,9 +5,10 @@ from functools import partial from Qt import QtWidgets, QtCore import qtawesome -from avalon import io, api, style +from avalon import io, api from avalon.lib import HeroVersionType +from openpype import style from openpype.modules import ModulesManager from openpype.tools.utils.lib import ( get_progress_for_repre, From 34b44bec6306c807c3c652872d5b53b8838b0e11 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 19:36:25 +0100 Subject: [PATCH 227/302] flame: resolving attributes from segment comments --- .../publish/collect_timeline_instances.py | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 54ff543f21..9e6c7210fb 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -1,3 +1,4 @@ +import re import pyblish import openpype import openpype.hosts.flame.api as opfapi @@ -16,9 +17,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): audio_track_items = [] - def _get_comment_attributes(self, segment): - comment = segment.comment.get_value() - def process(self, context): project = context.data["flameProject"] sequence = context.data["flameSequence"] @@ -30,6 +28,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): with opfapi.maintained_segment_selection(sequence) as segments: for segment in segments: comment_attributes = self._get_comment_attributes(segment) + self.log.debug("_ comment_attributes: {}".format( + pformat(comment_attributes))) + clip_data = opfapi.get_segment_attributes(segment) clip_name = clip_data["segment_name"] self.log.debug("clip_name: {}".format(clip_name)) @@ -130,6 +131,44 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): if marker_data.get("reviewTrack") is not None: instance.data["reviewAudio"] = True + def _get_comment_attributes(self, segment): + comment = segment.comment.get_value() + + # first split comment by comma + split_comments = [] + if "," in comment: + split_comments.extend(iter(comment.split(","))) + elif ";" in comment: + split_comments.extend(iter(comment.split(";"))) + else: + split_comments.append(comment) + + # try to find attributes + attributes = {} + # search for `:` + for split in split_comments: + # make sure we ignore if not `:` in key + if ":" not in split: + continue + + # split to key and value + key, value = split.split(":") + + # condition for resolution in key + if "resolution" in key.lower(): + patern = re.compile(r"([0-9]+)") + res_goup = patern.findall(value) + + # check if axpect was also defined + # 1920x1080x1.5 + aspect = res_goup[2] if len(res_goup) > 2 else 1 + + attributes["resolution"] = { + "width": int(res_goup[0]), + "height": int(res_goup[1]), + "pixelAspect": float(aspect) + } + def _get_head_tail(self, clip_data, first_frame): # calculate head and tail with forward compatibility head = clip_data.get("segment_head") From bd57a0fd56f76c71328020eeaa29aec294ea7efb Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 19:49:15 +0100 Subject: [PATCH 228/302] flame: add comment attributes to instance data --- .../plugins/publish/collect_timeline_instances.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 9e6c7210fb..dd44627021 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -106,6 +106,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # add resolution self._get_resolution_to_data(inst_data, context) + # add comment attributes if any + inst_data.update(comment_attributes) + # create instance instance = context.create_instance(**inst_data) @@ -163,11 +166,13 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # 1920x1080x1.5 aspect = res_goup[2] if len(res_goup) > 2 else 1 - attributes["resolution"] = { - "width": int(res_goup[0]), - "height": int(res_goup[1]), + attributes.update({ + "resolutionWidth": int(res_goup[0]), + "resolutionHeight": int(res_goup[1]), "pixelAspect": float(aspect) - } + }) + + return attributes def _get_head_tail(self, clip_data, first_frame): # calculate head and tail with forward compatibility From 420122b8c9ec5e3eeefe7f89e8627c06a30f6eed Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 14 Mar 2022 19:57:35 +0100 Subject: [PATCH 229/302] flame: fix regex to get float number too --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index dd44627021..f41f773802 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -159,7 +159,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # condition for resolution in key if "resolution" in key.lower(): - patern = re.compile(r"([0-9]+)") + patern = re.compile(r"([0-9\.]+)") res_goup = patern.findall(value) # check if axpect was also defined From 16a24d03daaf0eec01fb7121e48e40c6f0f1fa1f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 09:56:58 +0100 Subject: [PATCH 230/302] removed unnecessary style set --- openpype/tools/mayalookassigner/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/tools/mayalookassigner/app.py b/openpype/tools/mayalookassigner/app.py index da9f06f3f0..0e633a21e3 100644 --- a/openpype/tools/mayalookassigner/app.py +++ b/openpype/tools/mayalookassigner/app.py @@ -279,7 +279,6 @@ def show(): with qt_app_context(): window = MayaLookAssignerWindow(parent=mainwindow) - window.setStyleSheet(style.load_stylesheet()) window.show() module.window = window From c5b3098acd35c439628d12750d521dfe5b36e598 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:24:12 +0100 Subject: [PATCH 231/302] fix function typo --- openpype/hosts/houdini/api/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index 7d4e58efb7..eb1bdafbb0 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -13,7 +13,7 @@ from avalon.lib import find_submodule from openpype.pipeline import ( LegacyCreator, - register_loader_plugin_path, + register_loader_plugins_path, ) import openpype.hosts.houdini from openpype.hosts.houdini.api import lib @@ -53,7 +53,7 @@ def install(): pyblish.api.register_host("hpython") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugin_path(LOAD_PATH) + register_loader_plugins_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info("Installing callbacks ... ") From 96f55916dd3cd000aad6901f842653123313285f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:26:07 +0100 Subject: [PATCH 232/302] remove constants --- openpype/style/__init__.py | 55 +++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/openpype/style/__init__.py b/openpype/style/__init__.py index d92e18c0cd..b2a1a4ce6c 100644 --- a/openpype/style/__init__.py +++ b/openpype/style/__init__.py @@ -10,17 +10,6 @@ from .color_defs import parse_color current_dir = os.path.dirname(os.path.abspath(__file__)) -# Default colors -# - default color used in tool icons -_TOOLS_ICON_COLOR = "#ffffff" -# - entities icon color - use 'get_default_asset_icon_color' -_DEFAULT_ENTITY_ICON_COLOR = "#fb9c15" -# - disabled entitie -_DISABLED_ENTITY_ICON_ICON_COLOR = "#808080" -# - deprecated entity font color -_DEPRECATED_ENTITY_FONT_COLOR = "#666666" - - class _Cache: stylesheet = None font_ids = None @@ -200,36 +189,60 @@ def app_icon_path(): def get_default_tools_icon_color(): + """Default color used in tool icons. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ if _Cache.tools_icon_color is None: color_data = get_colors_data() - color = color_data.get("icon-tools") - _Cache.tools_icon_color = color or _TOOLS_ICON_COLOR + _Cache.tools_icon_color = color_data["icon-tools"] return _Cache.tools_icon_color def get_default_entity_icon_color(): + """Default color of entities icons. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ if _Cache.default_entity_icon_color is None: color_data = get_colors_data() - color = color_data.get("icon-entity-default") - _Cache.default_entity_icon_color = color or _DEFAULT_ENTITY_ICON_COLOR + _Cache.default_entity_icon_color = color_data["icon-entity-default"] return _Cache.default_entity_icon_color def get_disabled_entity_icon_color(): + """Default color of entities icons. + + TODO: Find more suitable function name. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ if _Cache.disabled_entity_icon_color is None: color_data = get_colors_data() - color = color_data.get("icon-entity-disabled") - _Cache.disabled_entity_icon_color = ( - color or _DISABLED_ENTITY_ICON_ICON_COLOR - ) + _Cache.disabled_entity_icon_color = color_data["icon-entity-disabled"] return _Cache.disabled_entity_icon_color def get_deprecated_entity_font_color(): + """Font color for deprecated entities. + + Color must be possible to parse using QColor. + + Returns: + str: Color as a string. + """ if _Cache.deprecated_entity_font_color is None: color_data = get_colors_data() - color = color_data.get("font-entity-deprecated") _Cache.deprecated_entity_font_color = ( - color or _DEPRECATED_ENTITY_FONT_COLOR + color_data["font-entity-deprecated"] ) return _Cache.deprecated_entity_font_color From 58d31a11b9c89eef71d784947b19d9c9d78a8eae Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:49:51 +0100 Subject: [PATCH 233/302] implemented function to receive task icon --- openpype/tools/launcher/models.py | 5 ++ openpype/tools/utils/lib.py | 39 +++++++++++--- openpype/tools/utils/tasks_widget.py | 76 +++++++--------------------- 3 files changed, 56 insertions(+), 64 deletions(-) diff --git a/openpype/tools/launcher/models.py b/openpype/tools/launcher/models.py index 8dbc45aadb..85d553fca4 100644 --- a/openpype/tools/launcher/models.py +++ b/openpype/tools/launcher/models.py @@ -705,6 +705,11 @@ class LauncherTaskModel(TasksModel): self._launcher_model = launcher_model super(LauncherTaskModel, self).__init__(*args, **kwargs) + def _refresh_project_doc(self): + self._project_doc = self._launcher_model.get_project_doc( + self._launcher_model.project_name + ) + def set_asset_id(self, asset_id): asset_doc = None if self._context_is_valid(): diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 4754a85bf1..00cec20b2a 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -162,16 +162,43 @@ def get_asset_icon(asset_doc, has_children=False): return get_qta_icon_by_name_and_color(icon_name, icon_color) -def get_task_icon(): - """Get icon for a task. +def get_default_task_icon(color=None): + if color is None: + color = get_default_entity_icon_color() + return get_qta_icon_by_name_and_color("fa.male", color) - TODO: Get task icon based on data in database. + +def get_task_icon(project_doc, asset_doc, task_name): + """Get icon for a task. Icon should be defined by task type which is stored on project. """ - return get_qta_icon_by_name_and_color( - "fa.male", get_default_entity_icon_color() - ) + + color = get_default_entity_icon_color() + + tasks_info = asset_doc.get("data", {}).get("tasks") or {} + task_info = tasks_info.get(task_name) or {} + task_icon = task_info.get("icon") + if task_icon: + icon = get_qta_icon_by_name_and_color(task_icon, color) + if icon is not None: + return icon + + task_type = task_info.get("type") + if "config" not in project_doc: + print(10*"*") + print(project_doc) + task_types = {} + else: + task_types = project_doc["config"].get("tasks") or {} + + task_type_info = task_types.get(task_type) or {} + task_type_icon = task_type_info.get("icon") + if task_type_icon: + icon = get_qta_icon_by_name_and_color(task_icon, color) + if icon is not None: + return icon + return get_default_task_icon(color) def schedule(func, time, channel="default"): diff --git a/openpype/tools/utils/tasks_widget.py b/openpype/tools/utils/tasks_widget.py index 2c92b7228a..eab183d5f3 100644 --- a/openpype/tools/utils/tasks_widget.py +++ b/openpype/tools/utils/tasks_widget.py @@ -1,10 +1,8 @@ from Qt import QtWidgets, QtCore, QtGui import qtawesome -from openpype.style import ( - get_default_entity_icon_color, - get_disabled_entity_icon_color, -) +from openpype.style import get_disabled_entity_icon_color +from openpype.tools.utils.lib import get_task_icon from .views import DeselectableTreeView @@ -24,54 +22,35 @@ class TasksModel(QtGui.QStandardItemModel): self.setHeaderData( 0, QtCore.Qt.Horizontal, "Tasks", QtCore.Qt.DisplayRole ) - default_color = get_default_entity_icon_color() - self._default_color = default_color - self._default_icon = qtawesome.icon( - "fa.male", color=default_color - ) + self._no_tasks_icon = qtawesome.icon( "fa.exclamation-circle", color=get_disabled_entity_icon_color() ) self._cached_icons = {} - self._project_task_types = {} + self._project_doc = {} self._empty_tasks_item = None self._last_asset_id = None self._loaded_project_name = None def _context_is_valid(self): - if self.dbcon.Session.get("AVALON_PROJECT"): + if self._get_current_project(): return True return False def refresh(self): - self._refresh_task_types() + self._refresh_project_doc() self.set_asset_id(self._last_asset_id) - def _refresh_task_types(self): + def _refresh_project_doc(self): # Get the project configured icons from database - task_types = {} + project_doc = {} if self._context_is_valid(): - project = self.dbcon.find_one( - {"type": "project"}, - {"config.tasks"} - ) - task_types = project["config"].get("tasks") or task_types - self._project_task_types = task_types + project_doc = self.dbcon.find_one({"type": "project"}) - def _try_get_awesome_icon(self, icon_name): - icon = None - if icon_name: - try: - icon = qtawesome.icon( - "fa.{}".format(icon_name), - color=self._default_color - ) - - except Exception: - pass - return icon + self._loaded_project_name = self._get_current_project() + self._project_doc = project_doc def headerData(self, section, orientation, role=None): if role is None: @@ -86,28 +65,8 @@ class TasksModel(QtGui.QStandardItemModel): return super(TasksModel, self).headerData(section, orientation, role) - def _get_icon(self, task_icon, task_type_icon): - if task_icon in self._cached_icons: - return self._cached_icons[task_icon] - - icon = self._try_get_awesome_icon(task_icon) - if icon is not None: - self._cached_icons[task_icon] = icon - return icon - - if task_type_icon in self._cached_icons: - icon = self._cached_icons[task_type_icon] - self._cached_icons[task_icon] = icon - return icon - - icon = self._try_get_awesome_icon(task_type_icon) - if icon is None: - icon = self._default_icon - - self._cached_icons[task_icon] = icon - self._cached_icons[task_type_icon] = icon - - return icon + def _get_current_project(self): + return self.dbcon.Session.get("AVALON_PROJECT") def set_asset_id(self, asset_id): asset_doc = None @@ -132,6 +91,9 @@ class TasksModel(QtGui.QStandardItemModel): Arguments: asset_doc (dict): Asset document from MongoDB. """ + if self._loaded_project_name != self._get_current_project(): + self._refresh_project_doc() + asset_tasks = {} self._last_asset_id = None if asset_doc: @@ -142,13 +104,11 @@ class TasksModel(QtGui.QStandardItemModel): root_item.removeRows(0, root_item.rowCount()) items = [] + for task_name, task_info in asset_tasks.items(): - task_icon = task_info.get("icon") task_type = task_info.get("type") task_order = task_info.get("order") - task_type_info = self._project_task_types.get(task_type) or {} - task_type_icon = task_type_info.get("icon") - icon = self._get_icon(task_icon, task_type_icon) + icon = get_task_icon(self._project_doc, asset_doc, task_name) task_assignees = set() assignees_data = task_info.get("assignees") or [] From 8a0005e93dd4eec57be0e92f3cf1741bc88e260c Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:50:00 +0100 Subject: [PATCH 234/302] use default task icon in publisher --- openpype/tools/publisher/widgets/tasks_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/publisher/widgets/tasks_widget.py b/openpype/tools/publisher/widgets/tasks_widget.py index 8a913b7114..aa239f6334 100644 --- a/openpype/tools/publisher/widgets/tasks_widget.py +++ b/openpype/tools/publisher/widgets/tasks_widget.py @@ -1,7 +1,7 @@ from Qt import QtCore, QtGui from openpype.tools.utils.tasks_widget import TasksWidget, TASK_NAME_ROLE -from openpype.tools.utils.lib import get_task_icon +from openpype.tools.utils.lib import get_default_task_icon class TasksModel(QtGui.QStandardItemModel): @@ -120,7 +120,7 @@ class TasksModel(QtGui.QStandardItemModel): item = QtGui.QStandardItem(task_name) item.setData(task_name, TASK_NAME_ROLE) if task_name: - item.setData(get_task_icon(), QtCore.Qt.DecorationRole) + item.setData(get_default_task_icon(), QtCore.Qt.DecorationRole) self._items_by_name[task_name] = item new_items.append(item) From 001f096f4e7861d8065304af521d848d417265cd Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:55:27 +0100 Subject: [PATCH 235/302] fix deprecated font color in standalone publisher --- openpype/tools/standalonepublish/widgets/model_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/tools/standalonepublish/widgets/model_asset.py b/openpype/tools/standalonepublish/widgets/model_asset.py index 7d93e7a943..a7316a2aa7 100644 --- a/openpype/tools/standalonepublish/widgets/model_asset.py +++ b/openpype/tools/standalonepublish/widgets/model_asset.py @@ -201,8 +201,8 @@ class AssetModel(TreeModel): return if role == QtCore.Qt.ForegroundRole: # font color - # if "deprecated" in node.get("tags", []): - return QtGui.QColor(self._deprecated_asset_font_color) + if "deprecated" in node.get("tags", []): + return QtGui.QColor(self._deprecated_asset_font_color) if role == self.ObjectIdRole: return node.get("_id", None) From 10333be88310781deffe118e276201c13ea5a4ae Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 10:55:40 +0100 Subject: [PATCH 236/302] remove asset document check --- openpype/tools/utils/assets_widget.py | 2 +- openpype/tools/utils/lib.py | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/openpype/tools/utils/assets_widget.py b/openpype/tools/utils/assets_widget.py index 9beca69f12..3d4efcdd4d 100644 --- a/openpype/tools/utils/assets_widget.py +++ b/openpype/tools/utils/assets_widget.py @@ -513,7 +513,7 @@ class AssetModel(QtGui.QStandardItemModel): item.setData(asset_label, ASSET_LABEL_ROLE) has_children = item.rowCount() > 0 - icon = get_asset_icon(asset_data, has_children) + icon = get_asset_icon(asset_doc, has_children) item.setData(icon, QtCore.Qt.DecorationRole) def _threaded_fetch(self): diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index 00cec20b2a..d66b636b2a 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -135,11 +135,9 @@ def get_project_icon(project_doc): def get_asset_icon_name(asset_doc, has_children=True): - if asset_doc: - asset_data = asset_doc.get("data") or {} - icon_name = asset_data.get("icon") - if icon_name: - return icon_name + icon_name = asset_doc["data"].get("icon") + if icon_name: + return icon_name if has_children: return "folder" @@ -147,11 +145,9 @@ def get_asset_icon_name(asset_doc, has_children=True): def get_asset_icon_color(asset_doc): - if asset_doc: - asset_data = asset_doc.get("data") or {} - icon_color = asset_data.get("color") - if icon_color: - return icon_color + icon_color = asset_doc["data"].get("color") + if icon_color: + return icon_color return get_default_entity_icon_color() From 43239740119e2456b8d7a26a28f3a4eaab05af47 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 11:12:39 +0100 Subject: [PATCH 237/302] remove debug prints --- openpype/tools/utils/lib.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py index d66b636b2a..93b156bef8 100644 --- a/openpype/tools/utils/lib.py +++ b/openpype/tools/utils/lib.py @@ -181,12 +181,7 @@ def get_task_icon(project_doc, asset_doc, task_name): return icon task_type = task_info.get("type") - if "config" not in project_doc: - print(10*"*") - print(project_doc) - task_types = {} - else: - task_types = project_doc["config"].get("tasks") or {} + task_types = project_doc["config"]["tasks"] task_type_info = task_types.get(task_type) or {} task_type_icon = task_type_info.get("icon") From f4ece8fd5ea844149c37c2cad0c9edebe258b4dc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Mar 2022 11:17:31 +0100 Subject: [PATCH 238/302] flame: removing testing code --- openpype/hosts/flame/hooks/pre_flame_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/hooks/pre_flame_setup.py b/openpype/hosts/flame/hooks/pre_flame_setup.py index 5db5757d50..ad2b0dc897 100644 --- a/openpype/hosts/flame/hooks/pre_flame_setup.py +++ b/openpype/hosts/flame/hooks/pre_flame_setup.py @@ -63,7 +63,7 @@ class FlamePrelaunch(PreLaunchHook): _db_p_data = project_doc["data"] width = _db_p_data["resolutionWidth"] height = _db_p_data["resolutionHeight"] - fps = float(_db_p_data["fps_string"]) + fps = float(_db_p_data["fps"]) project_data = { "Name": project_doc["name"], From 5b835d87e7707fd625cba05c7e8e587d766c75fa Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 11:40:59 +0100 Subject: [PATCH 239/302] renamed 'load_representation' to 'load_container' --- openpype/hosts/blender/plugins/load/load_layout_json.py | 4 ++-- openpype/hosts/maya/api/lib.py | 4 ++-- openpype/hosts/maya/api/setdress.py | 4 ++-- openpype/hosts/nuke/plugins/publish/validate_read_legacy.py | 4 ++-- openpype/hosts/unreal/plugins/load/load_layout.py | 4 ++-- openpype/lib/avalon_context.py | 4 ++-- openpype/pipeline/__init__.py | 4 ++-- openpype/pipeline/load/__init__.py | 4 ++-- openpype/pipeline/load/utils.py | 2 +- openpype/tools/mayalookassigner/vray_proxies.py | 4 ++-- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/blender/plugins/load/load_layout_json.py b/openpype/hosts/blender/plugins/load/load_layout_json.py index 499d2c49f3..0693937fec 100644 --- a/openpype/hosts/blender/plugins/load/load_layout_json.py +++ b/openpype/hosts/blender/plugins/load/load_layout_json.py @@ -10,7 +10,7 @@ import bpy from openpype.pipeline import ( discover_loader_plugins, remove_container, - load_representation, + load_container, get_representation_path, loaders_from_representation, ) @@ -108,7 +108,7 @@ class JsonLayoutLoader(plugin.AssetLoader): # at this time it will not return anything. The assets will be # loaded in the next Blender cycle, so we use the options to # set the transform, parent and assign the action, if there is one. - load_representation( + load_container( loader, reference, namespace=instance_name, diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 94efbb7a07..9f97eef2f1 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -25,7 +25,7 @@ from openpype.pipeline import ( discover_loader_plugins, loaders_from_representation, get_representation_path, - load_representation, + load_container, ) from .commands import reset_frame_range @@ -1594,7 +1594,7 @@ def assign_look_by_version(nodes, version_id): # Reference the look file with maintained_selection(): - container_node = load_representation(Loader, look_representation) + container_node = load_container(Loader, look_representation) # Get container members shader_nodes = get_container_members(container_node) diff --git a/openpype/hosts/maya/api/setdress.py b/openpype/hosts/maya/api/setdress.py index 74ee292eb2..96a9700b88 100644 --- a/openpype/hosts/maya/api/setdress.py +++ b/openpype/hosts/maya/api/setdress.py @@ -12,7 +12,7 @@ from avalon import io from openpype.pipeline import ( discover_loader_plugins, loaders_from_representation, - load_representation, + load_container, update_container, remove_container, get_representation_path, @@ -189,7 +189,7 @@ def _add(instance, representation_id, loaders, namespace, root="|"): instance['loader'], instance) raise RuntimeError("Loader is missing.") - container = load_representation( + container = load_container( Loader, representation_id, namespace=instance['namespace'] diff --git a/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py b/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py index 39fe011d85..2bf1ff81f8 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py +++ b/openpype/hosts/nuke/plugins/publish/validate_read_legacy.py @@ -8,7 +8,7 @@ from bson.objectid import ObjectId from openpype.pipeline import ( discover_loader_plugins, - load_representation, + load_container, ) @@ -59,7 +59,7 @@ class RepairReadLegacyAction(pyblish.api.Action): loader_plugin = Loader - load_representation( + load_container( Loader=loader_plugin, representation=ObjectId(data["representation"]) ) diff --git a/openpype/hosts/unreal/plugins/load/load_layout.py b/openpype/hosts/unreal/plugins/load/load_layout.py index b987a32a61..19ee179d20 100644 --- a/openpype/hosts/unreal/plugins/load/load_layout.py +++ b/openpype/hosts/unreal/plugins/load/load_layout.py @@ -15,7 +15,7 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.pipeline import ( discover_loader_plugins, loaders_from_representation, - load_representation, + load_container, get_representation_path, ) from openpype.hosts.unreal.api import plugin @@ -258,7 +258,7 @@ class LayoutLoader(plugin.Loader): "asset_dir": asset_dir } - assets = load_representation( + assets = load_container( loader, reference, namespace=instance_name, diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index d7f17d8eed..c88e72c46a 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1394,7 +1394,7 @@ class BuildWorkfile: """ from openpype.pipeline import ( IncompatibleLoaderError, - load_representation, + load_container, ) loaded_containers = [] @@ -1458,7 +1458,7 @@ class BuildWorkfile: if not loader: continue try: - container = load_representation( + container = load_container( loader, repre["_id"], name=subset_name diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index d582ef1d07..3ff3638a23 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -24,7 +24,7 @@ from .load import ( register_loader_plugins_path, deregister_loader_plugin, - load_representation, + load_container, remove_container, update_container, switch_container, @@ -68,7 +68,7 @@ __all__ = ( "register_loader_plugins_path", "deregister_loader_plugin", - "load_representation", + "load_container", "remove_container", "update_container", "switch_container", diff --git a/openpype/pipeline/load/__init__.py b/openpype/pipeline/load/__init__.py index 2af15e8705..eac303c10c 100644 --- a/openpype/pipeline/load/__init__.py +++ b/openpype/pipeline/load/__init__.py @@ -10,7 +10,7 @@ from .utils import ( load_with_subset_context, load_with_subset_contexts, - load_representation, + load_container, remove_container, update_container, switch_container, @@ -51,7 +51,7 @@ __all__ = ( "load_with_subset_context", "load_with_subset_contexts", - "load_representation", + "load_container", "remove_container", "update_container", "switch_container", diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index 4ef0f099d7..ae47cb9ce9 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -333,7 +333,7 @@ def load_with_subset_contexts( return loader.load(subset_contexts, name, namespace, options) -def load_representation( +def load_container( Loader, representation, namespace=None, name=None, options=None, **kwargs ): """Use Loader to load a representation. diff --git a/openpype/tools/mayalookassigner/vray_proxies.py b/openpype/tools/mayalookassigner/vray_proxies.py index 3179ba1445..6a9347449a 100644 --- a/openpype/tools/mayalookassigner/vray_proxies.py +++ b/openpype/tools/mayalookassigner/vray_proxies.py @@ -13,7 +13,7 @@ from maya import cmds from avalon import io, api from openpype.pipeline import ( - load_representation, + load_container, loaders_from_representation, discover_loader_plugins, get_representation_path, @@ -208,7 +208,7 @@ def load_look(version_id): # Reference the look file with lib.maintained_selection(): - container_node = load_representation(loader, look_representation) + container_node = load_container(loader, look_representation) # Get container members shader_nodes = lib.get_container_members(container_node) From 4f7d99babeee9a5a309b009e805877d70feec8a6 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 15 Mar 2022 14:15:35 +0100 Subject: [PATCH 240/302] remove plural from de/register_loader_plugins_path --- openpype/__init__.py | 10 +++++----- openpype/hosts/aftereffects/api/pipeline.py | 8 ++++---- openpype/hosts/blender/api/pipeline.py | 8 ++++---- openpype/hosts/flame/api/pipeline.py | 8 ++++---- openpype/hosts/fusion/api/pipeline.py | 8 ++++---- openpype/hosts/harmony/api/pipeline.py | 8 ++++---- openpype/hosts/hiero/api/pipeline.py | 8 ++++---- openpype/hosts/houdini/api/pipeline.py | 4 ++-- openpype/hosts/maya/api/pipeline.py | 8 ++++---- openpype/hosts/nuke/api/pipeline.py | 8 ++++---- openpype/hosts/photoshop/api/pipeline.py | 8 ++++---- openpype/hosts/resolve/api/pipeline.py | 8 ++++---- openpype/hosts/tvpaint/api/pipeline.py | 8 ++++---- openpype/hosts/unreal/api/pipeline.py | 8 ++++---- openpype/pipeline/__init__.py | 8 ++++---- openpype/pipeline/load/__init__.py | 8 ++++---- openpype/pipeline/load/plugins.py | 4 ++-- 17 files changed, 65 insertions(+), 65 deletions(-) diff --git a/openpype/__init__.py b/openpype/__init__.py index 0df1b7270f..99629a4257 100644 --- a/openpype/__init__.py +++ b/openpype/__init__.py @@ -77,7 +77,7 @@ def install(): from openpype.modules import load_modules from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, + register_loader_plugin_path, ) from avalon import pipeline @@ -94,7 +94,7 @@ def install(): log.info("Registering global plug-ins..") pyblish.register_plugin_path(PUBLISH_PATH) pyblish.register_discovery_filter(filter_pyblish_plugins) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) project_name = os.environ.get("AVALON_PROJECT") @@ -122,7 +122,7 @@ def install(): continue pyblish.register_plugin_path(path) - register_loader_plugins_path(path) + register_loader_plugin_path(path) avalon.register_plugin_path(LegacyCreator, path) avalon.register_plugin_path(avalon.InventoryAction, path) @@ -142,12 +142,12 @@ def _on_task_change(): @import_wrapper def uninstall(): """Uninstall Pype from Avalon.""" - from openpype.pipeline import deregister_loader_plugins_path + from openpype.pipeline import deregister_loader_plugin_path log.info("Deregistering global plug-ins..") pyblish.deregister_plugin_path(PUBLISH_PATH) pyblish.deregister_discovery_filter(filter_pyblish_plugins) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) log.info("Global plug-ins unregistred") # restore original discover diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py index 8961599149..681f1c51a7 100644 --- a/openpype/hosts/aftereffects/api/pipeline.py +++ b/openpype/hosts/aftereffects/api/pipeline.py @@ -11,8 +11,8 @@ from openpype import lib from openpype.api import Logger from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) import openpype.hosts.aftereffects from openpype.lib import register_event_callback @@ -71,7 +71,7 @@ def install(): pyblish.api.register_host("aftereffects") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -84,7 +84,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/blender/api/pipeline.py b/openpype/hosts/blender/api/pipeline.py index 64fb135d89..07a7509dd7 100644 --- a/openpype/hosts/blender/api/pipeline.py +++ b/openpype/hosts/blender/api/pipeline.py @@ -16,8 +16,8 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from openpype.api import Logger from openpype.lib import ( @@ -54,7 +54,7 @@ def install(): pyblish.api.register_host("blender") pyblish.api.register_plugin_path(str(PUBLISH_PATH)) - register_loader_plugins_path(str(LOAD_PATH)) + register_loader_plugin_path(str(LOAD_PATH)) avalon.api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) lib.append_user_scripts() @@ -76,7 +76,7 @@ def uninstall(): pyblish.api.deregister_host("blender") pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) - deregister_loader_plugins_path(str(LOAD_PATH)) + deregister_loader_plugin_path(str(LOAD_PATH)) avalon.api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) if not IS_HEADLESS: diff --git a/openpype/hosts/flame/api/pipeline.py b/openpype/hosts/flame/api/pipeline.py index 6a045214c3..930c6abe29 100644 --- a/openpype/hosts/flame/api/pipeline.py +++ b/openpype/hosts/flame/api/pipeline.py @@ -9,8 +9,8 @@ from pyblish import api as pyblish from openpype.api import Logger from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from .lib import ( set_segment_data_marker, @@ -37,7 +37,7 @@ def install(): pyblish.register_host("flame") pyblish.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) log.info("OpenPype Flame plug-ins registred ...") @@ -52,7 +52,7 @@ def uninstall(): log.info("Deregistering Flame plug-ins..") pyblish.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) diff --git a/openpype/hosts/fusion/api/pipeline.py b/openpype/hosts/fusion/api/pipeline.py index 3f5da7fcc7..92e54ad6f5 100644 --- a/openpype/hosts/fusion/api/pipeline.py +++ b/openpype/hosts/fusion/api/pipeline.py @@ -13,8 +13,8 @@ from avalon.pipeline import AVALON_CONTAINER_ID from openpype.api import Logger from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) import openpype.hosts.fusion @@ -67,7 +67,7 @@ def install(): pyblish.api.register_plugin_path(PUBLISH_PATH) log.info("Registering Fusion plug-ins..") - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) @@ -91,7 +91,7 @@ def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) log.info("Deregistering Fusion plug-ins..") - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH diff --git a/openpype/hosts/harmony/api/pipeline.py b/openpype/hosts/harmony/api/pipeline.py index b9d2e78bce..f967da15ca 100644 --- a/openpype/hosts/harmony/api/pipeline.py +++ b/openpype/hosts/harmony/api/pipeline.py @@ -12,8 +12,8 @@ from openpype import lib from openpype.lib import register_event_callback from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) import openpype.hosts.harmony import openpype.hosts.harmony.api as harmony @@ -184,7 +184,7 @@ def install(): pyblish.api.register_host("harmony") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -198,7 +198,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/hiero/api/pipeline.py b/openpype/hosts/hiero/api/pipeline.py index f27b7a4f81..eff126c0b6 100644 --- a/openpype/hosts/hiero/api/pipeline.py +++ b/openpype/hosts/hiero/api/pipeline.py @@ -11,8 +11,8 @@ from pyblish import api as pyblish from openpype.api import Logger from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from openpype.tools.utils import host_tools from . import lib, menu, events @@ -49,7 +49,7 @@ def install(): log.info("Registering Hiero plug-ins..") pyblish.register_host("hiero") pyblish.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) @@ -71,7 +71,7 @@ def uninstall(): log.info("Deregistering Hiero plug-ins..") pyblish.deregister_host("hiero") pyblish.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) # register callback for switching publishable diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index eb1bdafbb0..7d4e58efb7 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -13,7 +13,7 @@ from avalon.lib import find_submodule from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, + register_loader_plugin_path, ) import openpype.hosts.houdini from openpype.hosts.houdini.api import lib @@ -53,7 +53,7 @@ def install(): pyblish.api.register_host("hpython") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info("Installing callbacks ... ") diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index ae8b36f9d3..5cdc3ff4fd 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -22,8 +22,8 @@ from openpype.lib import ( from openpype.lib.path_tools import HostDirmap from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from openpype.hosts.maya.lib import copy_workspace_mel from . import menu, lib @@ -57,7 +57,7 @@ def install(): pyblish.api.register_host("mayapy") pyblish.api.register_host("maya") - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) log.info(PUBLISH_PATH) @@ -186,7 +186,7 @@ def uninstall(): pyblish.api.deregister_host("mayapy") pyblish.api.deregister_host("maya") - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.deregister_plugin_path( avalon.api.InventoryAction, INVENTORY_PATH diff --git a/openpype/hosts/nuke/api/pipeline.py b/openpype/hosts/nuke/api/pipeline.py index cecd129eac..fd2e16b8d3 100644 --- a/openpype/hosts/nuke/api/pipeline.py +++ b/openpype/hosts/nuke/api/pipeline.py @@ -17,8 +17,8 @@ from openpype.api import ( from openpype.lib import register_event_callback from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from openpype.tools.utils import host_tools @@ -103,7 +103,7 @@ def install(): log.info("Registering Nuke plug-ins..") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH) @@ -129,7 +129,7 @@ def uninstall(): log.info("Deregistering Nuke plug-ins..") pyblish.deregister_host("nuke") pyblish.api.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) pyblish.api.deregister_callback( diff --git a/openpype/hosts/photoshop/api/pipeline.py b/openpype/hosts/photoshop/api/pipeline.py index 85155f45d6..e814e1ca4d 100644 --- a/openpype/hosts/photoshop/api/pipeline.py +++ b/openpype/hosts/photoshop/api/pipeline.py @@ -9,8 +9,8 @@ from openpype.api import Logger from openpype.lib import register_event_callback from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) import openpype.hosts.photoshop @@ -72,7 +72,7 @@ def install(): pyblish.api.register_host("photoshop") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) log.info(PUBLISH_PATH) @@ -85,7 +85,7 @@ def install(): def uninstall(): pyblish.api.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/resolve/api/pipeline.py b/openpype/hosts/resolve/api/pipeline.py index 829794dd41..fa309e3503 100644 --- a/openpype/hosts/resolve/api/pipeline.py +++ b/openpype/hosts/resolve/api/pipeline.py @@ -11,8 +11,8 @@ from pyblish import api as pyblish from openpype.api import Logger from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from . import lib from . import PLUGINS_DIR @@ -46,7 +46,7 @@ def install(): pyblish.register_plugin_path(PUBLISH_PATH) log.info("Registering DaVinci Resovle plug-ins..") - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.register_plugin_path(LegacyCreator, CREATE_PATH) avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH) @@ -71,7 +71,7 @@ def uninstall(): pyblish.deregister_plugin_path(PUBLISH_PATH) log.info("Deregistering DaVinci Resovle plug-ins..") - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH) avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH) diff --git a/openpype/hosts/tvpaint/api/pipeline.py b/openpype/hosts/tvpaint/api/pipeline.py index 46981851f4..46c9d3a1dd 100644 --- a/openpype/hosts/tvpaint/api/pipeline.py +++ b/openpype/hosts/tvpaint/api/pipeline.py @@ -17,8 +17,8 @@ from openpype.api import get_current_project_settings from openpype.lib import register_event_callback from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from .lib import ( @@ -81,7 +81,7 @@ def install(): pyblish.api.register_host("tvpaint") pyblish.api.register_plugin_path(PUBLISH_PATH) - register_loader_plugins_path(LOAD_PATH) + register_loader_plugin_path(LOAD_PATH) avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH) registered_callbacks = ( @@ -103,7 +103,7 @@ def uninstall(): log.info("OpenPype - Uninstalling TVPaint integration") pyblish.api.deregister_host("tvpaint") pyblish.api.deregister_plugin_path(PUBLISH_PATH) - deregister_loader_plugins_path(LOAD_PATH) + deregister_loader_plugin_path(LOAD_PATH) avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH) diff --git a/openpype/hosts/unreal/api/pipeline.py b/openpype/hosts/unreal/api/pipeline.py index 7100ff3a83..9ec11b942d 100644 --- a/openpype/hosts/unreal/api/pipeline.py +++ b/openpype/hosts/unreal/api/pipeline.py @@ -9,8 +9,8 @@ from avalon import api from openpype.pipeline import ( LegacyCreator, - register_loader_plugins_path, - deregister_loader_plugins_path, + register_loader_plugin_path, + deregister_loader_plugin_path, ) from openpype.tools.utils import host_tools import openpype.hosts.unreal @@ -48,7 +48,7 @@ def install(): print("-=" * 40) logger.info("installing OpenPype for Unreal") pyblish.api.register_plugin_path(str(PUBLISH_PATH)) - register_loader_plugins_path(str(LOAD_PATH)) + register_loader_plugin_path(str(LOAD_PATH)) api.register_plugin_path(LegacyCreator, str(CREATE_PATH)) _register_callbacks() _register_events() @@ -57,7 +57,7 @@ def install(): def uninstall(): """Uninstall Unreal configuration for Avalon.""" pyblish.api.deregister_plugin_path(str(PUBLISH_PATH)) - deregister_loader_plugins_path(str(LOAD_PATH)) + deregister_loader_plugin_path(str(LOAD_PATH)) api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH)) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index 3ff3638a23..e204eea239 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -20,8 +20,8 @@ from .load import ( discover_loader_plugins, register_loader_plugin, - deregister_loader_plugins_path, - register_loader_plugins_path, + deregister_loader_plugin_path, + register_loader_plugin_path, deregister_loader_plugin, load_container, @@ -64,8 +64,8 @@ __all__ = ( "discover_loader_plugins", "register_loader_plugin", - "deregister_loader_plugins_path", - "register_loader_plugins_path", + "deregister_loader_plugin_path", + "register_loader_plugin_path", "deregister_loader_plugin", "load_container", diff --git a/openpype/pipeline/load/__init__.py b/openpype/pipeline/load/__init__.py index eac303c10c..6e7612d4c1 100644 --- a/openpype/pipeline/load/__init__.py +++ b/openpype/pipeline/load/__init__.py @@ -32,8 +32,8 @@ from .plugins import ( discover_loader_plugins, register_loader_plugin, - deregister_loader_plugins_path, - register_loader_plugins_path, + deregister_loader_plugin_path, + register_loader_plugin_path, deregister_loader_plugin, ) @@ -72,7 +72,7 @@ __all__ = ( "discover_loader_plugins", "register_loader_plugin", - "deregister_loader_plugins_path", - "register_loader_plugins_path", + "deregister_loader_plugin_path", + "register_loader_plugin_path", "deregister_loader_plugin", ) diff --git a/openpype/pipeline/load/plugins.py b/openpype/pipeline/load/plugins.py index 5648236739..601ad3b258 100644 --- a/openpype/pipeline/load/plugins.py +++ b/openpype/pipeline/load/plugins.py @@ -113,13 +113,13 @@ def register_loader_plugin(plugin): return avalon.api.register_plugin(LoaderPlugin, plugin) -def deregister_loader_plugins_path(path): +def deregister_loader_plugin_path(path): import avalon.api avalon.api.deregister_plugin_path(LoaderPlugin, path) -def register_loader_plugins_path(path): +def register_loader_plugin_path(path): import avalon.api return avalon.api.register_plugin_path(LoaderPlugin, path) From 0a7cbeef6df772531270755c93120dcb4fa20fad Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Mar 2022 14:17:24 +0100 Subject: [PATCH 241/302] flame: refactor to settings configurability --- .../publish/collect_timeline_instances.py | 103 +++++++++++++----- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index f41f773802..e54ff9a167 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -7,6 +7,10 @@ from openpype.hosts.flame.otio import flame_export # # developer reload modules from pprint import pformat +# constatns +NUM_PATERN = re.compile(r"([0-9\.]+)") +TXT_PATERN = re.compile(r"([a-zA-Z]+)") + class CollectTimelineInstances(pyblish.api.ContextPlugin): """Collect all Timeline segment selection.""" @@ -17,6 +21,16 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): audio_track_items = [] + # TODO: add to settings + # settings + xml_preset_attrs_from_comments = { + "width": "number", + "height": "number", + "pixelRatio": "number", + "resizeType": "string", + "resizeFilter": "string" + } + def process(self, context): project = context.data["flameProject"] sequence = context.data["flameSequence"] @@ -137,43 +151,78 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): def _get_comment_attributes(self, segment): comment = segment.comment.get_value() - # first split comment by comma - split_comments = [] - if "," in comment: - split_comments.extend(iter(comment.split(","))) - elif ";" in comment: - split_comments.extend(iter(comment.split(";"))) - else: - split_comments.append(comment) - # try to find attributes - attributes = {} + attributes = { + "pixelRatio": 1.00 + } # search for `:` - for split in split_comments: + for split in self._split_comments(comment): # make sure we ignore if not `:` in key if ":" not in split: continue - # split to key and value - key, value = split.split(":") + self._get_xml_preset_attrs( + attributes, split) - # condition for resolution in key - if "resolution" in key.lower(): - patern = re.compile(r"([0-9\.]+)") - res_goup = patern.findall(value) - - # check if axpect was also defined - # 1920x1080x1.5 - aspect = res_goup[2] if len(res_goup) > 2 else 1 - - attributes.update({ - "resolutionWidth": int(res_goup[0]), - "resolutionHeight": int(res_goup[1]), - "pixelAspect": float(aspect) - }) + if attributes.get("width"): + attributes["resolution"] = { + "resolutionWidth": attributes["width"], + "resolutionHeight": attributes["height"], + "pixelAspect": attributes["pixelRatio"] + } return attributes + def _get_xml_preset_attrs(self, attributes, split): + + # split to key and value + key, value = split.split(":") + + for a_name, a_type in self.xml_preset_attrs_from_comments.items(): + # exclude all not related attributes + if a_name.lower() not in key: + continue + + # get pattern defined by type + pattern = TXT_PATERN if "string" in a_type else NUM_PATERN + res_goup = pattern.findall(value) + + # raise if nothing is found as it is not correctly defined + if not res_goup: + raise ValueError(( + "Value for `{}` attribute is not " + "set correctly: `{}`").format(a_name, split)) + + attributes[a_name] = res_goup[0] + + # condition for resolution in key + if "resolution" in key.lower(): + res_goup = NUM_PATERN.findall(value) + # check if axpect was also defined + # 1920x1080x1.5 + aspect = res_goup[2] if len(res_goup) > 2 else 1 + + width = int(res_goup[0]) + height = int(res_goup[1]) + pixel_ratio = float(aspect) + attributes.update({ + "width": width, + "height": height, + "pixelRatio": pixel_ratio + }) + + def _split_comments(self, comment_string): + # first split comment by comma + split_comments = [] + if "," in comment_string: + split_comments.extend(iter(comment_string.split(","))) + elif ";" in comment_string: + split_comments.extend(iter(comment_string.split(";"))) + else: + split_comments.append(comment_string) + + return split_comments + def _get_head_tail(self, clip_data, first_frame): # calculate head and tail with forward compatibility head = clip_data.get("segment_head") From 372d686024ffb53be947441be48dd014e5608feb Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 15 Mar 2022 14:24:36 +0100 Subject: [PATCH 242/302] Fix - Harmony creator issue Creator failed with 'str' object does not support item assignment --- openpype/hosts/harmony/api/TB_sceneOpened.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/harmony/api/TB_sceneOpened.js b/openpype/hosts/harmony/api/TB_sceneOpened.js index 5a3fe9ce82..6a403fa65e 100644 --- a/openpype/hosts/harmony/api/TB_sceneOpened.js +++ b/openpype/hosts/harmony/api/TB_sceneOpened.js @@ -272,8 +272,8 @@ function Client() { app.avalonClient.send( { - 'module': 'avalon.api', - 'method': 'emit', + 'module': 'openpype.lib', + 'method': 'emit_event', 'args': ['application.launched'] }, false); }; From d408139bb7a9753e0892d648819af4db6093e9e9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Mar 2022 14:26:08 +0100 Subject: [PATCH 243/302] flame: restructure data nesting for better absorption to instance data --- .../publish/collect_timeline_instances.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index e54ff9a167..72ad2cd1c3 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -153,7 +153,8 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # try to find attributes attributes = { - "pixelRatio": 1.00 + "xml_overrides": { + "pixelRatio": 1.00} } # search for `:` for split in self._split_comments(comment): @@ -164,12 +165,14 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): self._get_xml_preset_attrs( attributes, split) - if attributes.get("width"): - attributes["resolution"] = { - "resolutionWidth": attributes["width"], - "resolutionHeight": attributes["height"], - "pixelAspect": attributes["pixelRatio"] - } + # add xml overides resolution to instance data + xml_overrides = attributes["xml_overrides"] + if xml_overrides.get("width"): + attributes.update({ + "resolutionWidth": xml_overrides["width"], + "resolutionHeight": xml_overrides["height"], + "pixelAspect": xml_overrides["pixelRatio"] + }) return attributes @@ -193,7 +196,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): "Value for `{}` attribute is not " "set correctly: `{}`").format(a_name, split)) - attributes[a_name] = res_goup[0] + attributes["xml_overrides"][a_name] = res_goup[0] # condition for resolution in key if "resolution" in key.lower(): @@ -205,7 +208,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): width = int(res_goup[0]) height = int(res_goup[1]) pixel_ratio = float(aspect) - attributes.update({ + attributes["xml_overrides"].update({ "width": width, "height": height, "pixelRatio": pixel_ratio From 48ce34c58e960e458676bf215b21fb5416ad960d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 15 Mar 2022 14:26:47 +0100 Subject: [PATCH 244/302] flame: add xml_overrides to extracting profiles --- .../flame/plugins/publish/extract_subset_resources.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 5c3aed9672..194557e37a 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -1,9 +1,11 @@ import os from pprint import pformat from copy import deepcopy + import pyblish.api import openpype.api from openpype.hosts.flame import api as opfapi +from pprint import pformat class ExtractSubsetResources(openpype.api.Extractor): @@ -131,6 +133,12 @@ class ExtractSubsetResources(openpype.api.Extractor): "startFrame": frame_start }) + # add any xml overrides collected form segment.comment + modify_xml_data.update(instance.data["xml_overrides"]) + self.log.debug("__ modify_xml_data: {}".format(pformat( + modify_xml_data + ))) + # with maintained duplication loop all presets with opfapi.maintained_object_duplication( exporting_clip) as duplclip: From 0f67d46ae0c7899f33b165600898e6743b1782e1 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Wed, 16 Mar 2022 03:37:11 +0000 Subject: [PATCH 245/302] [Automated] Bump version --- CHANGELOG.md | 17 ++++++++++++++++- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5acb161bf9..7790894b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,23 @@ # Changelog +## [3.9.1-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) + +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...HEAD) + +**🐛 Bug fixes** + +- Harmony - fixed creator issue [\#2891](https://github.com/pypeclub/OpenPype/pull/2891) +- General: Remove forgotten use of avalon Creator [\#2885](https://github.com/pypeclub/OpenPype/pull/2885) +- General: Avoid circular import [\#2884](https://github.com/pypeclub/OpenPype/pull/2884) +- Fixes for attaching loaded containers \(\#2837\) [\#2874](https://github.com/pypeclub/OpenPype/pull/2874) + +**🔀 Refactored code** + +- General: Reduce style usage to OpenPype repository [\#2889](https://github.com/pypeclub/OpenPype/pull/2889) + ## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...3.9.0) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.0-nightly.9...3.9.0) **Deprecated:** diff --git a/openpype/version.py b/openpype/version.py index d2182ac7da..17e514642d 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.0" +__version__ = "3.9.1-nightly.1" diff --git a/pyproject.toml b/pyproject.toml index 681702560a..128d1cd615 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.0" # OpenPype +version = "3.9.1-nightly.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From db7e9cc4aa6b4fd09496139d2fff878e3606312f Mon Sep 17 00:00:00 2001 From: 2-REC Date: Wed, 16 Mar 2022 12:13:47 +0700 Subject: [PATCH 246/302] Warning log if more than 1 shape id --- openpype/hosts/maya/api/lib.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 62de5a96eb..f49c0f689e 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1809,12 +1809,22 @@ def get_id_from_sibling(node, history_only=True): # Exclude itself similar_nodes = [x for x in similar_nodes if x != node] + first_id = None for similar_node in similar_nodes: # Check if "intermediate object" if cmds.getAttr(similar_node + ".intermediateObject"): _id = get_id(similar_node) if _id: - return _id + # Check if already found an id + if first_id: + log.warning(("Found more than 1 matching intermediate" + " shape for '{}'. Using id of first" + " found: '{}'".format(node, found_node))) + break + first_id = _id + found_node = similar_node + + return first_id From 802f8a482d6278f2184b0f38ad52fc56efb737cb Mon Sep 17 00:00:00 2001 From: 2-REC Date: Wed, 16 Mar 2022 12:16:33 +0700 Subject: [PATCH 247/302] Variable declaration --- openpype/hosts/maya/api/lib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index f49c0f689e..e2c07624e6 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1810,6 +1810,7 @@ def get_id_from_sibling(node, history_only=True): similar_nodes = [x for x in similar_nodes if x != node] first_id = None + found_node = None for similar_node in similar_nodes: # Check if "intermediate object" if cmds.getAttr(similar_node + ".intermediateObject"): From 47cb270532a3651099a9f86899ad23b2c70c9f5f Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 16 Mar 2022 09:56:46 +0100 Subject: [PATCH 248/302] add integrations and users --- .../src/components/BadgesSection/badges.js | 61 +++++----- website/src/pages/index.js | 111 ++++++++++-------- .../static/img/LUMINE_LogoMaster_black_2k.png | Bin 0 -> 287334 bytes website/static/img/app_aquarium.png | Bin 0 -> 74514 bytes website/static/img/app_flame.png | Bin 0 -> 74845 bytes website/static/img/app_shotgrid.png | Bin 0 -> 3794 bytes 6 files changed, 98 insertions(+), 74 deletions(-) create mode 100644 website/static/img/LUMINE_LogoMaster_black_2k.png create mode 100644 website/static/img/app_aquarium.png create mode 100644 website/static/img/app_flame.png create mode 100644 website/static/img/app_shotgrid.png diff --git a/website/src/components/BadgesSection/badges.js b/website/src/components/BadgesSection/badges.js index 4bc85df2ef..5b179d066d 100644 --- a/website/src/components/BadgesSection/badges.js +++ b/website/src/components/BadgesSection/badges.js @@ -1,58 +1,63 @@ export default { upper: [ - { - title: "License", - src: - "https://img.shields.io/github/license/pypeclub/pype?labelColor=303846", - href: "https://github.com/pypeclub/pype", - }, - { - title: "Release", - src: - "https://img.shields.io/github/v/release/pypeclub/pype?labelColor=303846", - href: "https://github.com/pypeclub/pype", - }, - { - title: "Requirements State", - src: - "https://img.shields.io/requires/github/pypeclub/pype?labelColor=303846", - href: - "https://requires.io/github/pypeclub/pype/requirements/?branch=main", - }, { title: "VFX Platform", src: "https://img.shields.io/badge/vfx%20platform-2021-lightgrey?labelColor=303846", href: "https://vfxplatform.com", }, + { + title: "License", + src: + "https://img.shields.io/github/license/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", + }, + { + title: "Release", + src: + "https://img.shields.io/github/v/release/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", + }, { title: "GitHub last commit", src: - "https://img.shields.io/github/last-commit/pypeclub/pype/develop?labelColor=303846", - href: "https://github.com/pypeclub/pype", + "https://img.shields.io/github/last-commit/pypeclub/openpype/develop?labelColor=303846", + href: "https://github.com/pypeclub/openpype", }, { title: "GitHub commit activity", src: - "https://img.shields.io/github/commit-activity/y/pypeclub/pype?labelColor=303846", - href: "https://github.com/pypeclub/pype", + "https://img.shields.io/github/commit-activity/y/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", }, { title: "Repository Size", src: - "https://img.shields.io/github/repo-size/pypeclub/pype?labelColor=303846", - href: "https://github.com/pypeclub/pype", + "https://img.shields.io/github/repo-size/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", + }, + { + title: "Repository Size", + src: + "https://img.shields.io/github/contributors/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", + }, + { + title: "Stars", + src: + "https://img.shields.io/github/stars/pypeclub?labelColor=303846", + href: "https://github.com/pypeclub/openpype", }, { title: "Forks", src: - "https://img.shields.io/github/forks/pypeclub/pype?style=social&labelColor=303846", - href: "https://github.com/pypeclub/pype", + "https://img.shields.io/github/forks/pypeclub/openpype?labelColor=303846", + href: "https://github.com/pypeclub/openpype", }, { title: "Discord", src: - "https://img.shields.io/discord/517362899170230292?label=discord&logo=discord&logoColor=white&labelColor=303846", + "https://img.shields.io/discord/517362899170230292?label=discord&logo=discord&logoColor=white&labelColor=303846", href: "https://discord.gg/sFNPWXG", }, ], diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 29b81e973f..e01ffc60e1 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -129,6 +129,11 @@ const studios = [ title: "Moonrock Animation Studio", image: "/img/moonrock_logo.png", infoLink: "https://www.moonrock.eu/", + }, + { + title: "Lumine Studio", + image: "/img/LUMINE_LogoMaster_black_2k.png", + infoLink: "https://www.luminestudio.com/", } ]; @@ -275,107 +280,121 @@ function Home() { diff --git a/website/static/img/LUMINE_LogoMaster_black_2k.png b/website/static/img/LUMINE_LogoMaster_black_2k.png new file mode 100644 index 0000000000000000000000000000000000000000..37ef01486dccb2fcb9f17818a8de58bf23b32a9b GIT binary patch literal 287334 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

Ch{idEs);7-#$f9MA8J*$p|0dfp1S-2 zx>W;zL))2U4FWCYaSuJs!FnhEQ0Od=Ps%yaL-PKoge<7q1wsyX>MD7@Y5iu8{w<9& zV!k!&D0YD09fcu7$o!@k2-Wou^MR{%CAh|j4g5svUz@RTN88cbJ~g&FVCb&-M+!RV z-B?n7`%jV{LacA%DL6lHVeO8HZIugm_J++D3L-}h&_E@)3QG$NQ&noF8KNlXsaK(Q zayt6Tl(ngc23wVdrSU(_vRB@&E^**?c!VP%37 zF;>vL;BwK>G5sv94XJmj&!2mlxApkhbXRo&vKX#2-nEVL+yaXW*u|=AVDwK=ql>QZ zvXpjnMqI)4Dq1|@p+#6x^P-j&$~s>dZzf2zfm-5Lj+{K`GCQ7t!t=D+LGYVb3F$7k z0Q-$jPw9{drFO8L#2uT!(WH$3T(b$Cc*the0ba#l;ZmW$!WOlZ_FM7Yhi4%4fDdF? z!?D%`6Q5QPDH_&R{H16~d3L_ zSI2W`lL{Bvir-#aM`lQWhM6}otuTuTS%fL#$~VmwcnV`3NUX8L4Q}Zr8KBASHaq3V zGSs$S*ejZ3pocz-xO&7!w`9@r3Y?kGpb1Ic8FeF96eZZ(m{~Q|fMI9}WGmyUINf~o z1{!C=;d>&rVMe8b$E|;`6OPbhN&HAZP>`^oAzodaLHp|!5Fw@|S4*B1o&RR>m96`Q z`ay0=;o(9&RU_Xqp8ZAijT-9eO=iRIp<9$F!wwTFy>I1`B(PX(rYxe9$CTRxZmtNk z^N)brYbzIzX{AdaZ@aA#J9reUl*&~l<127Fdg@`%$e5!ry0r@Iu(IpV1qYR2Vv!Yn zuDNM+>i1^84Wqo=^{_5}^f8a=19R24nWc!rV`_uh9&LFO+^i*$a0Lz31UFT8X<2@B z_K&Z+ghJqdMnDGR(O02ebdV>e4U#8{R9-Aszx}nIMhfcft9FpV-A}QLkR~W-8YV2{ zaAl(=Xt*=$h!r5MR7VL{e(P~2uo1`f$j0yJobv8o5CXY&feJGq6r1JYBO|%IZRz^) zZx0Yma|3s4LY?Y-QnmVOXX6D2ox@)98pNtJQFY`f%Zbi*HfERy#F@9eQckwUy4$TP zM5Vg$b?SD8npV2`ND|33HL$uFjqHbLn;{w=SeQjw?py_4R0etFb%tc z$1r)kBrEa`b7*s}24`d3S61qw{r~U?>41+QuU@UXs=LgV5ifJIA!}*8{+WEO{H;V> zhv(ymBnGf-!sUel415+lVM@?TfMa7FPM6cwZcZp%&%lLoJtaMIuRv_J=)IbDSPRXJ zl4dD8G-{BVD$ThF5P^n{{IRJjo4N;9CNq2CXwwl6SJu)kWkTiIXoo7(kYz@nJDFOY zy5bdaQWtd~M2!w~i!3Rw%Dg$FeJc_wveqqeJNfkFLF*1=pU6{;s>J5Oc=PuCAPUVT z^@&W$1H>o*&P`yA^n0q>afDaiWimy9DnVE~qskTYqvznfw}so~^iUXW0To=2sk#ja z!~JBb=aqLy_YJ7Y`JqJ77BRRU;=pB79p|-_RvWC6X!t#rcV*(oiJjufXXRG#+r&&?9d5%+4V>GIvtm|Jvi6(SFNp zi{v+JROR9ZY?Ku|!$2%6$dEvtXS*K5y4W&g*We|%?Kb{29bVHx3V!i6+V*5vC(3<0 zg+YlMT6tbWQ53kF8ntUJr9@gAY-QloH_fw%L*tbYM@%hd(lBTXa|F-oI2zKeP7;RS zM=ft4Hi$$cpTag$>bK2CRI3YPZZYAb`FczQ*O$Ti5g0@vEasua#=e>~B z$a>K`BdAJJRElN+N%cu>Lg)q2`~7Tx7w7fFgbEMM+*QT@OHm%K#g>!pxx_AZYX zqb{;){D?})o=Kv3CBa+Ux4qA^SR?s;c^#xf&OUMabosVVfAeBCpw~!xbunycq+ouz z(Hw%GWFrRujO{>XE*bkp~`0_AfSw@Vtg z+4-x8vqQ(pEfyInfqJX1I(8m|^6jG0@~-vB^3tsk04d7HZt&XqSOLCwd(a6T=bspw zT)0{*Ilc38{|iS_zSJ9y+PpgMn;Mrx^?Y7!1vp<+0E}@Gy4c`Kn@weIEj z%JM;mkII&%;G##lkhZ+!^>a`xP9IoWAMufkIZx5ZM03D3_g@MMwX*P(VsH-0)2q3;m7 z-}<(H#iUya#e&-qka}9biu_VtXe-H6EX2`AR3z5(&q{Ce8Yu8|)8lmLxSZ#!e+@`{ znFjjGC1%6pYl&5w+JyF>w+?vi%&Ho{(48Lt4oii;S*&)J7eX$TC3~OzK>Qr=4T_+zu@E4U^Ufq z3@JPG2V4-64Ip)uC?B4?5mnI{(fKF#py}lzY|wYW9V!%ro)MdDH=sfLUzlvVxK=jR zpnTt9bsIVq4B_o8O(?g7O+Ab6h_%rkj`GOM{deT=KM_a|5C5Tv^jMofi{Z6&d_Vnd z-6-~MZ807j{%%;a3rU@Z@(?X=H<6$W%!+6GmG4ZBc;ya}-DfM?qIZw=l~!c1f-QCQj#xtOxr(YM3HYgiW(9d0+j5M14 zhT0sw_=cMMAJl2!T8V&;V*S&hd6JK9-+!p7d?b0HIJ5#|HuN|Widr+yV-VBI zwe(oZfL{l1w=8t=6@E`v1)nt)mR3H}@>QZ%$=} zN}K;PDjXZ&q%+?E;WL}(;^8e?z>S7CWWU>OQJR&9M+hn$oQLfb^E=d;xA3fLrM^b) zD%km#IZ8qjdcpRIWTe_P*4*oHMcnSdF?W*K=|QPy8t_xrhWcM{6OHG;>%onAmpBTv zfmIM?*FK??jTLeLlN5h-6kVW!rNJG|)HpU|9Ow9OJLYM7}-f2aaNzB(; zL8g}nnaWN}54n!<^~r)1-k}wo)Nw0d*3r4jsg-r#M_5mkIakIB?5UMufrYP_mpTb= zpFGN4XU0*I-H!_F9qP;4wt*hGw8htaVTYqewtg?;fL&e3;5C$Cs5#PJblV+!=$4`C zPO-nK4~WD+Jq5$LQb*IlS}8?=#T{VPjm2{7iD#UNoSzw-hpuz5YJ(m6z#X6z4E zgj?gM~G>dby`S}EB#yq*P(+aGz;Hb`W3O~K>)G#kF6)sJNyI(MA@ zx~##GY(n+Np2K;ny4ggO@CV2(_Z;jUp00*2pIcDEZ6HBpF6ynL`2nvZ5E+ z_ZPM|B=>Y)boH}loiFteMM;58<#pMIenfZZdzk3)Drd|<5-=&SpFu~f(N}o8lV+4E zC!ju9oL~1)*?01>@{y=VadDZjs(-9RtLsIRn$S}IYg=^aoBhjmYRSoCFyDf*CMFp! zUa01)>M3MLgnk;dG@tytOrBcZIo!(A>bWu07g?APdfS>L|1f zs0Pd^&p;cSSZ>e3PE>w?Ho^Ld!a5y}pF>XFDs}nPGoDz>rTw*mP0DR%FwiAFDteC~ zMO8q1MZsSEWvbdR*wwsSKw=;!l-lu%6}XUe{w#XKWchK;Ln%o@YCs+FV_HSZNOudr zp>n#vEjrRk*n-s2fG_INTj29cpa}J=LjS)bmOd1U0(Xlq@2ourk{}Q_NLDDJnf(t4 z(p-RaH6!M%Clc<1*za$d3zdzhEuqWA9eKz{cZD$0_v}JL+zP~4^4uMTh-n{;c%+en zwOU9ZL&qOURj=aG9`eI*pm4xd6&(<*Q^$BNWM%Ly-&&%LOQ3aC#EtwiISq-;_8_|UwC+`ROLjo5pv7W*x0%(N4UJcCKp$&be4y0A*-C=%u4(dGYNPE9Sa)p9P zBLeytk`AZoLT~IBnggVlnS=@LQCV|MiI%MJohGTJy%d;v!jDJZr8Wa+1~gDUj6p`d z5jOg)jSDY@`p;v~^JoJ;>xUQ(v#fB&0x>xGwAAxi#XuFWon;T|mSX`1Km`jN z@qBGW;ujwP7`9FON&P-L<;AMT#!vf9~Q>)2QE%vD&~y!=1|i+oE?nvB7*sV(Piels|;ChlPbI3yjkqA z+Br|1B{8Zw@n`@Y=T(H*6O^@=@6oFawlpMda?Z z08jg`sUJ%bjc`**9Y#A>AB(-RN69s! zQj&$f@;{iJ1N|i22SwNvhqqb9WIJ=FMJVteegQ%eY5JmI#nR?N9H%LL5@S9zk+q8b zwV~oMBCf=viZ{XDf=XrAN7QlS;GR#y^h=&&B>G}sp2*CvubgKWYEouJ~`XXg~jLRFp55$*P|rbrvwqN<$wt|9{|5GmN= zV>%P5<7voSg1z$T13P6{Ij~`OGmRn|GwF|b>J9Xe_p|je;eLL7>L?$W zC@+M(C_s`|W7Nv*<(NY;w1qZV!FA+jCxknCIS{w_sc#ahR$~FmFqwV4gyTb!5bsO= zI^d}CX0-)C)~TcWh#If%onpo46Z0OX#F(^K%qrQ5vs;&1PZ?P}A#T_6VCyyogCrX?Je;N$kS0xuhh5w2{(cQK zh^h2xL7{oKcpiO09&oN*;YM90E4nJs5aYuvW+QR}h8NF4dwLZ0V@=h9=)n`fv|uN& z))2@i-(rrCM{P?*3`T^FnV(p}kM(;m!=CU3roO+oeNVp?UlY++0U)%z8pHb!O8xct z**Ej-;hM2j%^v;AV@^wO^##fN6e_m>8*%ly%IpeFzNw@hjAy#6hFo;{+;N;dU*Ll1KGA`VuiHvg zZoJ_?n9kbn_1nP=NE7;3@=q+Yj&H)TS4&^kq764Xsxc5IpG#(mVzX8)Z=RoCR~d=+ zlGQA`E2uA8&#fa4EQdAQ{qomVGNITL1!CDDWpcy*76VSh+mP`|Qt$*rXhjAc=5XYJ z!R458Awh#3$W7bap@c^wn!V^2FgK6a-7u&lPFz7beTwYG_u7PhnlR^rpH*Y3@(Kt@{ zHaIiM(}T>79g|piN^}$`WkeZ3hER7r^J^`y2LzSpQyz4JueS2*&JOQp`N zyiQZ;8R@!Zv%X}$Le!~NHATp_47(7tTL%b4nB53}X^9?=?0Zv<>Vw9=!ZgBrk`#CA2`AT$>@M2!EYB>n&3ekOBOt3KDsolE>!UXgNYI7bt8hMN!P z>Gv@bjmYWp7!VAyvyX^~lhL@-Cf^C$%dfG$j3S%kO(Y)-P$ND*()bU3KDzJ2sh*1*Quefo8ALTS6}9&nlskX%L9KlGcoLVTE6)j zO1}}fIuEyq3o@k}{cwFtg^#F4{A6gQF+nWjOSt)u!26f*Q<(42o)@xG{u5pY1LKxR z2-$$quGC+GdO;c9&c&3{01f(ZhWVmUL&@km4AXQ=n;H<85O#nq@nR^NNI%SXj32Ld zfc~W8PhiI)lYtnqZ(#3U&4ySbbVsT3lTIiQ7%o#O8`)bHLsBQ8?sMkjA_Cf5m6@Kc zyzd}C48Hy9X!vt$vXB-)V0zru#s(W*Ku`VMw$r7@#QPjVbuy`V|n+Df%slt^#CZ>(bX zuHZQFGvy+G;$?f-;W^xE}@GNh-^D+&=WQWRC8m5I!c zZv*m`73;NY!3NsALL)!D=B?5m)O6`c(V5*Psk%9EOyHz>h?gpSY2M9Yg%g)rJ#HXP z?y{i7NZw_E6md~@s)772bz}I5T;4wI?7DfN{ub*bF-!~r6a#c3DZMhlullL9^>>6& zQM4lyVu3cE*X$KvI<3Fa3EA9<<>7`?7^;2We)=A7t2^3}GV^2eagx{!tEF6V(`@(= zQvC5-Vd+F?hdq0rQ7a)RGFkcv;L^!j5-q&zja&R==V6Y;II#VI+<+2R>F+b%=TxOJ zr=aHttlWPlSR`g)@zx&TRjnNu4VO6~i|lgh)_l4>Kqioge%9{b#V&@YnTt*)e`VOa z-?&~@*0K*|+Q!6mc(EZCn|MCHwL!jB!hSscI=YfS{;kW2ee>A8)12L?Lw!zAc2%W+ zEa?kF+S47|cUMfpqtELineGX|i_!t%VJC=~Jf0;4`-+@yqI*_sg6-MjpH$hmdGRQCtq!TJ%HjROQxf zdu#^(#;sf-Ha9G*%mt|2e_ZkhkGvn80r>4Hm?@+{EXj*O`zrVN=Y*M&MI^Cnf#k0% zUV0G)ef7M3?m!@K*m>*OT1hS~3X*;Ux0Mj}cZ$TZpcV9cD!QTN$AF|Ro;mPh`n5Jg zB5&Pfe-vjpQ&r|D5)-i2M9_>LI`S6sdm;e%!asB z(fcuoOdwlhx;J(OZ$dlk_YM2Ev8yTb)oc9X^){obyiw((@1C#RCn`m|u(MBwbgx5N zw7-R=ajGf`(Mrn|E6qcL@xthbJtFcM;!?Z8%`RwhNZs}if(iNU4`!{xU!q#ZQe|W`<;Zd&I}(AB&EsU$tdHnF<|uT3=ZNKCQEM;@ zi5RW-B}v9EYv2~&W9XyxMUEUR%-ddohzW9Qrdz5xUs8K+N<%c|Cn8G0kJJIlr2d3E zX!CP#fgIt|+_A{*J(hko>RNMN5#=a5{z@AL%~bL$))bEY8}xZ+3PRrqkQ`Qt{#9Mc zWp#mERDxDMMqEe_hrlTqo_{c|eK)jg8WpRo9djO@s<8gqnatK-Sa@~9JxQX0)mMgl ztESyiK#0KVAMAf5_3i>uOLMTHk0r$k)J(G-jVvZxCZlgbE3qrh$JQY}Za+XM7$Y%I zs@ckY*p8*$fV%hL+Odcjk`Sh*hRb#>^$0>$W-((OKY{rxfuVhd)~PGm43>@tY}50{ zQl|fI`yThEs--)Ofrcjuz_J5NIh9=xls2F66eQ(j)-<=|*U*z826``k zxaoTwHzk}5#Zw06V>PQ8E;eimG!eAx4*mNr*(b6HUBB!X1<&0Nb1loEGy3^E!ZBK` z`Pkix)dC(dxhGOlhwdAv5~<^YIebV@o~hy44Why|%G;_GZtprw$H->{Bgn%cw9U!o z4|l)`UiW|EpuE$yoLemJuIi>BF*$-bxwz?&y?V-VA%?MXj^xapP{T{L~S zWKdlwBB4yMi+|;Ipcc|e^H#cjTSLDc166CeEt240n5bB~m_2UO?Wj}bY-&jKc}u0~ zj6T^MrnAK@?kjdmuM9tH6y0EPDhndT>vcK5qT4En#o{6w)HO^Exr*>ZqSG3{3CGUZ zB~DU?;0Ye!DHOZY@=va%j5Yo=g30Fgg;Al=(INFKi*Z~*bhsUNLqj-lRF~l8y zhk?cvuHmNu#>0d{|`}b9?;ae^pD3{K@t@el}$mwV+;Woc8H3AKuAy^L4gDa z5FjD>&T~%R-|znM-uqgcbI$Y3@|l^>%)GUgUlo>(=g*_A6s)p@$cNJf3kb(g7rSEd z4c0ZJwqKH)bz1F%s*PYc&N$nk+l;0Bd+clTJ~?1zT!bwqS6qzgkQEa1+_#&7tXbD; zYBaE~1r`Ljl-N1=*&M=UaBrqQY9w zZnQSh>}Pw}#C8_;m&2cXO97wDW)Do~xTzW-hO}-CivX=*xBEd2&Zm7TgU3R}#qq~& z5(bUA6M~!-WkhzYdJ-XoGe9tV9a#KTB5jimlmlRqUi%ItiZ}M-qS#Io`l|ZP8$-xJ zMNWCoZkoaZy>l!THO+2P*ZXvz?}#oO$c3o2^(zhXw3s;+E$>~?*YIZ zrRZ;m0n@~S7|W|1ml{vT%jW6)q?2T50&&zA&oT&UQFgL% zqph5+370&P9(Bv8IxHJMksx)Sj*$IIZmLk~1NJ^P`-%z7S8!mSn+IwG|1snfg}gNh z^I&k4#=u?zgQT#?FYkcr&U;ET)4>mOHNMOsS7dgeY_Ic_3x`c&Q%Oyx*KRKQdCcoZ1(p> z&Wlj4p$soIczc93|AARF-v@^OShYAuq}pSPet6kbql>EB1=WatVG~$<9Z^3d!-)K4MMwEYnc~vOf`ugN zgE-MG9FCXfxal{kmH?=NpFFrks|tj=VYPmz>IDL5l2AK<+cLzpbEtA;4s{oNy}b>!C1rP04UhWp{)GJH+yHYEU-I!tpWGO$){ts&X)S zq-po=Lbh89V|FhpUdz#65Ei<+B9H&_-zm*w4=?(jfF!{4ft%9NLLi1j_J6v{z>{&_ z`hz{5SP<-JiF*61&IO~b%E!xDvqG0imV0@od{sR1NS?=Fx)Vd$%{JmFBk}_5c2BI; z49P1!wZSNQ@S=ttR&^44l!`;2v}R)|MGxRNPq#LrFKP*T0I`{pL^)uNOXCGx8||eFeJMRiZkIupkw%fkGb>VaH+xP` zQ?64JY2J~O{4#wJn797V$3-T8(e%ybMrP+fIFj{VOc;yOLc8RO7A5QbQLHWqR(C}9 zD=w8W)XiXG;I~WT8=tRaD&RO{FPRipj!P%qox{m{VM@1e0>2R1e|%sY?0!ElowPF( zxAN!D;m*!m>aH!3j;lU%`X0b$4>M*_*kPne<)-+sR_`^C`0J1k$*)_8PWq|^t$TiW zeeBucjVewf!k&yU@dH++qj5|;9g^kg&~K{{Ky0}~bB=iP-w87s>k_bH>hqr^j4&++1GY-tICyY(+O!=bK`6VAIdwKL)} z#XkdR0A`I&VMhow`qVD@xX9)SXGALfZ1TR7JZ8w0Q^ZwNd@9n zObwa=4%YVqv4okd>e1FphtOp7$o{S?ehM#rGhr}Hr+Y31B;-#qGnXIwS<9oL`d+Fy zLHK4f7?+uKe?qmO5tSnA^bhcOHE9>&{My&S3bP!EW~Io+y0m2fuOxEBxObV9t;KWk zk!R~SYM#i9hVWO{WPVGp)sxT5nCs!5%?tPOs-evAbh%{RGm%7LlQF^jKbb`Vstl(& zqu&*8O+cfNieC}w5wnv3CvwU)sI&HD4YJFHh9u3nkYVxkfi*(ilUrlvS9>z4+!;3< zhNCT%ULl*0ZHZ-d9)4ORjhj+ zH<3NWLQb}QKH}6yiowSYEMoD;o=ZnJexaLD^GEL-7?YW#I~%jW z;!qe1&jXXK35lCrTUiH_^u6I#xBsjjPLad$wy8lG#Y&cb0~WLF0Pp1oUGI6!dCV0| zbmnu|%A(}?lGdzWfAW#1O4AstH!?+ca2YXkBV;5BZE|m9&Ht6cY_f;_8^!DUm5y|Y z1)f%kT>B(QZGTryKM-KDGW+^9yo5BKJ9G%!D@GV~#*C@kD>xNQM*0~XZTug#j!bqn zc&a8?eBty7!GI)@v!CPeb?`0D7cu(|R?tb~m6R~rW^)$o9}adhG$2%71QkAn>-wo$ z{SI~BXUG%wiDy-?R4Odm!yO+F5o=Ez^x1w3H%0bHFQ`b;cROoGOCd^!;~6OJxUEj5q5Lcdyq!Oc)MFr?*`wRPaaF00zx`cI@8+-u@8J%KQZD!`L0z zHnmC5usbaBBUvV!%q|xhqQN1-k8e&i?p4J&CsT3*Jf{X*b2cKY$oq2HsmVASt3kUG z`*NyB7^*7n_aGZq;r7Te58kXqts>rJVwVN^`7GKh?JG6PZLB{Nl)`Rz$L+vPSi*E8 zNjXOp6rR%&V_f&3MR#s34|XJ@;IPh4!f0#)fJBA@ZiFg1g~$p=5%zAEZ+97^a0FJq zlD4n6mm-RUOyq-yK>vm6L)L(_pM{xt&|F`Vo_1fA3aU|wVCBv#PZi2VCz)@9ZKgxU zS@6m=%K>a8{qMy1|8W68(bo`_uUOct{91NhY0j)l9dt(KdUvXLJ8tyeyAMvntYls- zCEia1;-o^*>$f)Dq7}?5GvU+P9-u`~oPT!I%v~pszwo!eEJ@c^4e+IDuu@uKgvDnhm)GS7vGirL;Z?WU5KOgc|Hb2n;t$8t zSPkX_QuS3ZW{FVu`)jBPhHwMLCH=20P}Zpb;+Lze zHydr=u}Sod2O`Nmr;pr~q-oFn!X23ED9}eR7i&Ku+(9z4%?puu4m6-cyS$NZJ?evR zYQ6?%?5Lp*`CMG7GJLWc7=xhz=E`~rk7SB>TbkkdDn4QwSBoY zXLo=})W^qy4nFe?o|>zEYAxaLvUW^}eEY;$M;8dU;jgiapgz5!0pIT+-{ew9wj;`$ zoqw%uKuljXo5)EavShtQ1*5?UTga>LB(DmS8{;U$)?nW*dhvwiDIgEtD|Q(7sCLu- z0SsE9CbEfJ2{bbnc!3E-L_byex!~m_@-ip(8eG88&z^HmRh9{^Q$8;bc9?eAxx<5b zL?z&Q@_v&5p0@17MpLncn;@H0oYXY_QMx>k*mSn!U&X$lEH~}U=DNejOp7uJ(_QQ( z(yTx%eMM&#J>AK)tI0nbhRg)C(i?4w`ATxh4hDe z#6}FC5*i7_x|rJ1abMc&0%Z_jHs*ok4c8NzXbbL5ZzrjPLTP$8-OR(H7ZsY~vJh|E z#QG|Qz5z~i-=T36Y*W!vNSA;5lK^=82yewtwo4WpygMII6xIVegbeKDz%4F8u_S!p zdtlOtmPs0ADU?xLxYF!RV!rONoS-_s5Y#XlH$kP}`|@0STV|S?h%Wvm;E^GO`t*uw zYCNT~vxS6`_lIhyrP^kDpFg5xL7}a^aryJ66{tP`3pUX(@E#G*LLNvb zAv#p#+TVP1(!vNUL;!mFjPQy-5W0gEY*m}oc#Wf-6=oab<|VO*MhO@21&A->t0Z+$ zVCXv!ul=Z-ZY}r^O&^qYd{!l+u@isi0Pwci=EO0lmEZ|Ha^rOBPt3bCWcJC=w6ig} z`@d1~i^Kk%f!sZyhrKT18j&ImN*B(1q~%79yW6TcGg6@;EBU~hFUFp1u` z&^M_bfXO&K^bt$|eEqxwc(No7h%T=!I$7^%^i4&(z$WQxEErt+afAxa90~$FLn6PK zRom1h2JWPybE&fQbpr6eNNc#n0}T1FV`H2ZaV$ht=ZjFmqhwK-O6P{N$RZz~YD>hH z7UdO0^?xjAp>qw1$Vi_$r21nM`n*tjWj^LFAW~g}QMkQt6QE^11Jd!ofbE-u-k8~b z4e3zrR3fIh1{En8dqj0NrK6Vlhv)S6ve!`5fT&QO3sGT+(203Oh%Un=G1ONz!#ET4 zLL+)iz+Efi(yX4mF{Q^jh_xkFkP-0i;ta`%XkrmS)WjN%G>yT-o>)isx7*MvMIG$~ z_56U^+33!PkdV~=E{8t-!%Aj;YBx$KM6x<@s~`HiyIdd~zb!Jomv|TA`ml~RX^;^} zfbe(5vSDNO6=m%<2jQ^jQH)%kX#IsXs!j*-NN@n6m)sDO^v9RN67L2 z3Yu0ib-`?u`+TvAuJ**_*MFpS_@ku||I1-Js=4~S2=+tK(tPt3hRyLCw@iFT-F4^c zv~ycE_t)N;aJ};5cQZ`xZZD&r-}Cq7dEfpu`F|dFZr^4#d+NCD3k(`b3A5% z+B!WSZZ~AWVUW$5k0pj};ivNjl+jVpJ${BMjJk)iXBwdHg{*A$Db?*pl$(8Y$e4~% zB8ZJt|KCra?wgENrMknT*Pr2D)1Aw}4bjaYK#VhikZ?t0Bgz905d3-pR%mn!D7zn! znpbiQo0`2Jkmt)2^dNb}aR80W#!D(u2HIPN>q_K>?2Eqb#&S)5PSw1Fh+4ZjF9J6= zqaSdz+a<_XmA4K7yqVmhosDE#7?fo_=dLZ?uGjJFIkY89Cq&-Tb{MN2N9$0OIhu)^ z(Y9Z4Ga7jTvK$v$HPoa1(kqC2UA&OfnNp7;t2bQes39Gb(*SpcoobX?z_CIhGb>&w z>nJm5Ns$KDTNk>3!NEnT?*I-rir3d|oLQyB>MF4AqQ~&Ip?8F|Yr6K7f2}ek_UWq0 z>MEOFuCKaLWpm<>o2hv;aANH4JBZR&&!*1~M+K1WKgYP#IClM6F3(Zz!c!f3HhzcR zUnQSjulh++ovJScdUV^B2<}GJ#cj9+U*lX&b=JP0bb?}b%DHOvLnG0rvqwEMS}V@8 zn?$nA^~|o}K+Or^?P4*(1Gc<5)9IS9wiEua*ce>@Kg^z+j;kLZR>&n?RbC3J<|s~p zKoq4|1ApT!GN5BN47Z^*wxny<-&HvJ#DLSjKB^@ScE-O4@phvoGfP7qOUD*gfsG>> zWm?(yC<(wL>e^O;B^Jv0?{vxs?umEFUt0t4=BP7z&0vG|fnOvw%wA5fC-~nrl6RtL z*6`?Vl+;cks&_MpY~6X9dCi|0iqU7YJM5~U(E=WEV-iF1@22I!myhMO9ECd^#z{uA zQ~;ThHd*lUaq`eRsh7&5wh{3XF9vU8Yz%l@&k&qOiOD{0 z*1&bCj~wCYh2q7iC=@xR7JtUH3?LjEt|@=9sqqYb4Bor#G3V1!T<-c_Eq~;u>fV{r z>-|)Ud8qDkvml-r?uHTnXa^njSro6l^@tAlSV(eN z?&V%lPB7{>Ry}}=C~hCwpf>-YZX$#Oh$%IHHg=AFtYM_ncKz^jmxGmZrWkRN(%cW( zt_Gh#<13e1XY80jQpgx=x!>b@0Un8Y|3O4N>y8!5tjaQBbFBR4Qkx8Nq9Tte83!ytF> z?IC413#(~D3hl%0vcYEeqT+omb`!5~D}XH{3x(T*D;5J} zRj^c{folJtoPQ>e`1gZn)o|LUqXf`0V#(*szi_8(b_OEHM2Y4}8pE#$F_4ldTJ;r* zzXb1G3H{xRwNLd>Ssz07nE!0K)&y2W4_5tdOwc_SAZ1Ns7WZQQnMe+S@lUvMvwgr` z^y1sBTE@&{K*1`K#gJhrE0aabK=dxehs^_CVwUWTkE-KaK#fk^@y)6PmmH9g^rD}b zuifyUF8p1+R?vvMf&c%vz2k+LiRU%SI0oRq`1xpg8IjAi2BavomDJ6u;#{>j<~vP) z{DRcq0+l_|gRTn)m0-CdomQ>(Q?pU7__Kv|7J8GP2yV>e4?EpE1KDi#)GF}aB*y2Q zZY7^ftPQP>$#O~%_RfGyEVM8z(yE$}<>*bJr#}JNTVt%&d#Yv!BGFS2UYkrXxr=We zPh(ZaQk`=dYX8|#0U~L>Jb#H|%}_5q>PB15wy>3~Q|g)3eskS;FTc~#gA(KPjCkQz z|BbMsldhf+H9OM{Ku2t9enWNZ_*F1abGqbWWc8jBR&O$}!R~l!s3&#OMRt(dB`J0k zYEMZ_ZHS@`tN-c{qmTpqx0k)i{tO!$WVmVk!h@=B{E=PoNpD)sG|HTvKR?HpGn@TR z80W}LAmePql~2(cFuT+1^fBB$P_yllkH%GKe2fW{(ZOeP&&zqfc*o81hubznTHNcp z(qC4uYu*H@)9 z%!a>>qC&mO=&x#`J%k&)PU#FB9R^e3ZI}c3rFS79#d%ld&R~PqSP=Ts|L~%vT_tC& z+PpM`hH3p%X0L=qz=t?5ywuwthdbHOGfU@R7q(dWQ92H~>dnSBKN_vzyMpv2R2nYyX|NbC^ zd>aHelqrTp%4iI-8C~I{@4z51YhCcjvJDZsibD8S+a>cxfrU4-{GfwUjVwxO>fM!T z1;sRmz!qgY%}g4qnvRcyw6QLj+=k0(n?gjD?QdGdSA?bANSUkxhp%Ar@T32}vy!`* z83n0*@?*WCS(<@ocidsG!ysyLDIWPX*Ih-ajR7q=>O@pBC;HOyBG63KfyGyDtb@f0 zSz3R;g#S(;0ddt?Yl?4f?BhbkNk2%9Yqe!I2OnLzeT{`P;`jDWA231mr^BrIBk8h6 zr$eq2NwIq~^h>mz(MtXvJPkPU$YtYKNX>8k%DO8Z*H(IVEeETyhU?L)>v)yoxE>}A z6o&#aUQn&cis{JyY+}3~r}F$wyUis7#O|)7qBV?%#7>{AS4h`!QT2H$=Mf%HO#at; zn(z$Hf+7}{awoBuxTNR+lTpda56 zCg`FBL+=gzI=k5wv-q$8Ld@iRqLO$+Q?U(nVjuJ_bx2qdVS7~XbAUZf8V}BPi2$+p zo_l?TEmIuSVf~vh``nbS{SLDOF6-X#MLDLQ_UDG5w zcNcyv?FaC_%Hs6}F6wjIV6@SZG^RYSR(choU>#=A1})_-*RBNeR3@$Q#VbuQS84df z#2AzHA75>7P8O>2kN}j_o#eCoU?>p~pWnf;0``vBk&SX3-Y?+2Bf6>D4;kA0A7I^I zq!WcB#w9TrFRg43g*4k>k@J_lLdDLOyv*}cdQ|HeHlui9VWNRNut(ULUE~M*$H{-+ zQ#^08!BV6GcHoY0;m2e55_?2vzV(7D8>)a4yvd!f-=p#q6Tu|hC#3@`i(v@z13ahe z!&0TrPU&Fw?8ZG*)e?w3VRqfv45-uE!-3y3-ovl6bPk>%B#R;63$3qZ28Tii*|Dy?6o$2zgVx}Tb76~}d;59PV7_Nd$*YjY zV2mbz2Jh}VvR_roLtgQvUtA~M&4K7rFEt_F+TtWsII;rK?#Z#B;f+{cZ5)b<4MdOp zOu#sMCDoc&^B7iH2VE!jw~LS)q6DJ4dJp;^;vo26qFw1$ z@G&yeuZs*ZFdQ!(-r3AtYm-9pCsII`fUiHBC`K(r(Vy=rnB5CSs}QI)Ut z4oFoKAt_zlN9j^xl4ivBCXr4Mam%kyv;mm=C6om5QECOXBE>^^6%xs^K&fA?9W{9$ z0%tCFsTB3#oC`yyv>uvhf!rs5LFtI-c!APvZ5_nR67lR<)h?<3(4LPPTO3+^az+q17kHi){lQry)C6#_~aYL_c zj36jVz}%|&rmTbY?lcyEDWmEvcj#g&x1Lbr6OEifb**@Z{l=+t-~Vbh4^lI z8?Ckfo>XIx%7{>P(p8%v*ehEJbhyuOLZIwK7AVpRJa0icqfDS3egP4Hx8Z4?wql7` z57i*}TQlqakk_=TRZ?G9&#C&1JX7>t>eV?YU1RiBb@-!SomI40`zQgS;DHpv{>2i9 zz59TE6BgZyV`XQ1COt=nuY8Z3f?BlZu_B&ow{&cCc?c&`FZxwG^=02KI?#usG~rF= zkn|uK>M!XO*$g)RCb7#2UHQgco+7N6jV#!xvJi6J{pWF{YY8cUL}7OhT%8>2&%oTm zam91~k$J8K-L0WMKE0E#|A@<{nL7cLyudlJcJZw|d6Wu!kFTJBpU{HHz77^R()I3w ze?F$7nx-VTWJ1?goWeej5#)HYz-J$X&{5PzXDeo@8Xz*skd@wX()JfH7+(3i0&jTD zLDDf!ICh0A(D_?g{HMoN|71Ysf~0BWh=Ptdx0o70mB<#3#*b2E7C`X0PF_ZbOvDK& zJGSG?c(HM#q#{*c1>tohc6y`}t0E5d7NIDLlF5a@+8aZvZ=h%wB7is|{dYI}iG)2I#lYh;m zIA}Xzz=3z5UaLd;!FIx@hj5lM2av}iDeAuwnZ)kAj{Nw-Dm(};Ui`1il0wIi{%@*t z@K&`y4?4m~8?yCVRm=Cnhu(z`1yAlRj-vAn`vXj3gLYzyGMLejIR_EuH# zTw7)gOuf9QFp(e-(4L4F^^)8OkJSzY%y6=#)$6LT^aiNP&4x~!%JEG&{4M?ty625o zsoL-zYJd4J8E4CebUt*h<~B;2dY4G=8ldYC9x;`>N*ig*unKU4{wxT;i2AzW;vz4Y zDaB-%3!Z#Un%OWaxuidG?Ktjhr=&cDW`Y zMw#7T_iS#GdtRetd^8th*k${Fjy_jg(b^V-EXBB8r!6L+E(h68JV0;{6hwV*1W0mz z&z&Ug5mzY#3HXWd@UbH5u|PaTdU@=zi&Dj2tGeS}wZ}WVHGjYAF5W2A&b;f8R?x8o zLQh1-BD=aKU1PUF!vQoB@5PZjgQ!J8pVY`MaOWBm1LxiMr0pKMj_~ka+~A#WIb5jB zW`_otRL6*yrRaogM7OSqV(^Tp<*@hwd*~(BOZ539QE+|#aX~}ta_xpf1R{n4pDxwz zk&{3+)&OdFwON&1rR2%RAOojN*k1zuuy~sT#}tv5w>j}B$Fq=lKQ5?zi$w>{83N zA;wFr1i7L$$-L-S%T9oW;y|N6^p6(>H~CRGEd)cA1Y0^iNMz zcCZ)FD|v!T`42fYqOg}p*8aF4UBpKQmn`1V>)11RsSerYkM+1+ZrhDVTi%}l-+h)+ zO}ja{>hcT};`e`)fw(^21vx--8qUi0HUr9%&_PlNoR1M`&`9zV8AW{2aTw~e>;Rrx zS=ge)s#NIJ>+M2=RT?=F2pah~QnI|$c=C!+kIvs)|1@AoT7`GRkcv+=F`J~zA@+&1 z`XtLycs<<%1&d|4t8u(}4PoKk1 z?{B~od9R4z2B`#+Wl-@9=GA)kbR#`!ubIKE@zkNR3yE|Y*FEx2Jy0ZS*ozElzp~Pn zku%~6pcqgfv;%+4f}WizSbTb8XYnzP`aPggZHWy|V{}v68H)ZxYMYT5>aLPO$qR;P z?7~lCCULmu-OOu#r*yK4&tdVkSrDO?rZa$250M}N(5Gd>cLddH>I#+Cbm*RlZGfF) z;|+_5YRDT{FRXtAwE4)>{wX(F-E5bPANW5m0E|m9#p8Y4GDZGW*dEWYcQ4dV5_Ek< z8l?6mNG(KoO)K)_qgE}wPz*Jav5%i59bQ})xW}G1I%DVULWFchA zXXg8ym&KzdA_1 zonM=37>5syTwCz=8Ai#1C&O#3x5ZY&10o|<)0NVrc*>{raVNcK%p0d1OuP?(>L9D= z^Eejc>B^fp9x}QJ7GCXd`SnoEJIq|gqx3$N+34^5Y(nxr#ra7YFctNdjM9+n$`J~G zg8`zE9~<|f^B|uHSkZFwnlhuPRoA=(rBPRDGZGj%VOwx)6?MGcqNC_jPX0XdtcZbg zND@EhK}yK)%PmYIE6`4I9x)}6m$<0L4SjcD9f$djJVG4T5seE`z;39}41`0UWw|fP zTzHSDqY>a3Ie`0tiO?tHBv8(n-C@o2Ir!~h>r0p@Jn{g}{Q9U=>Ng(+e&mw{tCukA z79l`MG7G`&wb?O>aS1bP2-hFA>@{8D`3!|0ktI)RR9Ma^^aI46v!GqqDYcCNLU!tu z)_NII@xyr|KVi`-T+l?^UKy?2ePY^z5@=zKJh+|(S09KwE!NzLA60z?Y4X9sAFa!#;T+!gE;Cs{HXiZ zKiBZ3EAd$zlgoafC}knwqTu;aN0l0f|HRKr};up)o(jZ`g~K z6Q?4U2Yu?b26Sy_2s2^;`WGED=h%wRY=&tA2XdBt1MQ5nYY{8MPrV0^i=F~;e5ivO zH@6~nSb*N&I?P|iq40cT0k%|^167W`DFUSv$hW9NucU4kh}pK#ozTk_#B;uz^>nfJ z%|d$0OkjFNmi(5ko$|Bu2$4IS2xRE&D$P7MWwrxQ?0xRCgH-2q!$qgoAKXgYKJKcr zv_bJ}(y`=;bgf|%EZV8hN1w3$-=A*i5q)QPCo}DP-Eb75$w+L(7HhOXwFD5C4{Z%F zvkY3@d_JpS8{+t88wZb2nQ5LSRi}egmH{B*Q z2<&#|P>*!wbVR3VyD}@B3(e*zBYticKHJZ`jW`WGBor%QvPSthf`qtl^7AT&BF7*3xw3E@qphf z8c?nCVo7^;Me38Cp3|#s?8SCkCOgr0u%=OC53KcdZ$kLza6XNHwjfMN;nQmyg~A9DP6k6uCh@wADH?UQ|Pi^d6lMvL4&zakeUtrx}g!jo5+okJ3kg|fqOrMDK zumfb{OfCTzrFf86tcUc>QGjUKHJ)uL$Q7LGih$kO-x8-S2x<8hy53GuZoUu*QrZ2R zk?6rF)C4c2GR0lno$Q!K8-?{5KYVmnI}v9nGKQ<#)5jlJ;Y0P_TZ64cnzew?>T&@+} z&-@S4HDXK|8fN%z46u8vlxP(-v*sT9QQKTaJjMjBTI?Ws8B&`%2B7l(z?)`_CPl+f zX0d!!)A6ndvYjm7NA<;&fDB&S@kDBpSxC=#n{itZGBW2SucJE*<9JL(iZ!j8ds$dE z2nJ+V;#Wr_#0OPF>BxtL`FB`{pt3E=r+rIKjiYZG$VQX$6Y|NoT9zP+#C^PU6#2Mr zV_ru;tLS4ZR#OH9smPCm`3q6lyy_O7y8aEKb#S^uyIN1hvuAS`GhAwqS}A2%m}ljE z@FxOI_^^hN?%ATt*KPd=4%5fx=|bjs3f(68P$R^qYwqGBn7p5gj@3y0_y^J9-x6n~ zus{B*ITl_|*Q=H~f$zTCP<4IioYbTtd)w#eE0p}XRGuc_%k)43u) z_EpR5J&HbAiEOr`!5N`5YeH2B2%u@DBa|seeD%T*-As_LIg>M9kDtIsz7>& zhklVTT-=v18}XciB8Cu(NXUy7{LUBXJXHJ8MaMp#8i-QX?c4C2MYTQTpkt`o&zw)~ zpV$!JJUvaXjdP9DYp$Aa^B+@eGEvUQf>q8==wpjlJvvak(;UGbyP%4THB(dIwawPy zt}=-U7$rk7+PswE){+Gy~b4bYapAKLI7ZDfmjvkvKzEySq-Pk ziVics?o1;^E}?Wje^e;(T*yq4y@ZGTxZw5!WWCL{>%>mXtl3B*UIvAz&6`||M8~EE z28op4VyQ&CRZAlJEDUiO0zUht4LoL_)w9zl_{RQLsl9eS^{Mi&VCseA~DuTO>S4liRnAg|1tH(RN6I)-*rMj#N*E}e;QJ(w=fRQ@V* zk*5AS!+FbO(&E!fIo(Ex_y4g8c(x5{J1!DUfKp*ihW6{ytQvZeA+g-q27CO(HyjyY zfr2&AA#DeWoqrR$21U-5sQ(uRL(WCRf|k)4 z_`DLA8@Mw!fHOZvcN<$9@p+_BammB|he@RE`)DIaI;@`7;3Igvt08;B3gZWbA6G5l zLWaFx^~|Ny(65E@orgefbK;fkoC?R!gHm1K;$OZZc{{NiOc=}qIIAYosuF-Gl=RK={*JXv9Z54H8?U&KSDSQsp8E00$Oi;P}j?{*`j`f9K)02xkY zsv92FBq0)p#lZ6sNTIp{d|%sNiIXn{#;w5>q!WmbtGSawm-ofUa(ege`5L?ey&6VM zMWC)wa{`*QohF(x+SfoV5UkM;9>hSyNJ;958=5S)_YlvM$mfAyK3v5R4AFT9(yUD; zzvhT6Je;|Yn_-#Vg+Q%}Dj$rJ78Whi1|m_NmS1@Cx^UPEhX+YSE#dFK|4ua~3R|zh zH5b1MR+eJ3VWtB7<=Cw2!VVSpV4!V!_N2+Lu{?Uvct}9mE+{_glBq6XJ}O-xjGTcX z6U#&Cs-FaS+a@vEvyT{Nu>A;}lYNEeCY!No82wet0_Ww)>~VtIXf_dsxw=$qW^$`N z2Vl?>d9gwo&u%b9$JiJ3WxNi0mW4{zAL^eXlJ@Af6 z=D4}Fd^%g}^+eY73T#E>Qs*QckEjfoEu@66V3wIc>g%%f`9@VH!0+=QMy;L$;TkKt z3zR=^dHP=k`(_?vAz)Yff={+(?ZN#}94<@6>tTkTrhqEfT$DbDMy2re>%*0(A4vPL z7iTwgA^L+`mFA|*>O|aJh%loR?_e0phg&Or{y;|RHLIvQ4zn2fT{Y3P;b(}i?CjiE zX;=C~AihJ2cZn1maj@^b00wF0{=rv=Z%}<7jp{Ay*Hb^}z80!+G)*f05V~7!a@@Y? z=8B1vtNw5h_l*Dejw8?6cKsC45{j%O_Nr9frF5lCcxK1<2!>sjsS9};?KL>3c6=sk zn_2f$^HIqtc+Wu4YT4*={8P}3ZlxRNp|I$E<0^U6Q&J> zIn-bUZ=^ExTi{aiUQERQb-gL^pjQp?3|c=LLmtU}7Oa&D3`7iSIHaRsHbM-vyQ#3D zV!Z5^{CRpCG?wHP-^z)ieII})ZeipXuL_nB?xAj9+jcUuho>q0j+q-5oP_=0`QF3`V*N_S&M)A**c4MNh zq>iE1C_NxV4oJyo6aqTtDtA821k;{Wjsv}EXnf@ln(+PV_(Xn)H{h${h0=J&X9dRW z-Q|k#q(wlJ?#@6f4Mx^P^_*YI@auJLDe*ueWimj0STin>**KorzGVhfW1f_E(@n#V zs;8PK+=chtehSx$^Y7#Bs#}A^(uxci2t=a3IO;H)kH!qw9=!PkdBwMYx;lDU5l2p_ z{&a^>@22h}4Q0QwqgPDcWc4hsutxLhCBqp!c(nCH@T+Z8zKFV>XwG5ot*$(HMe<`j zEox>Nu;MNI?AX)7j_lKC(3glcYt-hx5q2oB1b_-Fc zLQM{GUaD;%O2V$3J06cEdCn*`h8yrPYT@^6Oah3qw`O z{@RED`j#2f&Y8dB_i1`4(DqIMtiv!UL*_Hh}Z5 z&X*|znf@qimWK6Tm0B1P>lCh{sqY}Ri3ah(%Z}$Vxmr37Rh!Z^a0zmrvVUr4#Ncai zGo)%^f|$qNj?%2fU*q*u@5=xYoSh}{&H{>0?_CH3LV488%88u~`W*?vK^I&GH&b3@~F{OhqV{h&}JM#qC#H4K^dAb_1 zrcd6U3^@?E46hB7s|o47ZI}FEg=AeJ#7wu4rk4%$xzM=-D2YiTZS;$hS;Yrw(*dkm zx*46cZag4pSp?tKwK)N8szPciOVPdg0on8FyRvnmWzG)F$1`z*em(`)#0mM84wy+M z!a{waDyoAvcMEQ4s1v|vK(|CIw}$hs2zep0>m z@94rD5L$25NqV4a7Lmasrm;T^Cj}tm{@clzXa&Cn<=t5w(3}d)Xy1E z`}vyjoLWb97u>{IptYRPNR0uQnzlq6!Q}%*$a;rvw`z(G1pu+m__%^;#9X8(pb325 zHbA9kNfiU=bg&ATRi z2Ud?9o4tC0*3xMF1W|>qIAeg?(}|^L-(Yjy0mc@mts?u0Cn8Gf99sDhV+q_^TBma+ zNMe5`FvqGG(UOk9g6&8Xo8$zvJyJy+TtH^T^L?s`5jRsd zA%9KjU{#l5>Bd`HfAuneXKlNydpO)iCx7Lo=Q#7kdlm61N?-i~rdXofIl2WhCdJQJ z=`P_S%fTCNLOtM118X(yB6a$~!P{UmTp7dVj!Fdk?Nv)^4u1!fe|7o*HB2N08mabH zJ`&i_J6D(;s{Rb=ei@3-NaVS=tj02OLdncJyjxPy5XDqRs3D4g4_3f9w#<0t)VT82 zPs#9%5OKs#RlP-4uQw_9Hd`K~k@{<;am#oV9WJWWcP~V89%opCKmunJw;mJh_8_R# z7X(l@?;yX?CXv$Xj}%q&>Df|7As6-FvWM2;6l-cCkk$n}Wg}Za7WMuEXV){`hz0@b z&fa~v%?zk%Q@2-Xs$~($De(A3Xt1wRMMezkX}Pm|TtiGacP@l{*~U`k1`oHSAE8C~TnF{m z=R3}-wI1W;B9svphwHX=o<5-ocFPGTlDu6#PoEw>XWSct{z=ZQODFo{I!9a8er24y z(z#vC!jK5BfsWHhnQx+#kELfPDraOI;p+du;DGgU%yBZH)(w8}Zw?X@`&w^adiZRq z#3ksmXV0H>28rZbA+eo3_q!Q8o}6}hi{Qs|SmD>dse|>(vWexj#NK4gOL_K{Tx6yD zRNHy)jWYQdZI!)*Q&`QXhYsI$zIOd<61l5Xzl1$_0u~Mq`5HaI@RFl&u{v`hE@Fjn z=p{Ol>W4zv6;Xf?|IE;UASXLvuko6_>yyY;#kb6BcJ`-b1Y%&qfUVmk!}Lx}>Tdv2A%?x+3X zSzU9%h*;Yd48kjV|5(5KqIjVsZ?$-2=$TDo3RH0@fgC$suf>K}9cE$u7jDBX0m_0c zScjY?b*{BVhmCZG_nW!-Yu2xLE6erxylC3V@IqqygjCuwy%IRze`b2x*QS0&gNVgh@Xdw7o8&Rj~Hdd1tKH>2{Xd3F_i`o7@= z5q??w%7P6XQBiIWSdT~*p3U^WmNj&?$65|&MDh;#mSfGXS=r>Kur8bge?<(pIvh!j z8#x`4jaAm1UCZdKp8--JO}@)3c7C&-a`Xb#l*1TKFT93M=I=y9NJumgTQ4o~TCy56 z;${qCEWZuj$516Jot&#`IqpdKUmhNFZCYwvz99Kn`vd$4(Q^8Sh8s#Agg5u4v{*M8J(8GTg{>e2$PW{z2 zV>Lg2+uJ$tO#Q`rT6M&kn%dc}OYJ62Zn#)@L@;u^X&$)+Yz~Y98fG4KNpYr(TOGB8 zRPsMjvudo{v^8+OXFF+s!+*`lt^VAUs>bcvQ%`$85L_os^gG0(hV^p&{vD%H+M`NA zo)mzu^Z0J~$J0}Hg-w|jZ`nM1wdtSpKa8$>LQ~g`%D!xvHGwoJG8H)qOV{EjevOc^ zzj{J;hK#IMZoVam2TneKD z>z&Y4XH&V8au*#{#LD4MJs0G0@KTLg9%n#?*AS-W{N8!>|Ij3wN49xB!`Ajtm0#s( zywdY+kPQemDRQv%m!?)N2>9Pmk8SO+*zD{UHYN6vWpl+QB?|`fF$x#Mzj9L`8Zv2e zN&PP@4|{z4EyyS-4Idp-doi6Xsg0g9d-H6Rn4hxb4WqGor)~#wCF3VrHsmhV{Rpw=zq^IZEX|i);ixH< z=v;m&oPU+mVTvEyG}u`GLr}RUzB{C7Sh>e1a#LKjtYCN3yj^fEd2tHNKV`hXF?Zek z?ue*UPFFkVmM}z zYBSq?5{Y~}qIIL;Q?^SYvPJ|l!X|A1p7dIAKbc_rxb~C z6S=mqK1NYUkIe@^^;3;3l-d z|4;snIGsCpYpYZ%9&66&{(&|)v)wMLc+seYxM5i5dvvuRDg1@_Xj#vmli&8)*EX+) zE22G38~nZtJYf8=gdyn66OXJ#26Mcv)!bLB+uMZD+Dn}LQ5*|`+1|_Ie;EB)+UU3& zdDvr5nF&4XHt~@6Bk%fhzcj=N3(Bd=oqB0$!Q>J1I@2n{v9w&uO+WaEu%9Fs#1&rQ zCqu??lNVN>q|Bv_tOb*o6}l1EfR}^@Ol!{7L8g&=&eKhckb;kxyZC0S z!~Yn4-(I=nY!SHh>I*DY;fdh^i=)vBEtd)7QlO$1*b+Dv9c3f^*lO? z3u{%!hCH5{hHzW)&8D7fJ+o-m!igj7x}0*a#?f`jZnKsZP8!=Yvsa+4VQ5z78vFk! z-~{2H;d`qkm*%m0;8w}#wrn`)qK=T&X}P9Q+>a z(1cx^^80cI^skb7%uL_?-pD%6>j`#Q42APH-HLqsqb`&?guLc`PS4A5JQQNm7v~suXn0s~ zt#!i0G|=g9S%J)x!+)}KdnE`7bj@M-PH%C5t&3l@vptbQPsK%6e-TnqtzSy;BXVbW z3>Op}T`<5Pj0r;?)|gDc^0neDH$6>}Hs^md)#sKIZ*9m6KLZT^0| zWrdSRtnQ@>#y>6v5L)bin=_D2WeTp1_a1B-u0a0zS?dRfylB||7rZYGgk`)Z<5*_0`@DL+Rx#SXBae~e2C}+l zN?mrf7iR;w#ZnysfL*{ao#C^oS3HlTf_3G7`ciFUaY4_j;KEt!vn-ppXgV=TZ&UYb zqj2?^qe!S)1Jh>anrF@!W&C4`TiCw;;TID1;L^j@3nZdONl`>SiU_ubjkPwV6yX8K z8nHY3ZBIc8T(X{Fqq-~_>(8Y`a&KQ4u7Kgzg9AO7`H>c06}oR%M#y|isZax(8Xa8& zQTvv^2I<7K%ng!SmBB+f)#?Wzw(NTEQiS05-s~kJFtImHVZAoTlkfal&#$B^0pwLD zl~DvgM)<+=$x$HMVe9zpz(moP9`tU{-rY+LUnU%WO5Y1V?S*GTydMFADg>cF*VJj1 z04Qb!`&SG5%>+k`-IS(V zY4|s+=2qOG?*a29GUzrm!V{iUP`{ z>1~i=N0z|$=fg&-vv6%ltqTIWyWTUdZ_7rg?-n}-S%5B^;C=man0#K_(-QJIJm}{~zk4p504Q3p;E(RlO@%uV>?XAl9U+rTpXD5B zbjU-J`_AjxQpn8VzG5(gDM@uaDS*4Z&TLiYnPz*QnM{_#`+uSS^{nDj(^R!8Jg9y_ z4BSBJZ$`f3skgZ18KOaKNjJq1!Z%L>Yf!~* zkS1?jng-KB$@lT2sVSc0GW<~$QRYWsDI#v2A~Z#XM;cjAd!~CUb~vrnwFtnm#zK&{ zDX4-GZ*#=v`LC2g3j|Wn#Iy%iS%-(CH!S{<`01=dnZWVLN%TjpiWjdJf_L~b1@{hv zJc&iTXIl?`K8=leapUZ!dBuytYMe=+GUZn6-aQDmor&V6^2ht;;AcwA_IgA6a{rS# zOBJ#^5LqH_SHULR6IZ{$rD4LzQb4f(UPR z1P11E|26FqkM#$e)@eafk~qA179gQ@h1R047eBl(d`r-Y`6!JR9`9IPR?X;!|2a7H zu_xUH_iJYB@t-z;d^VP8sJ&x7e=3oRz1h$Ie>8o4Jkxvs|2kdOIj2spF4u{U;+)ed zm13tGDq}ho=X66QRB}^7B~}(QGh3(Yq@q;F{id8!35OMCX1a6FsL+^=uqkX98)o)< zy+8fF|D4C;Jf_d*^M2jmy`QhT4DtySsPA7_29b6aDXLtx;LnLBI2T8e5=pGEO-~7BnP*b&hl?Mp;WejGgxf9lgPY_DP zTX>Q^7)i6`E_L3;U!oE^R6lAbRN}#BLn&+FSB|{I>bwr7Jl;^EQ7m@&384cmMUgmzVW7@6uvo6(Ph<1ID*`?gA4dR z+8I_(<}I&tafF*|AGw1?i`qu()YH#TWFT@m3VTymI5%2q z9w3|a|EJOhsGcF|bYVE+^2Jl!89iF&!@#fOZDBVBZMB7JFK4F#Mg} z!)wLd?ZW+( z?{tNg+}{MBUm*it;NULqVe&=$gFPutRss9BK1H!Yh&5mJO_Jc;7rPf`Lb~H6muFhV zt+k$)vVUhLv6-(f8?x|7?EGr{5-1$>sr>5w3nF*ILPe{huW`P8^wk{sGswY@h05;- zp;uuYi-B#qGuwDmh8U%IK^1rC-Q549RO?5$S@&^rsMKt!=iEgJk8=VUl*5?Jt=+z0 zz-a$7O4-+RnXWCk<<(ca1IEtWTUVvzHW9jt4U+I79Hhv z-0+O5@t}<(Z1Oe{96@nr^A|WqJHLc2MV=eLdg2_~X|iWSRwZkY{mt^oo6!y}wS`b; zk)XawXs=U&EwOLkMblrWM)zGE@~;o8pakFEjl-=BxD?3o@!E)3ew>T3{0p#j8N>xI zcT#7yYSA|L4Je^B5`UQ5^*GSw%PVr;AG|S@OfWdIiT64LonmOH;qgu=rkDhN?=^SP z_0=5OpeQ4D0 z3)kE1o}HBjVu4F!8s;#98mWiWOe0RO$6BZYWf_%61*to-!Qd9t?_3?^U(OR4_+I@i1_Lv6de}G zo5)&CAZc>9oF@P8Q78qSd*aG5zk!VIDl-s1P?0|{%b2gK_>Mzk9f0>b6mW}Fs;25< z2rm%F8>Vzab5y6~Yi#Bj|KyUNpnLTn5c2XTt2=A~p|s6M=uIWbX~L!0#eZZ~9i1`W8asV(o5;*Bcf7rt?*_Or@?ESrn0VK*i9_%mv1S36M|$cW->*$Wy+rdafDz z^sIJ*D1Z@$xkeoSw@KdPoWV$@kO+OHQV~#?b=2%N@Bq*`-#2?V4#OaT$Xgv48c-yg)=F9zORSjQoH2L@YAdcRlKB-zk)2 zGxIT%wI=)K6M+7G6<=>D@Id)2{}Fws+C~m_F^~w0wv3Ff?mm#&+nVk!(V!e1q8JXL z;}VFG5$fqylvNlnj$zpIM#Nab+zXp&JkT&^a*CpLLqP%mvv9#F(zXX!sFCmPv?V-- zAegNf`l=WHv17G}7!MM9Mry(Tm#;BAdiD~5l0u|)4x&e=tQcI+_98t(|28WfGvU`o zpWY8`E#~3^XkmRAl)80tduPX9$UspUImzCp1_6mQi z|015A8``NjU!evpQXESwtCI-GaK@Do5wxvRiBIx}!rGx;j>B6>!cVC=HoKs3ea;)R zbZ(e-WoO?!t7tNtW=A=NSDeAr>ltk$k;*`EUCebXW1XOP!Yx6~a@Ze?m zb!n%S+*QJBIsVFt7&6WwUUWt{WZRBvgAg2HN_E@K>HAEdf2IfAMU>#9$hi?ChIk?i z7s#K1d={+-`4h2J)6&E8pg`~paYaFk>zs8v;rp}U`_-g=ZcD!?=&mIk+V|o5qKNR1 zi;;W5bzzNc{6|?vN>|{$CcSPr)DtS5FQ_r#42Jiz*J2q-EZ9*xUZ~uVLC=&fy!7G% zU3Nzfw#ixn8kZFZwg-yHA-x<;J34j4)nAEqiqRzG*urmr5y)=h_*b~ftDRx5hK?;j zuBdtN;c%uM;~i#s%Ju*$Q{_&!>`8uyD?Gzr%-f+>$ADuU_;&v@iX=haCKXC%KB4e7 z;B~20pwN32c!Q-W*_bz2gQ~=pHRyfA1VLm}@ei&TiB7xMNS?bOLB(|ih#3KzOrotO zrapL1-PZnv;0A2z=}>Vk``vI@NlcN4*Pl`%gMni%EBySwA+^aa`{R0Zl_Fj>lAw{arpBT0bKxxu>-K{x?!m?k7x7BM?T`A`7huv)%J2NuwtxByb$(mX zy0N5h>N$twCpjFl_&`--!726!s6H@R&AYU`>@o4s$k2@(?9s>Eb;&~^C78EQ#JrcW zDU`=0x8!o`CQG#yCYDLOje_OKd2Fp1fXXPd2#Qu4667zE{f*L~EOwx-WRpi;dS*2oE0euD=#?Jatf1$;wDx+Ate(HvIG;2wJ4O;qd-mm3UkylEzzs|L$&$6Rf3; z6Ht*_qr{A+BL?8^s|IB*KF4Cy37)}Un7f_64-~6nCKD@~fBPi+`Za2_LM5^}?tXUB zfeux&KSO^2sowiLLAc1{hQq<6CUB4pJ)@iaF(&&L+$Uox0B>tS>!B`GyTOpVv zX0&Q-i)d3ZmMdm?Mvb=(xtoDTu2JSUnMH{;zQ9=egKO$|ikv&#H)QsOv4RaF(?fyD zs9Ju(Vks)x#y$G=0ldH={z-OPpq!wz-_Gkx$Fd%$lDNivH;AN%6VG4{E)mMH18^1Q zgxnxnI&4&aqI+0X4c1L}Q_sNRLq9)~RGeZ3Qlh2dQ!(fXSFfHn5GqC^;Ed7waZJya z985xzYWMKwS@9}rbnxY;#2L5^7~m0*PhV|2QFL-I{95)hpgll3Y6b?NpC91_jU*5` z{J+yqlW4yPyGoku|2d4j#gZkhqavZ*GGOzB`4?7TC^Ehe^EhY1d9lbc4|u%0S>vsL zF*JS+%O?Ko>a^vaO1aZ%_D9?5UxCV9J$0*4P4R8@9;-RVKkk3#!Ag-oR5nQzKY`i$ zXH|Qh;v%Xpypdoec!iD6;r+nJr&tO3_O~)MaY*Nt9>6+?thtR(rhv%nHnnYkRSBzf?#(-sAvs^DG> zub7C z>fDgYCl5mu%iQsNPk_J-MeH-ViOb>W4W^hKB_0;0c1>ZnXa1WBfZ{V8_6vfNBdE!B zV2qcA@SROflU4-TpyJ|e7}(KH3ACng`U8f;ZYFYWSAs|@vp^reZ;+HqXLw>7W{VMr zk#}?5;s$bo!71sO9;MCre}^~DjJ+;`Obh4ew!&>!avo%X~TJa{wslFP^?;R3Ge9bhsj3(POQh&>q{ zeP&{6yEL8mehr*(>}0y`%_c0+h`Vb*xmVlH{N%LIT4Yhy)uF-4k+Jm7AYp5GfLYP7mJ^W%dX*U|8VH?|PV zcr)l#4q!1o5dGzjbkFz4#^kBmKm7wiG~*s^e^N@|3C*D_WjCa!8+5zvpE{xbyybcm zpnn5Bk_pE%sJz-1svPuNh@4K;C8J; z%^q{HP$nrjtmM%464m zg`56hE37vyMiCyaG8c;=E(2GN%NMjtXWheZE3r_uX7|1i9JQ!&KssYeUo0^$=1rua;7esYS?-fT|t3n|@0-`CX<}`Ejs7D-{e^byXQi zImB7P<%#kK$~wjQXtljQ>KoV}zcab6i64qi;edG;z)iooA`fie3Y3QzQ1`0U8^B{{EdW_987l|Y*}HOFY*6`>PueHfY|t;4N^3e%%9mm^}xrNTcgvUWVx6KOcNX>s;`W7jxHSm^TEi|*w z$n4c1Uob-S{$Q=;x&2I}4(!LA#-en-kP*jt7V8tx#pyQ{ zQx9tOx9zk|LnlcaiWcDpc9>QCqJ8mL7=ob#_3hfP>%`pTqu;knkQNsuxsB z)1Mtu-`#@c#>Pv-4i84J62>>SPjwu8Pz4>|&!nPcFBI3#{rj4AbecR#*`VMbP>;Hx z(X??E*A|$nIf_k>eMVpEz*fO)aCSps1_9>QqO-SA4me3OqRfskLPg8qCr-}IvprV* z)ID;8zhx;o;^kQ-cI9L>D;Pe(7q_KlaoFs6rTB~}*tyaEBO@6ysAn$(fi|jtfbL&L z3z~yIKYEg+nrVm>_1{E2RHne@;tBNvl~Z(4b2dh=1{kyZ)x4jpnk({m2eUF@sL4;H z4+Mfl;49Nk`#mNUY+cQhc8LB+#_ZG0QT<(=;x9PI#PkLfpfc?-7~RO`?aEq61d!B5 zglSgX(skXyq^cqRFP|FcnZw0p+2W;H#F=RYst$j80ttk11Q@^rEIEV7Yh)K0D33xv zve|ZN$7Wj19z-Zb@E1a1hL#U{F6mVfLc4~R+ZcHQLxM^p1n{h2xfRNneV0N&n@u%U z=MG)p3%`$FyT{|miO46ZI8l>E@*0qY~r`4k#sDxG4 zX<|zx+J4NtZ4l&*#irxmU^+JpOt>*d2;Ud}r;8(WUiQRWCGu6b5v6bef2?RF?CqoW z^@z5yAQ>C53Vdn^=67@!=+n)H)8)vf-jO+Gg7vnaVv(b^^Oau{8Az}UY8(UiYtDlg z>>eW&ss1Rk@IH==l3qNQQ7b{iSl}9R+h(J~yK+HlrmGX(I7ajwICILG{m%;jU?tn9 z+I2gLa_kc4!E{w&XF$BY4a)IZhfs}Wd@FWFfBF19 zD_TDUsb)Twi>8$8(dRQtS84ln!1>DcLHAcoN3a4{BN}o z8kR$#i^I2M{MGY2#+ zjtxwn-46yzzbtYyZHj=1b@dEzI?$?ot6=kJm?%)VzW7_*z(!iZZLi=ixNS2-+X$Hg zX2K5~Tj6#wdd^vGCdY#9{^bR43<9hoztjU$4>?W9{KmvVy-n@KFbXr^+D@dU~v9hBHuftT-#q#~LkX@%+t7-L*RDNmn4 zFCBvY>da-XPHuKd@=ps-8Eq(;KR}iqSpzYj-$|Hp#qWWcD>oH^{A$QKFpy2BAXXR+ zL}gW~&1&=UB<@;pK3F#5VDusUxA4Y-unPRW56~J9c$t^!Mnna->u2t-DBCQIEom~{7^oHrkX=o*(G-fz#4ErB8Je!)&WAiZ z6L^VR1HE36k9_I@pW}BE^(dg9-1>vaV=306=w_#qW{14Zgg3qJflU&4b)lg4{r8ZX zFkiws*nQNFY0-+Cwf4G0=p0m~B6znox*&poslphwGo-k$;m( z+m)U0+!X2;Cg67`9`eY^*RHcJVA-L+OoI+yt6g;!qrK(xu|rMxslNReJ= zVh4*XETN5r-`ZisF*=6^;l^Q(=aVf!#b=0IhLY&fu_!6MZ4-V>ooj)L0f&OHIi9~T zrY!kKa!8bk1^LXL3-3OPVv@0dzq{c5!Tnn|KntniCJ!~b{vkx4R~yJt&6Ezam;;{( zF2prFWveJqz7}F{W9q_;Eu#pn6aFU8FY!=iH?2_e=VGeQncl8t< zV|0_XD+j=_;ea?tYzQXB4b5@Ysz%m|{HstpS~;>TZ6&QG65-h{z!BPvS}q~4Sh=reN)>|W(au0m)!Q-IXD85<3+{2Gf{IXxR< z0w`&y*@$~J_-P@hWI6PNK%aL(GSk>Ob-9xi7TrRT{Dq80=TT`KoFUl&20d_-7RaaG zmjCgt$grTvUgLvOp;x3EO_zyg(OAy_UxdtFWdoEpT>80#Wg>qg)HGr)X)d>*Nj>Tn z@az2JD_T z7fAa<4`!ZGDkVXDZx?%_{<&GX*#ourU9{mpA zeT614=T47_*ZQp4I|mm4cTq=^2rSK@m=c_-9Z7^+qQ2@4&*=(DH3Fol%;*P;h6`{Y zM<{0bHpiN17z)oyxrq+~8Z#4BAY2(^*f^xxC+sz+Nk2RvnSe`LOO|wzVKu>~vCEeQ z>p*b=Jj}~nt!jI7%xW{=Yk!M#x{JUh0$xpR<5?oO3@c(Tx;88Ow-LN;

01#Ztw} zI`MNO1;6+>x`?~-Z{A4-HU1BF;E;>h79baQt z?%xYoE6a{$A+fdEIO?m`xCT~cpp3BJKHTR&)YjNdz(MC;00=gi{oC7O)oMac` zefsk?gOZ@VfRjJN{uD1`6~lHu2+;s3>ts5CHJT@0X;4FdiViZb@i72;XYf9V5Ij46 z@Ho7sk}>kpKwfd-4Z3>gSPdxH*>VhX=yKo#iOo6Ivtz*vDh1PTCeW30FDnFN?C1u3 zuOhIDy%bCMv2~{WM{uh)g=$5|Q=}LQRprwQKxdr(MU1S@E0pu)$qzbrSKJHELC8?P zKLVJ<)?eba>^r;~4&3~nO)%3xFM~*~^_n8lMLue1RcqHB!*$I^x0?gR2b8)3r6(RP z9=p>q0MCYB5Ub%LuOPp0<6PPp${85g8(u$vx4R|xeBFdomxczpGUg2G)O z^n@Ftm20$bRp{bz^=h|dc>QJBq-23UnG5KZ0FJHUi_`Pbrn9OP=ZkDOoVizqR_!9U zSEnMisN&Ctaf6N{Fi6~{Du^p^yIz|RlXx8f+yRWe`H3cH6{AIiIQ)+wz@_40X+pf@ zytnfCswekxvmB{iiY@g9P|`Q<^Dat*1~P0T z2IX~mg6L;oX^9f&F4KdcY1cFBR1Wl1sMo zG`>2tT;v*S~;GasLC0w3))+uWE5O8uwO7f2uT|a8Q0X>yeKx_l~Cm$ius_ zh$=&_*^xDxYuVUaN(Kf)h0{tPSE_sjT|pf{O6Q;lP|6uDb>`)Hg?KN54L%_Ma|Hqq zSj)yB$fw;n6h)^R_A`AdTgqHE+UXjqKTiU|Yt8|#7wt18⁢p#9x@6a}j%u=1M<} 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

yZbi}dm-n#1QA$Qya%*hVHYY9XcWIQCU5=u7 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 Bw$=au literal 0 HcmV?d00001 diff --git a/website/static/img/app_flame.png b/website/static/img/app_flame.png new file mode 100644 index 0000000000000000000000000000000000000000..ba9b69e45fa73d3f9298e77ec4a167a38f7ecf7b GIT binary patch literal 74845 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 u_1V@IZCsr_eG=!1bAOm;|0UAW6F{ Date: Wed, 16 Mar 2022 09:57:08 +0100 Subject: [PATCH 249/302] add kitsu --- website/static/img/app_kitsu.png | Bin 0 -> 5999 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 website/static/img/app_kitsu.png diff --git a/website/static/img/app_kitsu.png b/website/static/img/app_kitsu.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e3d00ebd953a38b70a48e80a5112136f912104 GIT binary patch literal 5999 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 literal 0 HcmV?d00001 From b0a8809cbfceaa58a11171315d9579479885f02f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 16 Mar 2022 10:19:22 +0100 Subject: [PATCH 250/302] dnxhd is adding bitrate in case of dnxhd profile --- openpype/lib/transcoding.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 6181ff6d13..6bab6a8160 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -705,6 +705,12 @@ def _ffmpeg_dnxhd_codec_args(stream_data, source_ffmpeg_cmd): profile = stream_data.get("profile") or "" # Lower profile and replace space with underscore cleaned_profile = profile.lower().replace(" ", "_") + + # TODO validate this statement + # Looks like using 'dnxhd' profile must have set bit rate and in that case + # should be used bitrate from source. + # - related attributes 'bit_rate_defined', 'bit_rate_must_be_defined' + bit_rate_must_be_defined = True dnx_profiles = { "dnxhd", "dnxhr_lb", @@ -714,6 +720,8 @@ def _ffmpeg_dnxhd_codec_args(stream_data, source_ffmpeg_cmd): "dnxhr_444" } if cleaned_profile in dnx_profiles: + if cleaned_profile != "dnxhd": + bit_rate_must_be_defined = False output.extend(["-profile:v", cleaned_profile]) pix_fmt = stream_data.get("pix_fmt") @@ -721,15 +729,28 @@ def _ffmpeg_dnxhd_codec_args(stream_data, source_ffmpeg_cmd): output.extend(["-pix_fmt", pix_fmt]) # Use arguments from source if are available source arguments + bit_rate_defined = False if source_ffmpeg_cmd: - copy_args = ( - "-b:v", "-vb", - ) + # Define bitrate arguments + bit_rate_args = ("-b:v", "-vb",) + # Seprate the two variables in case something else should be copied + # from source command + copy_args = [] + copy_args.extend(bit_rate_args) + args = source_ffmpeg_cmd.split(" ") for idx, arg in enumerate(args): if arg in copy_args: + if arg in bit_rate_args: + bit_rate_defined = True output.extend([arg, args[idx + 1]]) + # Add bitrate if needed + if bit_rate_must_be_defined and not bit_rate_defined: + src_bit_rate = stream_data.get("bit_rate") + if src_bit_rate: + output.extend(["-b:v", src_bit_rate]) + output.extend(["-g", "1"]) return output From fedde965c1ab77093b915231db34fad2831e4a0f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 16 Mar 2022 10:19:38 +0100 Subject: [PATCH 251/302] pass source ffmpeg argument if available in extract review slate --- openpype/plugins/publish/extract_review_slate.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index 460d546340..505ae75169 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -360,11 +360,14 @@ class ExtractReviewSlate(openpype.api.Extractor): ) return codec_args + source_ffmpeg_cmd = repre.get("ffmpeg_cmd") codec_args.extend( - get_ffmpeg_format_args(ffprobe_data) + get_ffmpeg_format_args(ffprobe_data, source_ffmpeg_cmd) ) codec_args.extend( - get_ffmpeg_codec_args(ffprobe_data, logger=self.log) + get_ffmpeg_codec_args( + ffprobe_data, source_ffmpeg_cmd, logger=self.log + ) ) return codec_args From 30788b6d382deaacb0de08d0768313ec585a6067 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 16 Mar 2022 10:20:36 +0100 Subject: [PATCH 252/302] add overmind studios --- website/src/pages/index.js | 5 +++++ website/static/img/OMS_logo_black_color.png | Bin 0 -> 541863 bytes 2 files changed, 5 insertions(+) create mode 100644 website/static/img/OMS_logo_black_color.png diff --git a/website/src/pages/index.js b/website/src/pages/index.js index e01ffc60e1..902505e134 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -134,6 +134,11 @@ const studios = [ title: "Lumine Studio", image: "/img/LUMINE_LogoMaster_black_2k.png", infoLink: "https://www.luminestudio.com/", + }, + { + title: "Overmind Studios", + image: "/img/OMS_logo_black_color.png", + infoLink: "https://www.overmind-studios.de/", } ]; diff --git a/website/static/img/OMS_logo_black_color.png b/website/static/img/OMS_logo_black_color.png new file mode 100644 index 0000000000000000000000000000000000000000..9046927e32cc2c70ab5bb337defeaa447f5a3ce1 GIT binary patch literal 541863 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 BOOQ-;JnIS$z&K$fv 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

    #_`d35o;^6Y!k9=Y$)og*#-mZVug9r$(v*dx zV@<0YUfWz1PHI9kbgy@P#Prh+i>ivMREURNG?vIRS@oA9{WSMNWB#mjib}$cYJ?)i zt&BTL*G87=ww?bkW)JaiT`lA2~4?%KS%+#H0{+v3Ls?Ih+` z&8s%vrLU9hgtIN`+bvRgw@d!{FqNV6RP&hDajxlkAaCS)+w%~(6AgGycx{uZ^GVkK zpOGS;8B|U6A9HI#c<)-9rPW}6$^B=rhVgXFvRy!7}l3Of-5KvxY`%RI}_5{lI=mEnR&c5wMp+po*l>Wq}j zz+d(eeHsk?`t7N=Y4Hmo%PEFZeKtzUVveEXPnIpAW1t`ldW2lHI@X4=g84>Ded(~X z<=>sKNiwOQGe&FNUtRG@GuSY;z%lshzs+mgH3Qgf=Z92(6)6zf5gayJmyK&go_=eOB1J)upLIKj`Ab`R*6$MWZQ`i4>GCa+|RF^`*CnZzXW4G(%#56wNg~0M_8oWcV;y;Y&FGYzfG!HvYHPO6~u{&Chgl z!MhcJA;$WvCU_k1EG>*U<~dvAB%A?oWkbKRzFA><5|;|k1&&jGPr{aLKXC|JAldSP z^w<+)N(+6wNIM(-38kxFad3O_jVoT#62--L!f=iAWbe%>L=gG$3l0Uv+>FK}c62S& zy36HL6**3dJ&sd~>TS*t$6NTlRZ(*uq~l)Az~eW@ zqs`#N7Xyo{%cZ_Bde`IAgE3cdhCv9HWV|RQb!4 z9>|P*#5|L^$9^S`D*CZ-@jRDuut~A-NeNVwkjs~6*}UvFwU6SQ;7NqF=)V-|i7Zh* zP`1cha|rP^RHWF?KV`cgyJTmHRKa*>HLR2Ze_P=2E!jjSS$yWzM{l`w$rE*4G)ymN zM3}eoViEXRE_hpj(Rj>EO13jxBI8mI=U6_MQ{k4>`BPY7XuMpyWdBSey&yF6#*P?z z!ngivNt)P}CW-qDR)M<`CK#Gs3Dxp$QocXgG9)EQk{r?YpLJ{T9!;p_BB^*DJ?%v^ zI-~VO;XZQ*yd1o$z#{**?)>q8)a52cE9wNF;;HR;=W~hZ37EG&P1LZ}uQD6&^YnU|oNqfgXcvKz}9lsoSM^sjR zV8IXqRmiHg-|7=Vyq?)%rnZ4wC#lRjs|`N2SCF83uZ8kn*=f-(-_)s-Zqh<^79pmv z$2c@G)UY2QU}CNH-tkSSoDsgatr42}ZB|$w_5J}#-UcX%`?Q&3?Sdunm)8KZZE1^= zU%6eHL01>%J=(=J--5|@y_*5b9;e^eZWDN6#I+E+5HkhVTnoN>{`^$#KdSGgvHglk zh6yt%v*%pC7fBW>toPmAweXW4dJ(}o+tqKqqyuwLImbB&@cj2&qP^R7V*IEFzAh_g zE2dS7d;l}(4f7uJU5KrncgyosHRf?qtd6;4R4*hkFB9j**`C=6gh)Ri*pMMskQwiJpEkna27Ny&zDlwB_e8BLU zM*$(WCZr4o=K7#n7kfqjesY%j6!M;TXBJPjBkaIFc=K zjO=BzLwI%U`Gm}EdILNg^ZIAF216y2+We>q{^@wpCG%Cw2^RulRS}6Uw;z{Us-vje zZe;0sqY2aahkGP!QLkUP$f}+h{wo?q>pR6ZSQkjj1-h|dj`gcD6>G` z=}1ww2yLajY_2hVGRU%uz95H`Opkc=>`8mnvif5POszTk)HvtfTIH zeR?&Pzp-U)&)B;Reh4tNyZzvC2AG2Guf)n>^>Jm!kUw97>T%VmuT<#K_5dcZU1l|F zH!Ek6Z2YE};-9{j*X@Qq?(MTyxkp8AUbFFFR@;@)o4ilJPUVi%fEDoh-DTD-Tji5B z+#>u5XDY(?Wd9O@hA*^58Vo;XE>@^Dy3 zM8Az@Cl}5jTvYD&%m%|-e?2s$P@O6q{_b~GJ@Jimp--e3)CUQg5*1>qsgY0@W$V^4 zDGu`C&)FAT53GRJ0Zr?@$add9jX!`6n}c%?N6r(yJ?&A_|DdSZ1Pgl3zBU|;I($vm z9W5rU+lu=oh50U<`Eo%8F)s^S;LWQQ_A*sopfu=q|IQhGpW!E|!7wm@NeNS!oY))n zjWBhaujtf#UDu1!$35k~M`-3K?qPw8o`*8$;73Djtqckw+q6AL?00)Qy{!D)uFh;L zk%O>isH6Ur)X3CeA{BDuYmafJ$?j=Kh<5duf!B?sqE!9RN3u%5qe8HxwGqxH;<3%p zeH6N)KpzJ$(mdJ3IWfQ87+0m&p(polrgVKjP>pY9au5angel@{c{nr<9pFxH8U#-RDRcrA8+?fGZ z9oMP@PM10mjbPs^A_{k&x!QWzDy~2mwC%xX5pU+KHS;9^jMbQ;Qq4ekft4fdfyQsw zxa`;@w$FMHw%lxt4UZ%OfC6s~IWs^PdG{UPf)O_y8)6vlv`&MBa?Km=Hx~(j_byL8 zME~&utfPxX-bTWW52-rV1~DLTMXf8P<#Z;Ci@ySLA=?Jcx+F-}^|SS1NYuq!(J;c6 zr&my6#g%FZGn2eY*+raN|IzG%j$*1wkWFQ$>$PUk=`}~Gw+PCu`D)fmPTIWVnIoYL zze*y(Dq7}yenUtt>+)=hup%mECC-d$KlE{&lV$pwXi7HEkf~rHcUNol#EM;*Y2koN zG}V|_s5722O}KRQpz5p*towN>hDs7e``ditD~Uk2dxE?b0S%lEu+?$YBe zo$slI&|VEc;xtaz{cNjqy*fRr{r)Nz?H6bd$#D$3Zm#On@%vw&`FS*!!3NXUdk<%$ z6E?a^VJ}&2JXkl-mcRU2bz`@N@Tgz2gBC}#B|%D>w%TAyM(ap!Q))ch5;5Z1k5cF$ zSs6lH6ofc{0fMz8QDRq)V@N)tB!ZY~HX6=t5N11={ zP~YMGc8%#bztSTm|G~MB|FlWK_%`TvDqVawUVAb%F5PV8Q|HF{ZP5?6)Z$GWCJqc< zb9%W_zHYZ?zrk%@`}m)PFN*;eT%PBVA|(AQG4xT|rX+C>(aN74|3u(3X_b1KMIbI~ zm+DlR{)u?$GwU#XweGxbe>cn4jIJcJVmqz~gqdC>jAmj%Xl?b~Q)xfXniJ3HMQEE6Jv8Gi4ma@b$#d7LKL0=L~8EpP47S#=()g^ih@cVsa!e2k+ zYv8hUlGblZFY-^lr5A_@|<>cr!5DDL0M!iI)t!4B_@~xiP582a% z9+Jig4hVrP!8+jTK1mTXp~IH)GnT;8v8_I@rA5G8aq+JAI$?fu>d9qw(ne(=ek`Nc z%-_%!d6a!FO3@|a;tLQD>2H7fQmsEXvk1?y!P!51PEOOGUmL~ioiZ23mK9Ys&EL>$ z@P`UT#(aHN(n%3su9kXJ+&5N>zS(k=pRwC>=#M(s1U6Gc?|-q@tr_&5^yiAYw<1K> ztAR9RZlP10w0IiCj-eEKRArlX)U8ap6uNYKSO{+WVzK3XANxuz^cVEYtv?c)XUwoY zd&IxCG#T^Ocqz}{xRw0pFC~FVH3ytG^alIS3*ne|*i>JnN%fbjQ;OB#7WO_4 zZKLWKhr5T0{z&16uk)wSvygK}z{{5ZYZ){>bK@&QUJ=t;_2+dD@h^C&zYU$7i~-Ly z0jtllL+xv@C)vj?kq6ghPNtVHy_Bv>>l$Ij-jA7`L>vP&pPU*HEF|izaoKF6=kPuE zLB?^&c9}zIiS&tVfG^A%iy@Zw7g(V)Vr3&AmWqny!OTDMfObi_LLzJ2@8=c~l0O`- zi*)g_@aV~xq&ux-=hqr70PoHzG^L6@g^^#8iC(Z+Z;e>u9&_^bYEr6$?cXttX59FZ zsCVTZ*eh0AY;O7eS1HS)Yo{qZZJNkZePwJQ@^;;_P!7N$sHDxfBD!zZB>AW2 z2m$We&Yd8PDa)SOPOS9}V!C#+)1Vu(o0H%6qCi<-tJ22rt7v<=VZ|SH+G`+H*0N*s zTC6?ysxRG-X@=jI*i%tm*E6CO}>K~_sP z=x!`ttDSt+N^`fOb$VU3F_GnZF}fbxun?6@|A!@;FP9#y4ksKWB0Pzwz=uOHIp@dx zv7g8)qu{n#ZjUv`lQW@Y0coYb?gl`>UHBsSTf2?akp+o5>pq7!jKfXJG zLe>lHY8^=Lck7gJz-AV-zhYyyI|Tz{{cdjOxydbUlEQ(TmR``0rn|CPX{HfKn|acQ zW`MQa&G`Bj@LEru?h9&5CudElM5wd7dK!z7hED*PNS) zJlG1Fw7%;+U9Fg_@SY5M9Weh)7GkpxUt#|RDw;Z67`I^DlN{JBMN z8Bq~#0oBNTJSey@J8Gj}9zOE+Lc|iCpK^v2FFjE(Wd6uP_~1dMs&wZW@)eF*4`IFV z*Bg3Rka_TgR#2lnn!|4HI+@=Az2ornU$_WImya7P7jRRWQoSymBcGN{E(@-yQm| zg;tFSGp)6(G6fP2{hgm*PTJYix#$;T$$j|f%-oML!@t8)8Ugi= zK3&)?B4U=Z)$hxlI5+cAKEfIG;uzYVX{>)IVGX{3AvBGF5q_;EeVirbCTp>2{E|jg zt~%j#cO^W2zNz%n=aPq!`P$Rh^8R>nn;dJptNJ*0k;xv(uFxtg+L}S{T^-sM%(S&4 zGFRbmXDK_xr_1^U@^L+ci(*;_E z2mKFfboIYD1b{*(p9q9h#el+Rz!#F$qqemIyhg1!hOUaj-mzj2V=d589Ra10JCzAE zGhzp!82c0o!4&U2#q170xbi+v&VkT=_Q55pj8=w52MTR$svX=csB=FRl{=XO_WYz+_#BN) zkv$T+`SPKN?o&Q|hOM{Im1ck7oRRo}(tbH-<*t~p0Nl2!^oc0s>NXArtYP#`wL+mt z_L{t2{{50F$U9b+JDVLeUGd-|G2mM-Jns1>gs? z?!9JgIutMwX0BVvh47!=UfZ47*wY**hmgELA)?^o&V!lJ5R`<3)a_<8&1S~i(;G6` zvTB49z7zG9mif*+{P<%*2&`7ihB#qNbY83KwVmN?#U-XlYwlL@#8_M>XM~k4W(;Si z5ykVa5vn!$iDpk4*-tPz2C1jP|EQxoOmHvwteD)NHj@nsXx)2#Zl3<{x%t?y)>!dB z^aS6*yvnHJ1xMoGSx8x9Jn%JNn!FL&zFq;3olTJ4z2gz|O9+6f2`wT+*O^v9bt~)1 zIQx3s8QT3F&?|qwFI)m&WO%iT<|}Csh2f_*!l+M_*`6ty+d}U+;XjotG-Rxm01Ydk zzq6Jg{hK__BC3@n9awaQ-Us1rMIu}eLp9T+s2Qz(G=W))LMb3V5J^=1E&X#cD?NO8 zrR&mF7(|c6hpb!fEB9$ZW!$a&3@_a3$37#!I2&+}e``KPo+}x}H{Ppwd!eoy7hRD6 zX=AHvq7z!zmEzRYO7)}Ksh1 zX+r6VKcVj}Y62#oeRoyBhv*wbb_wMLgQsmngoC%W3OPJN_&XYVxb-dulWUnL^;3S> z7E1R#viww70}5jv6ech)wJRc1Dx269{^Ka9IoknK@E#wV(~rSq!?6z@Ad-I1wH3Ga z0I>FiCxPecl#H@UDHk3EgUm#IWCk(nMr!WcllQTc$kXAoJUzmMxaf*OF*~ z{NT){Rj<%6XnB!U&TH{Z1WAuzm^kA%;dvYB_`#*1%C_0Sf`GQqv~X1{@Wa4ISgw1d zD&^SrW}@Itb;QP+>;uaj-#|yey+<){?MiViWKR<Iq*;CX819F94Lu(Ef{skbkg1 z25f#wrf1xb8G9I3?%8|U8DqI-u*9C*|kwJy2%D+`Cl0_KNwxf=GF11^5Pe%sA-rZe~>7Mz(E zNAfeg3YZ9SJS2>t8z^^g-s9{^>iK}}s?F;*W=q9LIWXhSNhIMRUQ~!|u_L-JeO6-d z`|xTqE)2PPs|E5ti6aU=P3BtNo}`7rv!jSqzhlzBTO|6eTQMsSrJ;C>ttn%lo4&`x zpX~C}V~@vJKM{I>hTmu%5?(8fvALcN(d1M6yNo&7PTHv2rVm}5ISqpzf(*43>#cgC zlnwpa481ePn|*E{@{}ZK-?XYIEbNP^)RaxCOQP+&T z-ag>#@V1@`WN<{9J#KiS;M_LgyFUti@;kK%M2Ole5>XKm4RC8|$WOV-V@{@P{u!l$ z#=>m1f4f_)J8zn^2o8DDN|N(^UXVimvBe>iUpMd7kHV&h{2oHT%<5BU$SvQRyx7)A zR()tjV)2E@E;nN>(y59LHBMqiceS8F45VqI$DK_|}$AOYe!g`N?jO2zL+p=io|2qemDnD5KFR_T=*=<~2ZKwXHF{Lc4 z_L%%ZA^<($5%@eU^n;gK6chPNs>!KnjmdZMyFqr*F2PRmg>VE^bg~#*u{Y`NEa#+M zWXu}%BZt{aZB~y>Tvg2=w713a4V412W9zfFNB$}_v&s52hsJhgik%{3-EQ%|2`ghoZ107|>4wY4j2yb9 z?shvZImdE*_(@CPF}$1KqW?f+-kX))2=T@5V9CXNIdAVDr>YEH?_l1H6=AmSL@rM$ zbB9lLLQ^JX*gik>jb@a}7(;gK-6Y_V-F84JV(J^xi@A{NZOqa<1 zqCR@Q<*~!c+QD32!|Aa?>H(*9D1#)dH%Eb$F&Qp{2I+^FygC|L z2^u@MF)?yMhc4+>?7dd^qi4b+nlT0y?z`93CCF2C^lp!`lKv!5t7~wrx3$EBq^XP7 z!h*}JmR_Q^bX>m#3(Q(s5hJA8-K$kM1!KCJ=Zwl!7lJRS?Y@;XH5lMqashK6;KC!t4w?0 z|4Smg{vi>}kP3ven#@;#cvjs0K^EK^+kBH48DU5EC~M*P-dd63_2pPqG}ae$qUdY} zWYl2&nMIXq!rC{C>UIKQxpjw^)di7R;}#0R%3^z76hs=4?Pp-iaQeUeuZ6OX`FcNN~2D!P>))`tThTnW5>J#*05U*j)jW=txBO^aSmdI)9 zL-=9FhBB(d8i_qyGdx%X522A)Qc05y;+Wp?tSl98Se9K!>>7L8yew4V>vHKwz#1C_ zb)R4IkM}AsqUw?odJ|LJE&Jaj+;S2D+s4|2@6rp|G=e^Di1-~8j9CnGZge?~KktwE zN`DOO&l(%gI^2E8{~qScYC9>>_M&b->XgSUbLH>t@Kdlwa9+K^S|+WDx^R*Aap@qd z-9zEfhZJ1gA{YYmBf|uxWXfM%B8LKkZYpN+Eq62eqFwQpJ^pErj zF?!{En&?S8}#V5Wov9*A}FD?)7i;zj>{;{C8SNy~a-dmtB^2 z?%dq)0Pc_htDCRCtNlK$1kc0BLTL72Haz_xM2*{=FZ2U~%Z^ihZU|O-Oh0~utSP^< z^!bx_PZcyGCnAcLfB1U=mx`h}-#4U%oe4?D9{!wWj05LY)L$Z8zh_7U#@z;&i3&j! zG#EtN?DWLT(s<(>!OQ@Mrxr!cE7T`kC_`Qs;bpL~%=!a#OQp?nAXwwa?`Ws8Dpf@0 zuFM`wAN+1Q@^8r92elOZfwxfRA@(WpORC@LwB?jf;PmefvyZ z^_(uCOZ*O@X5~m_zFGT8mM}$Bq+PwUWR3-qs)tpO?5=Svn0$C~pYV+TKEu|({`(X2A0K>PZIKfS-ss$R9@nT* zId8>RkHzgRRN{C2^k4>t0@6n~9@<#_oHqI>#U&adJxwFrOXn3uIWJZpf}|ZN0b|1q z)wlJPkC01)ERR%lV|_*k=y9b&wbn03wYz?wA*%SwFlISqHLlUy0i4Ve^cWbv_N>5%-!TZ{hM@4ChhkpUhhW(#4F^Po77IGI`7+wfEXB=;r%^D|DvN~`iCV0 z*&m;IQhSL`4?j|qXbVgDW;WuQ0@sjpnT^hOjACK~4k8_wPdq>QuKTyOKYm2~SL?#Z z|1Xtb0sxYimR{gGlYw_c%AapMp32uEtwe6`=j$;r3C^r2-cn(oelbZgrH9yobVwfC z6VFhKnKjk`bS-bQDQw|IuwU!g0*G&@Y-q9Z4Y~^`H<~bZ1U4w)=uc$P5uND)1c#R) ztLH7#I6Kt;| zFc5+WRDea)_V5O}i0>dIhx(veUBhO%B-V?zK*PwzjEuOu&Q9W^bw`%uMw(IOsGDZf>5tXPa(=d(GoaZS>L(RT#N{f(gfpUXOs>&r~h$ z`E(>SK%i%H6K+D&4@( ziCAE4IM!mtoU)ff*FVbWKhuNc|3Jdd($H{`CmzBn`W}+{W#Bd+w+FM`T4My81iX|zeGN)Qs448or$7L~U$$CQ$cY)y*UOakh zp}ywBCm0W4@n1795Hk!`3t*2=7`In@Nuqy#26Q6+C4WH&XIy@3QdmeE4r{@ztN!zl zMR{6_CVrSZtqZHEGfB&xm9L%T141q9BD+57wKE$~~Ux7=I@Z8g2BSCjpC`v2(VuP9lL{jYkCul^6yV1jgnNi(bj zTsn9W_})pk5%MJ4Uzlq-f{cR(^+d(5U9}Cfje!ZjqcR?QNS9{m+{!;wp@-|U<$bM? zrQXL{bZX4~_Pv?tB2McyNho$rgn+yKE~h1EFl1?Kea@uLctKg5Am#@skKVI-md+<8 zml`&5#9dYIlq?|(JT4Kb9P-7RZVH$`ltC}tiRyp+X5=nV1S{aD_L{KLv7RJ|omQZu zQUX(Mn3Na*Tak0}=uod#sr$w5w7}-|{8c`}d*N#ii+%%HvP2XVd;0GB4(cNK^_E*r z!b)e_VzoiyL59KtA=UbQi?ob=*DmlK6Mb^C`&=nB;qHg;(s6MzK>)gUf5%p;F^*$b zCE8Bi+O+Q4o*nf~I{#SSWrk&^D!TL8B|NmmGWtT9q%wM-e@&H?j7bo?)V-Mfci7DV zmOd0W=P|}aAuXCgWAyN&)vL7upzM*x0VwIi&%MBjLbutKt+JA=B}p+bNZF8!LR*Ib+wyCKIx{H>!MzYnNaG$sf}_%m@2k2rLEJpR zGYb<&qX3UI-t?`9O!Fh-NVoshx2Z~234`~LW)DJ=wCJ8^JWni$jyoReG_Ou=t4drU zz=LDVnZKQ6tBKTYpg73Uy9@jh1s+3WAa<09P#{E&eMxgr3YT4%W49IN~BRPE!=C*9e&aWqbx{(>mB8wZ>32o}Td10~HRG#05ySZF>zWroB>hm+%=wTs=LG!?%)V6i~ zwF*6X*xcT%svTJGaIQ~f>|+>qe~S3^M6`{(4kN9Koa`i)3$HT6%-}|Srs2}3_((K+ zm017Qpzl-G0ZWH#Mcu$HHMYC`{WHzDD#?%Q%YHt2SmEiDufWi;8H8M@PGaNd}YAQM)ThM2JI+}FTl?9L3; z=gb6A@HF{WU^2CyQB`D^d@FhnR;&FRIc1KL^~xfO2Kzu_B4u2Roo zoNu*U$`7~U*!xr4j({RHcC%9cDEv4yMOOkmy&rua2f0?2&EC$Tt|Hxu*Y2U>!zr8A*Ttvs%%;C7-}UbdfL)}{4{6AB|PMHmg`Db>MWp!|`+ zF7@(%_ zmilmH)dci)X3s;EA#HlRMEAw~KVE=GZ;}t6=d?0@J>r3iO*(bNe`BJdC5wgEmvN$8 zIU&P`BRm%MTq=b{L>LK}3HVuV&S6f&Re_}bOwyt%tS;ITNeG0F078reV0M-!T@u&F zg)J1$q@r>@guQji+=phwZ;Z{5By0#w?CX8vo$W3mo$$*lvwWq>Cw!O{5;jxf5>R;o z?DAwNq>j7junCFRy||u!zV!%5@x{JY75@)vU)bLzD5;_CB0Jq;CZX9XKTP&%@iDip%{w&!S?2<;e)kS~Z`Om;@i4>S+g8@ENWJ zo+{{lC;&+Eo_CH}e=0urrI{6<;*ip;xK*b_b0DzMf8llT<~_>O2w3+=jn`yfgX27% zQ7V!M-uk5_AbLE>*7=7+NT@|uD{;Z6uoB~fB&c$+;6e!U(FhEiD6q1WBl9}xUGUMV zC8?R?+smCY{H-bQWX`949Rs%UF^5k1;Ke^qJ8`puiJoQC(uKby-EPE?M#m$Z)iMY? zJOMT9L_DOG^{5f7$1d5t-_PSZTj_0`vJ@aHMFk9}JPS>`8I!5hr)gd=%sj>qFMg(F zyn5<;Cvr6P;^BKv@PaQ$#SbYsojckS%0K&8;@GU~|F6aoLC*6JIR&gU0UlfFnPHfx zXG(V=A}{Q%gHx-VT}%*=V;?;zz8>%cwMpkYTa;+r^He8#cp!kc%r%&J^EMCswv$;f zjkIy)bC5!i5u@qMTwB=EjU?vwu!LA{h9Cmx24#*nfH=t8P=unTHo2164XZ2|6-+{uj%TZbN#uOtqlZNn-2j(&ggVdPf7B`D%A_73o`aaJ#(L`gMTF}wli7@O-~WG_EjkBZbpwllqD ztlELF%J0rzvj*x(vZ9iqDu(1yvlf?Cx?`4DLKVY1D8uhq0W4VOhaOL) z`wIdNht6n?F6Pd{=U*$xpS+(o`|%9_C8Digsv!TaSgSVc`d)R86M!!*#ON;1Vhwt- z2A6fWXB5g4aLI$!C?wo)7l6oGib;6|FfIgxvgyOo6naM>f`kd3-^b|!zc9?|smP(x zejzKKM{Q2iT_h%fZn3H1^)A8x#T_?5@W+B^{q+*Rn+Q-|A9<+f7Hwxgu?I`o5M`|p z<^%N?o-CQM@;OVY#UPQv- zN%yJk(qE0dzim+T(6d^$4!Y+wmVp6RvCzj?#WtrH-p(cOi85W|S??CO9D6DGQ%?XX z&gJ(vgp=eL13CiHPQKe@`|Wq8bsAc@rQo2zEq~=MmMuP7y3HX$>-xykHvJNJc5j)( zt5FHBD)8i{p*Z`Z8;P{w(4mphlLm3=<(D_bb6(6M1#Xc!-(^7UEoR^!yYDSd#9cFhr0hfFwVhYc>pdbCT*Tj?5r|wsqn6Sx2hgJ8?N8uIGu?i=@ud^ zfEGro%lw+9uM{5^Ov&z`i#v$jc_@3LYi7W!IsU~K?P@j@AAPMs^3%J#WPicu=j7)T znAzFl7V5g+E0b{e|K@^Q?9Rr2TI6=vY8@7^j*M#u zL?pyOG2R7Le51F{8Aopv*zG~QkNtU|qB)$o2t`CZ$U@%}OCa;_H@%1Z%!B@^IyxT% zpXx(f2Prcm$`vmCeURi^OqF}2^< zSb*xSJ$-k-j!6=R1q0Ae+rwK9gm!_yRN|C32Dc&d>0_l+@+si| z$JAK{H2r>mf1`(#QX(x1NQX2Vr6M7XbeD89nt@0up|r$6Ksuy56+~hnog<`SqepY^ z`@etRc(ms`*L6PUdY{*MADm1twIg$#)e{l+0)O~Kd-!`s+GKrKp={wYYlr&LaM>*QbWZR!+^ezOI3C6WC z4N9&%@(>NzG0A_P6npJ#IcJdK7#kVyqsT-yAa1RC)*9bo zgV#OEiVf{-U)lt)yX^17jd8>GY^`?FNT#L0Lp!a6Cz>1H;Kj$QO`Z%3gh_qZ8y_)N zbU8f6jgyCMchMSgAjHDY+Y^YvfEPwu=fVA|>rLI8O>nv45?On$hspO9FiyLj<#Qyz zpgrg$z~GkQ0BW2q1xSUO?*&>stXsUzR(gY4zmLXy@Bd;>=JW@@KabO`CZNN$>-7Ii z8!LtEDcyfpqX&|G*nB~2j(xWNz-k-sIyO^R5+Uc>2Pv*6laxf-%r<>zA@)b12cBGx zuguw$lLJ9Gk_K-mQsiYbivHL?5!N|M6=yy*?d~iO`Gqzy?oL z<{1}Jec|l8#IJS?Ub3rOhw(})AB;?a|LT9o13zM2l{I1n zkEE0Y1GGB^6WUj_9~MP$PtpBB_;;Kfn@&miB-M@Dx;NVOXed(d{+b^_ny`xIk;PSD zxSgE|T2bDdF14wV$#It-!Wnt(dTW?B6m!(k;ZAR9)9ebL_WvdU^+tvTe0VqeBvv5- zd|?Y?h?)ie8fgAf5;SP>=vnzJdga_DE%^Y<<9L?cb_X7CXL{I6UP>pXH|ppJ(4Cj|7p;DdN|PGW%cR&jhX#D{mrwpS2Qq2JBAl=t2H1Ci<;_ zn7=hI${*On;4Da{b*+od85_4(X5s2rf7H7WK^YC9U0-`>Sy?<_<*?PH>j^!OpUWHP z(A*cPIPNBLev{QA-%3CuhD%LkuNeVCfF`Lj1BM(4<4iN_NeMos79Uj3mW{fik zU2W!J{$zkWjdEXf*P$q8p}rcfnYo8yQe39fjH`PQ@cLe+wigM zq1$j{it|*kz5K&VTl@_FS`J`NTy&DIxia8q&JccX3(l+e0VVSKX=6H@Q;f0b@$m9$|OEGg0_b11tQKp7on2RXmronWla;+iNAPT)|M*Ddc>LF ztTz@}4a&#~&GmlU#nv57&~)L@?d=*w@Xmvk#s}*r_kQp8bJ=ef%KO?g!)hzO@HdCK z*T>g$F(7Rtb`}fq$mVjEk{ttI_m<+c&gSk!MT2q7W`9_BzekW#PZoX?pLZW4W>W7L zDrMv~mbn_liQ9RwXQ`OyBkMhlbuh=6q8(Y1_xw3a&PldCrL7shE-1CBBN7eAtc?N~ z4O>*YVN*wTr-TN>o~?&;xz%l$%n+v5&#QBe(0k45=n~au%QtfMtA_`eUh$Vck9=P! z=4DQx(gn@D1rFQMXFtg=H3Ao8t{tQwnu3;~*rIYu`rmWl{_UU6Dz*Aw8b%vn5x-Lm zVn2gE&qj9&!w)*JoubQ-L4`lRA#Ad)Gv48r_tQYs#48MByC}+^s<6;7COm&844ZuJ z!*8-*UvbUiu>b}mo3muA?%Zd*-2rWs1YRrY=E+HiYmN+lSBu~p(((VYH1~s8G%NIQ ze~It?{XVSez*yFaCQzkOn8h4!E_EG7NJLXK4c^oD9a(u}2Jf^w#sAWHf`eVISa)1F zdGjZ;w~^BtqM%|JrY|f1>W`ZfAE~|O_u!HVicq>P@5IAaj1sZ6v zUf;9pc7AtZL$#uiQ@{C$HN}}I&Zw_hWE^If*2ZEG1wY|=+=gP}4RGD{D8Yhr_gbrx z%(P}j&-)v_$O#5drfHXY4qh#%c9}jKsnGr9P{xy=7jATpsF5oDF|k)Zct7X=fFH2m zNoAf3s9;4s?LUaEU$^rJ>C?{;Dwv^dLyj^+FFzvM73rs?A#8oeaitf02TnVmUvVe< zvWt_G){v?t`qPQMTIXGKH&!@aXz(rEH#cE76ju!7n>h`v_Xl1t^j-2oseBn9m5k+~ z2^Uk*mR||_(gx95n8P#)fB1HujPxKo{tb;)Iw%TtULkJu%i1#B6OUYl?~ix*oZNf@jXv>@qMexo4bKOYi@R}EH1Gxh&cSY|Mf`V7y88iq=mc{kATY*0M?Vj6)S1E z-q5&4#|Mw9?8kLnOi^jPkmJT(7R-q(T(1La5N0f~2WBLtt!}ny6vaGPV05d|+3*QR z)OQxMPYUO~N-;;2FGU+nH~JR86iYUZTzSx$KUtw*uycure6uB3Mwn0b(rDXDceqrD zTGJ)$PX%E!0gI!k9MY+<4&+6bKz!VS>T&3EEQI0g#iit2@=^WX_@-o2hN!1{wQXhH zjnf&Mv?e*a0Ag4LY8wr5vjZwc=h#EobHsWckI$5RoebEAJSY?6wf;jONoYJ*_Le0x z=Af84&7!7;b+=_6zBig5uDq%neP__YGl#7*2A>lAYo+UHT-SZR8^1qp#PR{s_-3cZWac>FYR0c1^22-TaI-md0&|5~rjPrf!8{h`LclV14JdW;~hiM4lFGSpOYk@mtlW@Ov19Hdga^m*BEKmt2 z#$aX>r(jjK8OEr{FY<6n8%*l$yD;;+XI^ECY2=YJV{fdypQAf7Jn4*}N#hD6!i><1E_Bf@44ggB(PvxC^Si5#yScrn2*|g0CUf=4-;>j zl;RWZG*>9nQOH%k122yi%m6snrj|f;i|&n>LQl)dQ2AkyP!8o})yIcF*1w70rn~Xa z%AxHiR(mZf=_wN#v<6MvSRjB_<}<(A@2eIxr0i5@0fSOhT5aW3be-Q5E?^E_k6z9_;Kv!+b4;Zl`?mRYOq7}_ueGD>ehravac%q-HEchPl1u_al240n9N z0UIJ)ZDg5D&KR66K^IE#jcMi}HMPu3N&)cow*A&8G?Dt{19q_Imj42>?boE#yJ*(F zEY5|NPM`8rG;SQXKjVeTs-8_;pzS*DR}i|%)Qc>j(+8EkA{~gq1d3&!7vBEC<^&AR zS@}OIrfcJW4ud);R(FuW?MwyuPI2(InDF_o3&UfPM@;BYR7!TYj@LbZ(lmRVHm8z8 z14a?M+)14Yq0slhtv1stM2RS?MJu9UVmW%!uw=~DFoOjI9s$|$8L7NuPu?kmcJ?1` zE^KONo0q&(PK~wKjwdAfJU7|Xl7(R|IpDOj(Ei;EtkTIxEogs)+k7cE;&v-gzHonI zUWY}L!oE70WsnmcMmo)Vcq z50&xBb<#|*Qgq9VbTIVG+~94S%k^!F?Z#%d08PeqYHA3LaJUmcKW z=w^Ao{dV*9Q4)yf)K3}|^)B76Wte#HNUqM0v83{x$K#M;%(G`Cyz4wbSH=;n&C#}W z;p0%KVkhom&-mENUX&*trd4BB<}S*S*pSQQfONgLg`{(JFI3SCo?m8ZWenDGE z@-iYYqABqWE!K!#_yb05q8MEha^#GRpq0Lr@cqGk!_9OTiNDlJD_j2W>;v z#_9}3i66PWO*M(h+qEIGsV~R(syL-QLpqT{+o)1&!&$GKUU#PDz?~NjGtzSzo*6jP8d$1?!7k=d#@ekSwWmX3IXQ&~&6vJynIEC%@uK&WH)CcdUZQCNd z4H@b8f((B=153!fPS|52iMZmU$Ts87w4*OXOtYq!f;1H0YE6)a-pJ~c`IY3`dp;ea zE+N-$G8NF{)g#;L8Fg$W$~8!_dY)4v@M&tp1&3vszfYs^%liI= zZF}mO%XC@b>UkDBNhI|vILhwNIF_G4#6ZU zBpJddWix#eAz7r8C}E|TQ0PtG9TsuDESJ~(6|?AW_37Cr1gv-|XtbqKp6xkn2V1lW zY|JZM*Y~+@%(DUnE#_tCY87SB#pS73mJXxE`TY$CKjf>FPYDt*MV7}Qb~=~2#w7HM z&TYE4>2Jiw;v@#Wo<1ONxVa-== z1=o;AU?hFWMLKghuTH-#Fe$^f(Wh@xeu(cjkglplmTXxJ$P_w8GgXj5unZ>p$>Yqj z1RE`#-?rlljlCgK4h*#*r8v1KWOx>h3k(+2IZS<%kr7+pR2Y`AA7P~l`<8T&9vCG9 zLK(K8yU6JGuPj&J_6*#-J<-fn=R|2v(e-MHs0$$egmxRnV%Cr5YQ9%qSppZ-sxjdxQWxMvg-r#wGzv2}Ch<>N{|8q2UibbhM^71Hdls&MKt32INc9}*mkRyiX?AwE(U=cF zY#CkPW7sw*tLhz}0*Y}Lcruq9b2wSe z8$KXrkt#eZLe=0O14x}Y1G#2l{p5)hO z2>UEJR6+WUAlqu^0Yk`zBLktr;{=%U3xE}PfiXy#ST*J%&}TbgRQkI?d&_?M&Kg_0 z$fH0gdhANbADuqm|3^KpVB`J=KhHPW51kFZuVx~TRAZEBl440HNwl?aS8Ku!YJKS(J2h@E`CwOngg$~_f(R=o(v$VX`g>yKdya_edtWnT%QA+ts@Ai@A5(Jzvq-*L%%P`@At$7epeVN=v_Lkqp?z-ed%azz(VRDPZU=PC-m{@?Y!CpeC&&Jvep%CEPNqlL&XP z)p230b(lK|k8V-;Jn_RwmX7E)xhG>=UAvqKkkIcMO`cbv*6BzU`lX}a_afP+|9Jb! zWCGkCF-63*les;mG@=EX65|<|?#YM0Luxm}C;E-Ki7h4CK?Qembz%k4(0m^dlon54IOev-3jsrPGsJ(-N!UINGOi`!-Kos?Fa_GO>^k%JeG zYzSUb5#HOG*OCaa$Gs>CQmbo9njiZtE9VrsYblUl9k~1T{b`^VK7J*GM$^AG z)!21wD67ctn<{(FZdL`q`o5pOQo&X4`lu_PEc9x~X_qSMFl1R+2G{HiAAU6Q$Q|?p~m$o z(YU59bnG%v{1U^4?w8~+Z?^2?E#}x6sY{P)mKFU{6TEVpVP)TUtIcf73|m|LF#Rsx z+>FVAgRET>3Kf-$u5S-gcynjh_1~~rFL^Zpt#C5ZWH^qc^a`Sr#&{1Q{-OQ|yGuMv z$7_E9-pgx>tNO9C%)YZ+_{>*{b0KEyQXdJ!3M^uqQUCI{M*T-+E5)qGIi7yU;vs-K zAMs&DGgn8c4m1YfT zLx8srGshTYba28_gcu~45*!%x`gt@+LvEMm4QAr1KGFqcLY4P7@}Pl-Dl%kdr#2yk z@gMjJ_f4^C4`o_yCjR50;H_vm|BjBU@u7xd|o1=XCDu{b4aF2DtH06if7^n=fA{@1qf zAJG;BOW>T#dD9F4sr=~BjzvVqiQHGzzw}3>{}amL zozN10fr>D1oPkWS4C-0g1vcvO@{8eb8)#zP6ZpE%c52CY-So4v(fQ>H|Jg(v`W^0R2E?=~X{PiOMDyaCgP#h%!4H z5muF*H{!&-9%ZM&A@d%PFNNA0AtNQEQsWjOF>-^K}KK!o)*muxIEOsB9$ zwXXP6fI<#rTYQdtv?XiDrRIVM+VlNlrMb{7upW`eXc}}<^~r+18E~OM7A(RoB8A+8 zD#mV&4!mBE!tNaE6C``NHhrWq_A&6l{`yn{6JI|b2EJ|hcA9LuTPst>`Zo)Z;Afe& z0ER}5Y;!{I<@Eltsz9GIGM{&o|CkC|&|Cd@fDg!SH)FOt`@3^=)*t(CRVP#I-qaSv z41g0Zono=+6GSk5;GhZw*m0cNVIuF$)j2d=HE{3g-7G2M2V~wG;!gVs0VE@;zK)<% zAeDYYGYy`#PJUlqMv;kM!V^{>N+~+7A&}vo^~{&oTr>jdEW<&@l>nEbi&e-M4fVPf zYIAi8Cc@GZ@CQR~FMb&)<`D7vU2us(Y-Bs}V63|}{9aVH4kkQ{$-73~PQ)|vanWNc z`1*5mNLQ1*PK|r4#p`$6SC1P}9`ScVeFp2~P4Jg8RZ1pi#Bi2DUsT{@Qbs_&bp`zf z@j$IhC_xp2*<>-H`1*BgJK`DO`-62L5dukVYKQZ3ozPV2{+=HDgM3y#Z(AhuS?2R$ zfSJsycP&KIG1WWwIe|d zICRtKHl19{b(#tg@JC}Vpq#2N^xc~{ch=g&Phf+~GfW%D*>06Ry6I16tVpKyxcU`u z`+5e>g^p(=pE%n-1a9&BvP8Q8$HdhOjag%b-&=e9U1(SaMdhmKnw~0KE-QFu=Ex?E+OLl%i!X+BNH7Gsbu>nJpz#obt)IF00^0UeDmEW?cDpnPzTmi4B<7MO%0*w1ju z>8;MI80}{>Nf@ivBu-t?T_zgU3#h{N^=jYIuh2$<=R9g zV;d$*jy$;x+^@oRyTsCuKNHa75BZV5dPCvA(}-I$3wLbo*Z<~UY^$7ER3158IdE3P zOMGpztu?Jy>9S}>$@L)f&aeUcdNDUU@Kz%<%O(K6F5Z`Qo!ljYd+LkXY6AD; zp62f5#hrv{jw1 zko*L3j{sFGNGT7v4gszRz7EnK{f_Ol-j7qos{e2$6r2J9H0L^ z!%{wrqVJqaM$`IIfs0~X5Wf`HR;i-FKv!zL?*c+V;apHX8(kj0d2ma0X15|Ti#U)> zJKI+lTEG0h3e#cP8zv)1UN^3{v>!?Be;~gCTovA)q4%T20apiDROIa1AI$&pZ`nqh zC#7KUZ<45x2dm<_5-#21?GI$vI?ahnuhJp91Fmk6@EDb_`sgWw=-iSe0I;ISa!8B1 z;xz+c>HeeuEj>o#J-*hH5{`)h>Fc1klXu%0a;^tC=ei49>3&;fVe|s%o=E_TAy9n- zr~Bk@Y+80YUw7Y58lGo09Dswu{X)8b9zPb#7@sYl5RnC{fRmEShFG`(V_#P<(TB5(^f_%qKJ{L$)roz0OiMZ~(X7@e#A0IbIJh}PK(=`r8=N3%^T zE>CB+_N%1~(frQ7CW*e2NW56}&2h&-uM0k+eya&6Var)mhVBBk;Svu>^|XC@=hP!d z?nRZ4z}0+=n}p^ekww&L9*q`mtWZoZ46geSvusRnqhKhjLh?ixs3PL6WBUC+pIfE2 zt0vZM7*{{y{qYq@|E)ohMcrhahv>SyYAR(O@i>0gRYbwRGydRc%ZaF6v189Hgd1{v zj_4x4>BOwwJ#YY^Ys@M`F#i$qPyTc33!nZK^*c){z^hpfKBrFRYqk)akU!Tq&j9t3 zn@PlrVQr$J){2bl{crWO3*O*wPE>ZSFeTbm%la=OSB{R|;*nzyzw>Of7IAm@>1X7i z0ps2y4vD8;A9n0Xg=VLSj|sy$G`=)6S;0CN`f|&65zC!>lWw%9;mw@wx zw<39r#A1wj72%(Ng@&p>0mP`2d z=>v06MDg(Bk#)lvlFjB9`9sG>h0UhCTKZuz$b5+Q5$py$Gq!~ibuV;YRJ z&>ZYLqyUO&?PY{=B$9zMg%QtW<;E8;la_!ESzD5?-ua;$@4IcJjqf$7O?-KF^G7b1 zcmmXZ7Ym5+(d>Npwu>uFmE7Q)Re(As2PNqwgU&l{X)md)Zc~0l{eO{M+8YPxVEC|At-qudoA`Q~+1? zeMz_Ajt%gEUG|9+c8&!v17;9A>&Bcyi6Y(9$}ctu<-W&>(Mo^oO^~4G7-na~8DN?97^?yM;RjD7j5X?6%|K@R)7M=OndBBnX zXr8O()AsxNTPLHQ1$-xk3LtQxgi9^{5=){O-J3Ec(y%glqoq*kNSwTW(b9W?Mel<@ZgK-e1 zr;xEUrJxd?4ga;)^J4PPbVWvdJeBBo>p|LRCGuH#({ zyq}n03I}oVvL&D$?TkuvS|)$kxp9d zOH3z?#)bQ6ua%}lH0VXNxzsYerk8PhDy6!wJa8*=_i>>r&ux+`XJ17rZyiBS!jsV zlA%3|N=+N*}P8cYH_;^W88X*l^&`p%EaSRF@+A;)43{n-Op9 z=q~2Gwd0ER;2eHQi*8di>pK%AnZ9BCZ$SSet%~Vm11bi96EER1Yu}_^ADTs{23~Qj zlrLVC%Y_`(vJ(Mx%n|p-ox`U%-3TdUGXUTWmoM66_ z-!Sg{x{dJ;B}n2u^QSqSFAXu3==zPW9|uF9&_TBdu|}Irq|Tp;nxXzmQin3b~TN@DbFI{7%UwAA5nzb=2nK$dILRjVDRFoYc6*Q_DSzZ#B zYSka0mZM8`5bnW%^|ijv88iJlwC#%@FG2V^8djZo_YKke{1|ml!a=4vp1vI-B**)^ zz)F+#C6auTU%RGK_?xcrW8eub1$3@PCWYaE(#L-7EWknmPq{j~FMLFAsdmQz@bdof zfh9fF1NBqhB7ctk_{3QwRI!6+n2%|t)d#|Dh|yV9nZGY0(sD9jNjh#DBjchQyix{D zl=L~B3K&MynBGg~!esUM9k&^@VJqA(KmD;Ua8P}@P8%_M*UHvwo1m+-4P)PA9 zqipY|F9nPwK+8Zmv{i4FW0C*FrAa|q@B5lehV7Vr5xWp0+@SuJ%iUEb$X2jK$c@)?~qQ_hvN#`k=gqpnpb}O$fmXqSQQ`hR;ub6}oPwE$PWq~r{Gbu~A zFb3&h9yi+nss%qekp}riDT&rk-s=~8_*)Y~AR$HK4(yVOoFLh9XxMtc8i6i(2r{UC z(4q=uKd9;KO{Wo8iDSw28IDjrBQ=K^ z&KjlZJB#Qcc**9SW6Uh*VbI!<{e8E9UBhpi$juK*0bYusxUlJ>LFFC&RY4U#x;&l5 zNa~^C4H{FH&sKq6X|b4v;Nz_uSzyRwwy02~l*{LWhBl^8pAp9BpEW!T0^`dS;irL2 zzUJGTwAXW_0Yw|6ow=0ns@LiEjwP&ss)WBh>;@ykFizxsr9S214bqRk-L>kjJ7+|m zXZdy=Wyhk=QHm{{*!I>fmXu+(`A0PK-Tv2iBBoFko2hT@03p zcYZ{MBIk@urIOwpgIF05bTh4-(ekg8pGWqe{ zsk=HRgCjp;Y;(Sa<>T#y2iJ~_q7#q}4l03_a+!)6;+?*s&+9zwiNyOBfI^FSo%Z-_ z&X25HKrqD5dMnr%s^-VgdBU$D0t)m;JPTcWUbZ7`E3uW$&Y^-=zmA;V!Y0b=b_sJW zy8B?cn42GiSrn$ky9&YVXKj_ua9%xeGIHSRZhWU#k5XOSa35%9B|DeeJl49v|RTk5paf4~f(;@Ju zr)&~cKQ@@G^g%T5Fxg7Xx*f=XPtTu_SAHkV=R;Kh2{Jdj>MkJX>#kR+XPfbXtihXv zX6_-HZ7v5OKz55+<&Mno-`1D}mj4p6+FaNgx`VnCNw80Y?RuclH<$Zzh+s0qIa}(Q z=*KuXF_a0>{#kloInl$XIxuwg z_phS`L&^bA$;zE(ttV>Y>)7FV0>)jA5f zTM#6FmxC(8f_NW0;|Pijl6cnlEP9=zL>_a7(xG zmC>Czlv<2mEpoQ|!cIMAN?1JUU*?YpGkw>==UqJe83P;lBRHzfw7_`;o)CO{`y{`b zX-)SBq7F0Cn92K^{YUe>_7r)!%kZRda}R89?UmPu>Q++O0DG(0H3Op8oRS9F6m~{(xu=NMC1!R*=*XkUVH1NZ&N8 z+3)a$PE!CRUK&8%L~~g?V1W+ue*xaLc)-<_V6&<*9z=0sOesB|f*x^V#)+vZ z$NK|;sJnU_{;+Oo?d|{J@nS>C^cf%WsMNMI^`@=ky}I9w$~Pp~Fzsd+TP5fB(fmCw zBYpDUc=G)fPlC==pzj>ENZvtic8J3G>^X!DW$y>u!>o!XC|~F9+H7(lg455w;^NPY zz2@k|%*4Gb`OOa5rOg0#oIbgv4nC3%{V_S))}u)H60=E}>!%n5|Y)o zWsw-AC7w0660{lDI(6GJV|JiBxiv%6ehq>N<72rCr3h*%oxB{DYwO z4-wcr?{XS=)!V7@Jq+<_`0;XW7K-$G228!n?)*^0uES>Y=o}fZl~xnvk7hmOQ}-gP zoRqV<_9ZO_h!d0&#(UiH&G%bjk7Dw)!P6N79?y!gcC?%bPr zCe!T+Tauq}(5r8mtQ0c-sdn;Y*3XYq9T5Sj>3!6>+Ai?;T-Ecp;sWt+aN^70uzeoK z2#z0K}6XbA-T+5jHPfAIy8K|8p^gv}dsl~afiovnW)F42F< zBzFmc)q-JcDx56~7$m1#)JGqmuW(yl~EPXR+<4a$z|`S?dzTQJ$bBQrQNr zMY9JxKk&Zln-v%E_K_C}*|pHg%1V~-h7Z2|HpGNcXqsi$mjP~0{=TGJlB>=AWYf}m zHCnvh7IpIBqWTEZdkQauMu^0%*2m(#$9Zvaun|YVNJ)~=FjMyDHFx?}g61IXX8#bT z_fC*4w+7~x0_Gm|Q!kk{kVz!N>thg`P0#?LopRizafDPN?b@JV6(}Td2E!+Xdo1ba za@e%5^||I1RFJ7`BlF7qP?nP5*gta0SDb*;HdS12c8*;yjiTAS&A?OG zfYb(ga?*XrZp!sT18r`)lfB6CexE|iIjILECOJ=;$>NwN&hV#pD0CvY`hfXRe|9;5 z(`K$Xhnt;|!h+fy4OJN;Uw%r#bf9F7=q0nX^z~w2UE3#T*;AEVNt`QTO}CeC&ykIR zlc%u5h>NBHESbEf{3ku|QNonnwF~k>fPvb2yX{!$yZYoWqy)b+P+Sxo=0 zJ4bf)Uo9%`|6|Xjzg{8yOW|+huQuajTuAiKfW0bs+K5fe#cqE zkePGN$rbo43>q3VPzb#bxWmJ8CF()}S-}cbzZ#^HkUvH1`Xd9YBjqy8Qw>YRA9Yde z?R}y$Unq9#BSDa^y6Hd($u$}G94I?$I2E7sA7aARK2xv=tU_IC+U@{_sV~FL_==KZ zg07jg2q|eD9_MXeFpZ=THdZ5KXByvFu1be=D%msH5G_pjKMz7rWSFoD>_oW?TG4rX zq`vb{+A*$vRkA)W6Dy=X&R8qOm~9fd_(}*%}_(o9VR*~Rfs`N?TE?Lhy8PRrT%kw3$S-L;LegT zQ2JVM%@uz7RC+^#zti=S5iKd#dR!m3GryxT;;!K^D$`wbtfV$=LRdkzbC27Rl=WRW zDle`i;X_efu8s`e3sQW~;x_)m9>uX{_OSiW%Fc#9_laNF(btRmeJiXsLJ7+ao(-~T zx_90qi2q8(!V%c^(f>_=WqFxR?@1n#r;s?f{#8(OdR1dBPD*&{LMT?N|HZQID?1}9 zrZiMPt0D1(o3 zbk>uvL_vefAug_|3S70?f~sz@5(hc2Ihs+^^`2i8&`Nb} zT&L*PPZUi%-`5P>I_xONKM*<&+ex_bsqz?{ZAh?oPyaxgP~MLU@Fbg1j@aG@RU5@7 z4*nC5uwp8`P79UP z)>btK)X{`yE3x4Ov+P=mBij}Yj_n)nt3a6BE|0JLFv`zf@oT_+^v3mcQ~iIsF$Qgf zT==vu`60S6!DuM9pUe-uQpa`?!T-TV#{YwjsGt8IHnIUco9;^z6TbRZ*deosiI--a z>nAi0E%;Fvj?xLmwP77oSjYmX^Stpjkr%J2w%6dMC9EH^^==rq(i~=l)maoSyGL<7 zOJ?+T$#sLX1Qrcb`)v4rN8{<*reyzt28Utr(3AIEoQIYY+U3fe&RRx=?8d)gKI%_9 z$#xTbn*z<8uwry!ZH`9I@gGfA-;ylaT ziZ|B0rm-s9$p{d{PCWiTcw93wZM&=bIB;WnG|u6j^FJ(rZ7qCKT&Jp3z8%nlA`)K- zr8|jkXzsjKz&>iy4O{SUj)I(e2;pL4njM~y_T`-pGUlU|_)hYU!$|BK*9dU4k2TWz z&nq|B|39xDxaePb*cAZ3vt*Agja|^EDBnp>c>KWHr;#A-FU0u_soMr&hq;TqHX_UO zGjms)7McofQ@{6Fzr$-1iEG2#a0HlO5)8n(s^q z7ueiKP61ODOw`u|LPJdHm4NK`<4}!G&eKV?I1w5810URpHmA8Xs}yH6T|3Pwx#@vv z$q7aE$ah2GT^_9xw5{k-g{C-z;xZ*1*?ahDm6}MmlaTdRlZT;SxSWSeEle$C|H#o6HxCJOEa(aoo)JL=gn-vT-S^Z{_ zt4)0z9l=DigXdvM`gB&J*JU2tqGiCn2=Hu`B=~8&350*X9C;=?+w{}B*;9^ss|+2; z=@j~XrsTOo7dI;=7VE)xkx*lLQXrNAK4`L?jtoZ%yRRz&PlPG5TbU`=VbjnXfKU!(GX3`i@3CJpTzspq|inaagYlh)F)hX505RBIeZ|T2=pQne=s0VM49TY-Vg&(aZ3eYZxRG6)`Aa)pL{04ttaF4iRu=@0U0ytp_V>Z$9tM1lm*@vSC7gHnnj~P-W-gjrbsxi-~++!Uu#(7w~s^Lf$=7Do2 z1vCYWwU1!bgyQnhm+YayA)akqFKiVMyeH%IxXW4*|bL9hgN$ z9L0=zNn30ajSMHBNr}$2rg<6G?58gbKe)4lop`X#3xEOk{ulTUBx2D*fs+s-1^yWnw0`fc;NN}(>he6 zaQ2a>LzP0e+{Vxm*OpDr`N&SFrXe63wPYol<4gH6knr~8hwx4w=i2X)2yLNWS6QfU z@G-6HBIdpduD>G+SH{SQC-?!(;0pJi<*zcA6<21fB%3JNn4X)3K~~GziVwTH(u~;e z7J)F4E%mJ8NbcIr=q*Qud3t4OVL+#Cx}$CT&W>H^CNauyZ@*Lx(C%sC9MK!mbR7pg zm}vsZ-Z^tlyF>(d>+O1wPrXA}%RYn{Et+MMX^=yQmO6%NOu|U|LXJ4SHZI=KNVT4E z$Xt?`zY)E7BlbS#g;l1f-#T9h`3}PqfPaFLg?8||J}gO2c8P-GP5MW~!PCJMaff$Q zCMT+=iD){SH$(0E(=|`gfq|7T=kx|(o9;N)4_xCnVskX(bJi^*c!fs$!0(Z84*TB? zS(FQ;0v)iO1=;ruEzYgh=jRfcxP-BNM1j|fM=0nrcgKq0*#h!N$MJN5?SNs%Ny{ngLp-nR|rBnZ>ijOO`&;AT$=U@a`Dt~Lzs$-pErbHd>m`hXGNk2i+B>CANn*X zmo&YJ3G^++*)gQiw8fgnk@@pgU+|b{#3^H@ry(wB260^F55vlUZu22XAyt^C6W-q4 z#C`bZ;-Q=D*{hI0=TJvBPZKjeef1XFx)Bif{b9niQTPv-i`9)(Z&vdOGp{d z304-*ZdEO44+MLf5KWvnjnwnAPffQ*>`GZwpvDLYGJEZekVrBHlk1W5i+;s>+D{Ox zz6~y;r~PlAk*v5^crE&)Z?0K_PTmg>5Vg`d?Jlze%X%*CbLW&I{gyC9cY4%-DT|}v z>Dtc4y)ff=+imu?sL$d z3~No=|A%S!9}ac@|LN&LU6w`L1%J&OBr%@;dZ^8kshH8e@`5qn?zsA;hA0oj)!#PM1~LjXSl;W6F~ zrBW}OO`wfQST zk{)48ehMkrcdC>=Nu*x8+}KwLU{3d;os(9;*+1vySJIlr8{~*p?$;|FApNhVe?=Z< z@$4j!NII-F@?0;?pJ&HLH>KKTp&ISO%u`m84u&r0q+c~1I)h92$E=H57~Vk=-dqgA z#=aF=a`XDsm^rt`xAG$3{D4_!7pc@LzP1l6j)9kknkq}WcATnVAFy~spfHH zKuP;_^MH#o9X|aIoPPC(GwqtrJ%%2`LbyS{Hy#_l?L#&(Rs^Wk8P>z9RvwMF?C%t& zjvd~dGZyio|29~d&E2EfCK)=nxYtm4WH(K1mcXo=MXXB=3-JG`23m*;OtSD86EpNRM|U`}O;QoWQr&!?~IK zEiWBH3(vsXN(3RfS!aBbJ}lB9g-Q2jFbi%Z`gBa0^@8CZ;h6{6gJI7=Fj@ zuqt69knvtZ4r+|*&ao{`+%$=>VXspO)=;=A%Aj4(d6WNinxjxms*BgIjPGh!;4NeE zWk#`f!qh_3W)-Wf*&tWe5GXcP`iYha)gm#xTj);r6A{Q#B0=!0V=;OXu()mO;iWMq z4w-4(>+_!5Q(e0Dj`{Ys&|w;M$8LJ1)ljpa`$|?zpAnZ}^GstO^*4P2cGJ|0+K*gW(n=S4C}RWj zPX8)2HO-0}xDpI|rhHh0?75``TDUU#J&5@uzrd*45mXu(C7>0rJw-qILLm>E0iT~1 zKD;^MEwCh@Vn3y?@ha=Q&1dk^V&E$)*5lypN%P_NRHq+L6V8foiCd>Bwr4d96@sl< z8W(ScA4#K9pbJS*nZEUBlfl1imH9)3EBcK#UBi}Usqx79?85-Y-F8Ot%0VCYBC1OH z2ZjfB_>M#9sk;N6z17b^{mjp^LBUpR2x%-tq^3h;uikNP6j)p7k(MsWnLuf5AC?T^ zQ1bNnk=jd4wcIo|gYP4vpc3|Jo5)q|P;;xX+%00(IR z;=ds_PA3dDPKVH&!gHv?5D1Ojp0Xh#6X@*$mm(m+O`^z7pv=^;yZy+)`yU&F1%%5_ zW{P3V*NqAzziZC6ulZU7+@U@I|Cr$PDNA*;EG};^*}lv)@sN#lg^rQQe1=G6ytTX5 z(cQ8f1I*G+r-&cVuq&k`>iskw05#aF3IXFRxySM;N@{iBC%E7wHAaSu4jZkepH z*S7_Md@WW>O4vaCfiH&^MpB+|Ua4EA&C3~dTc%fWq;dZiZE~YzL9=A7nvPSSVFr;Mn=!eqC{>->|I9&zyTJGU8TGI(h7?syIUys4vGJdL}&?`L}P5nG# zge!E+@#EtL`f0;RUDKQUOM(bMUI=3KLz6=Ec7^`wjqE&@V$u}m}g2)v{QY0YQ zgTwmHktP+QxXh z;!2~#W@~-f@}BeWhTBy=C*hPht}(QJU_sVq6)tC-xtX~uNe3)?Y~r^WR0C{fws)Qw zxYGV^U3ozP#HPIHOZR^QmQGZ3hm0qM+^tZ=LDrn6D;>!6Gp_$rl;1+swOBu ze0xR}*@!qtE>l$DPEU2*>Cwd!$wN&<&nDTgtO9}X!nmh;zMmjBb>d#9G#y#C*xamRFd{PsElLY`i+O->gdA%RWF5KQ-J~cKS zx3f*PV{|%|5BbBwqRYH(gK(b0-f3mW3x&E5CLewhax+eRqIlGVGL6NC!^Y|OtY72v zuH2zDMK1X4I)?9_Agu;I_+O`uH6S}-F1mnvrLP=ZOf!-p742OqYb{OC4zDmpO@uTq%~$RpjJ0qi9{j|2@rO`rJqRLxh3GECdTFCG-QS`y;1BHIKuYA- z_{`M$9Loa|K?U~fje`yro4C>zCK>;e+7G!`Ud6!9a=sf%_KY#9*9;f>1LUr@XK7|S z_KG__+oV92^&!OJyFQqT!E?Dy1p%Cv;F8u%OQ4UymTw7p)yef2Cr&{4o0oaI<2-+* zTc{V=Fv`ZQ!Y%m(tbNHpZtI9{%1vb+j~_9dOdl^9*A}=Cy(fa~Uwg;*OIIeT7BGI4 z2*rhRBn<>p_m`%T?b!aBG(Hh@u;DIBLd@2x+w{io4DkCIX@&e1j}TlHr}1^Xt7i~L zX0H5UM*t^cWz6F29sN)?XN&fsb6MW7qP#v}cG;C%1E+ZwxJy*82j<=-&h~_Mak6`# zsRsC6e!~PsU0D?vrp}^Sm&Lk{+>aANc+=Jfo!p=x5{uAZ_8lt^gMSM@ck;Jng;0-gyORp|q*%eQ2 zH+PZK#aHE*-T))aTfBQUSx#O0RxebkJ8o8*h+nQE3>&%KWnJ)-#3d3`{Z6PyzdNTGqE0K zoAbhC=1R9yk6!?d_ukd5SJ{g`o#m1BrSY%PEAj|k-}1xy!{9PB?y-i9KEPnM{nD49 zTHv%9jERngoh4#L3u9M*S;f2wAbDKRPB)m{#Jp-UXfRH-ar@Iw^ePNtvn%gW4*td7 zf0%H__yK2V?-+A!HD#lU2OJs2Y_kQU8?anZ1Z%;s8hJrPHJ6*Z36CNlclPbi@s9gR zi!@BFF_ltpik**FO_eT^U`&niw;*jZZj*E$w>!?0%C5vw|~jr*2nhvS#o3MgAe zg=e=Tv4iqC`9Tq-Xwy-y*Lco$clpBk9U&cKiPh^=cM{3Y+eM%n?R-un&iakETfdF} z`^+&uPpciuS857RR^Hh=F3^~O@aWVBNCQ-uJUh&_@c{F+xW8;dRhrG*ODdxeCGqs) zxo+wWiL)v~_MRzJ-yV16Y*#?B$>>>GB2x>w!@u7fa~4kU(uM%Bkg0}IjSu?&AB&KW z|FD?AJIleVqi7~d9^eE=qcK~jJyzeIpikJQ58j0uSyR@dp6)k(p;O@&Yo8k#6bJ@7 z%DkQoyXyY7o$NX!E209lus3gSeMd)1hBvCL35sN>u1m<6X1K|4+eb2incqsNoe%X; zw?hIvbYa6vfaYq-fcva4!*>K?Bb8FNBEZ~~h%@fBEsyNU8ryOXJ0KaQ3irxaOjaAE z(=oGgIEnsIbD^x(;o*lVM6JM3HdgJBAdrDdi=1gXaWWa?DBZN|J4mdNYbhC(637Fc zRIZuTc+vOrU2Qn_>u0Ep#T$0f!hZ7^%1Pz6|8AOX%g)EB@IZD(%E4&Dq0{-1Ve7u^ z(J&X^7t++%=l6%#f)i5aO>+v>Ut+9HWY6o#oNkUzOY}>#X5Q6o4K1-ue|JI_u&1Y! zT9xsH)@U={GV0*EE0Y8OM9}qSvG)Ty_J-QNcP)tFYN%!Wzu?2sD%H`wvxRLKHtRL0 z;f7sx?~C6}SKS*I$hZ*Ui8T{^IA(Y0co^?Q60x{dz(|}zANKigo>K9i+7Me;IMF2Wm=M7>O+aN(N0ixRf4WP+#iBoTtwCp|xkX9W2$qD&~H) zw=)gD1O|$2yYS`=;6U3yP9mWl$<^0uuG(|}!ezqT^!DsWFY5=c=Dq$h0dO=E&y5yj z&S$d zQ;*bWvh!5i((T-az@aV06T{vM7)Vt7nPe%-C^X^I}H{Wlyss5lbz?#Cj$(N^{ z#_U6muklOdi`&-1DuyW-DcQBgMGR>=!yr|I@9GQgxb@%A2$3WotBV!sSEU2>L% zqqEtJTCwM9R(S(%MO*ICAE!0DohYqGTr;+rSNN=H zR&3lRAp|HQO&)d{4^W7{Wj}vpoZJ3DCCi&`4J5pIW*)h>sstq!)TpF%7=j-;jA{_r zGsRArV)5I92oKu$q=vwFhz#O1*ZOZGyzvj<-|uE3uiNSY8E|w3KnP7a3|&>WitSQA zAkV{v_cYVnxGsWL_}xz$K@R}76S9_I8h403QJ2ctAr&Z)wB#(rg|!^E~iX6rP<1b#?#g~(eG5T@xSjA zo0>oyjF<4uBtbWu`{u-2?b{kRPjuYmlB}}~&H+47XW=g!GdHKjk-Hd94!h@sLrOHy zct7sI9SL!Va#^_V$a8*r9k~*jFm&Z6rl6yWeJ$Y6~OiI)YI6`pV^iz=t`cl#!HAXn@`&5 zfc#JN5fv4PJ0%fTeaJ6+RBVG``Wwr875-OjK*ETAgNlwEi8cAZzV9lzyfyMAtB zUC7)H8#gbso*5@2{i2Gp#@rO|=z1ij99Tp(7ryomM^;E7t6~Nw_sk335_-%{vbuTk z^C#UofU!5L91B!gSpRP=SEHi{Cf{1|+*CS&ADRt7~zSAN^E;YonE(UreuG#lr5Y z$(H0g=#T69E8QR>t4}KFQ1@nMcp$L?2{lJT11GHte;gk#DRHxJf9;QZA<}w7RCq2o zK$qE>#hxys5-sf(z*ajI;p%SIinE+;v5xKtib<417sh!^9lCk)JPeATsE9yzv$KTi z7I?%;y%@PCvf}Iv^wUK+sFN;XGmc$OioErg`>1^OImWc&sF@M{r>+ot1QWJ{-Z&ng zrux%!7>yc}RB>cfQ>dE2oQRFPrL(f&6;h9APc{3}YP%|@RY=$>%O`G4bg-3d&Iij4i(OSB zuxZNBI4;Cex`Jwk`bqR-oKrrYQMI%VMsxWl;^%VvLFyEk`g&5Tyw_w`OV{q6;rB67 z@AP9O7SDJ;cjx$rmk9dE$NSDIDr`A_``BL6SR zylgnturbvDWyJE()%or;DP!jRC=RFOG-4uc!7j&lQ*1r(&4UX2D^q_W^EHi=xF!dE zBV;3lo}p^vO?6z`bq&^Fi?@lD|#2x_S-{DDV<3=~$5K3;7`wImQpu z@VZV6#arv7+)kl5=MqjfJReF&_n)Y@-H95He5bn?B%x_w{ap^gQea>?kFw*B+&3tJ z)Vii{o!V9&$Mr9c$e*cYeAUHP%yP7^99wj*zcREcYfiz=;Nz)O-1i-#$_FI`3NxZm z)A`R{fY)+0d(%<*DZ#V;zY(+R7hhXXv6(yN=!L!{f7UTvlUtZ}dH5DxK2t$ipJmD^ zSm$y9Np-{9T(|qec4_;*c_4Cf@?%?-@(e=&^}3yL52=vv#27~EvM#Om@C|&|km97N zrLZ!uMmOL1K-#*ZH1(8uM{{=Ys0zS^-ueOUx`|os$J8|F213O^&(qwxp0w<~+3P_c z$xS~buoJy&eBji7ReJXy7YwBTPIdotu<=03IRH9vCP-roe`KP*#gB?NI@~t$n@Uj% zGhl5e0b9X(ohr;i3cabm=Uy>N6g9o97(G5<%#n7Z?T2G(>J%mRym6iR_GT6@8Gc*#yxZ5vW+u3f@Ox_g?B z)r^FuOzf;(o|@k=H`|SYKNP`3xLia-(clSTc}z4AMs=4vZDbeWFB+!`t~+d3egCD08r-GHJ%H2g zE8?txTxf|fCd`DBXkhjgK?+K&CZ_`HlB+ie+R*4eu0m&(S-4}8q2VV1!)5zJu-#ys zUrIS;C~xeA1?U)1x$ZW#xgY@*D)ryT&qW7<5J5FQ#i3XKDm^qLUvK|g>|Hjc87=gP z(~DSPbLycS{%xb@neS=a*IOS1!~&Qrr+wmprYO|8hePLiw4^bi)7ex9IoBP+3(RCm5ca&Ht(L*%_n>AY(1s(QSp zb+(9c#VeuBr#Bo!1|9%9mXJm`#GOeWl;L7$>g*w`taG@p*8I=N75R zD@itVI@EITd-UMCaJM`iUKi96ts&U}xCRQ0)YyOJ+| zci37Ut947iNVgy?NQ@asziT5Cs8~Z|nbixeOajGlsa-jjCzQYxD*NT6mq$W#rXTjH zsb_0E{C_@&>=u;487Kj?H?aO6F2bq%e(tZd_L-jeKjsgjv;$7IR%Z`|PkQj!A@qLy zUP|aMqlk`kX;&&+_$#N_ADMHfi(bqXQSjo4liDp-8+fh^Y-OgTR$udF;Y8=RJTp3JcT*n*@RI_ju6WIewL=ajn7yWGPP?~{DQ&q=jc&`Yp^A_BuOsxT`|C~b!kQB} z2cfZ=FAey)ym5|agHdL;kMrPXY-BxY2T6Q=*plB%!5@$BKP0MA4J~;@-USuh%Qea* znan937KepB$)w{UDeVHg{7mH^<|J7vP(|I8!|Lt&j>)Od?y0k2*=Fh@<>vGV(#?u0|;JG=t}^Nn9JhpS}SZ@v&2nA&tb>i7&D zbD?755l=$G_fuL^M}HspE{1n$YzY5+9X(`QsP1znSC} z04=E00B8mxsWgZ@Mc4%&MaaO<&;E`HMJ(m;gTTmE7W?Sxz_;9%C{x&~y!Od3Op}34 zG9FOCQN=7U@hswsrca9_tUN*L1#CaTa`4O*q67KI4Gs-6XrO(**@;1Ka?;@w?StAW z#E6~yQusRE-divp28%q@$XiW1)&Y0JiT-(h49;qNb=t5E3GD2;dZ6nii# zmp#>e$4UU4_%lJFc@z<0inRB%>%U36xPJDaLhs8xtDC*q9WHrgUl70D^NMF|R1|7h zNb@1LP3)g3P1(SdD$7T5r>OogMLu( z?)-3ADILk1Thl#ck~Ao7*z_rXV9D;{lRp)&tAq+{`42eFhcP`@-|cN#D=0f&2b?s9 zumA0PJ^qJDeqNXy-8lxFoI#+QH=3!KEjRx)JE3^L=GEEc&Fh*jPK=Wf;<-7LWUDzbu|PSY>$!83%=R;m)eCbn$m?$X-XFFzWCyrA#5&3&LM@k6jGZ=x6;QnYvHvs;r9+LqFx*2W21~F>=9yY%K=t_CjT$xy;9TSe zJqGtukm?`g8xv>8P-Cg-<&272BfeM#Yi_Jz6HQGgpG1b%S8;;w^*rFv*gPILG}Q&r z?j+s(-z* z?EYKXKfboFdeGH1s#k#DDi&f`b|WZ4<3gw8EBB!t=?YvI6xU(9`Otd2ngU^T7)PDh zy?%D1G2gNA_6Fjl+){cpOhWg3f}i$y6$RzL0g{l|Q&2`BxjWb{g1qzc>VT_8+uW$j*DG|!TV0C*X zuRpWeIhGq)spdTWUr!28wzG^TU#-DsLrrf!)lxoet6$b5>>m#}t_#~7=93g74@f#| z-btgXu9xLAn`Z#6+~Xc{U0FesJ#m}k_FA`JmxP^R1bw(#v>2#VAA|bEG9q1sz6w$N z(L@T))JVjvv4am2@G}k=967h|Q9HA#SJ8oL+|L&IuJ#+9mmY%gJ9rD+XUE~nPZc)w zL|&1p{Q#n)Nx0=LCiYuPSwSeVl~kf2?2)xeOUs@K(Q+8iO7;L?*3D4y=wqZy*Igiy zomWZTqv>BrLbLwy5U4%=%q(N@qBOgnZ@87AJC| z*1zmZ<51!P4Cjn>p4F>ISR2Hi<#;{KL~P4iZ#09|a?5kSkYnZHO$q{r56baph$JPs zZpDq4J)dZtA{ra_$}0G7*BWWQ&q=1D)1t-qlq;6a!hen;eDu5dJpUkW2RJ_w?USp8 zxCV)rZlzKczNU|v2U_;7y@=W~FU@!G-9a2bzRB|^%k+{IoZ%>PxttO7cI1YA6;gtu zE;`FD+_);suBI!fG5<=uITYwZcAxG7Fpa&P=r8UGy#ElGUb*Shyv-N6bHJI3m@s@* zk2Z9d=B74W_})Ws>2A&~G?a~)7F+;9B9 zczSy9@ZUFWn~V0Sf&RB0;(uK4msKDGYWJOl9odriEQV6KtD zEfwNt$I$E6(N@BI{ir`mh{jsPK~wuakn@Fd|C-gOYkwxiJ&a#BL5o~h_{Fk;L|)vL z1f@-IMtSHSaz?#-y~=&HpxGc%gr3wKU{#G>+z4Z4RNE^u(}$f=V9l7AqtE?yY^bLS zSO8$@DdPAAf#rLk0Z9RthWP+JX8kDvM#FRHU~TovfI9u&bEg^$PC^(XAZjvmZ#| z1lYI8)uj>CWUV*%Rb;TA7fEr_Xwz77Y*E)hA)au_dq_6t7U}izPgLRWMnh8HZ#8MF zxBHPM!Kt5w59fs2{DQl@z37f_(IDyl7m^143!XB6qhm)aVlomv==S<)kW^}H$p;_S zst1iXGgqm}*y;ii*lY53g7k@g=d#FfO$Aux#)g9Sr@NuB@VRD^0jw zs#=lpkwKK-Axfy7l_8Ccg7w)MU7-V$bW{8Y7N@ zeW~53F^XT`Z<47X>-%#b&}MBg?S8&4Ci}a`KgHg+==K4GaUIOA3VU4E4V=agmhno+ z6(PQ3d2qFetp;x#;P=iG7k4?^b4`3&9E=LL#L8~MN5w7+Zh7Cpj5Schq9T~?kjrCu zR$>^%Za$fjYW^`~SZKsIQ`LYh3gqxrU=1|=R(2|XgWbs$LM}XxwbYo2H5X>wHr5TF z&v6)nDLH;OtNBV1mIpHm?YckqS88G6!Lp9ZZ$Tp1r#&KfjyM($PJi~EDx!|#jancF zySY2G&BdcNB_1ss@qg!;u}A+{wfUU6ZWew3G$(QL+Is>OF4GokwTG8wdo+00L9-AT zhZ~HrV10A$yTawCmF^eL3>u2NiT%d7p_`N0ZS&?d2Lk;xVRdT|)(7`9d1~ti+EQ7b z_h|)!G{Wzk7^D}-9t@kpY-^>mE&>5z-`8pnR9s*O;Xou`ol1~sAMY(vmDxcWtG`2T zzxeuD*~&7p*t*|BBb4topIg=(xnJOIKhu3mBp^EsC?S?F-Iyy!rh>2Qy+B44nXJ** zvH6#^XVi0w4J1BceyK5)&!BHCWKz?(LqAodkkesoXPLa^YdS46bNcM`gdwl5j4@3W z6|P|Z1bibZ*wFUal-~c<=_do0sWrHL@KB~@ZWH4l=aW1_9x*=TLIc^C;;bVL+-~SI zil(y*`CMRoGJ5!Qyt+`4J2vU%8g5)!m@WsMh;t|1d*_4?72ehMwxTKAU1bZ`9I4_k zDfo8#b#YX3YuIK@hYuffr3%YC7YNh&QI4Y}ms4rX21>$~pk)4}l#xR4SPdlmcu4&f zEDa_9Wt*rPpLKu`1F?~&eEguao%IumCuNRh{KhxW1D0|-;{=Crm~wo(Mvo}{>GZrEwuR4}u- zLi4J1T7L2MYvS@9qpR-)S)dH`IP4jl zdeXNSKqLubxwhX42ixnG9oz?Kwdy>^2KxjQ!sj0DwUF_Z;V~H!&ig~6-1-G{%#ILW z51RqP9(fzB#XxnQwJh6WdxEf_JcQ$sw%e0vp9dOMt<(?89>QgCfr6vjE@-WdqnLECGg&?i+GRg z(6~dtK7VN2RN!lr@<(Fr>7hq39p-=~u|0xwI??6V7%;fMri5JoZ}cet7d?&dw5c)x zO5RvK9!Dv1gs_(eEXur1^a(^Om}S0lBP(hBp$BWac~L{&E*Ma})R*af!T@$`#6sjKyM_FsTll`WRS-Za!hWT#*s6hDjn#6s)(yT5pVG}8(^m$5 z1Y1ru(>*WDlDWMTtLZO2c#oeAmg%A-r_iaYyRGQaUB1Os>mEgq|{+N8nCAsS820DvrmkipKd=uvB@M@ZtEtDolYl<`)|1RG*uN_BlD8 zzB-!br|&!p@O<8LJP>4GHS}1Q%8>+=THjn^#UJ8;28`f~6bJ5Hqm z$8?~BE&1Kc&9hyfQ-ls9TUL9LdP=(X>Ti!b$vsrTXq>M}bfhTssgC5#EKk*uW~dl$ ztDQQTu0sM<*UxlN!_eby>+WCGdSSbNb528aQ_`=~GsFvH^&Ty*dlFVuv8F}N6E46& zQq*CP_(a1dR~cTiQ~6vEVnLX7*E8lDRow4#+L^@G6lJ2OI|mWu8Lep4lBy3 z?ei{x+ws51Zr5a1Sk074m>hO$?D~+Kl{O?#RbJ|}G7I*as9jde^k7*s(uc-|#ocHX ziWf)(d=lX=@Lu#Q)Hua$50WhtFW% zm5cO0{c9mEk16!A2S+Y|WM7@YNd&Om-40db*8Uv6Xy-*%D|Kt8)dcagqdUc8&Ed=G2?cFJ)04N`>{COXS$eT1zB> zDJ_kKN9U8cxMl@0TdEgnW}~ub?v9Y<6fB9*WdxhMUKh9fYSZ<`@4!DdKhy4U7F16> zj<&r^-jLmFipz%W3iImzsNFb9Ir(hm>a zJ8X#wI@#m~Jzzk%zitc1*>ie2Mz*AiB(R$v$UlkPmZ%{sxp!;i01koQwj9OI@;6tKNC_H&0E2|N``t@D zxvn70{!?svUa}IQsg!=qh9!K0$AXj%Rxy35qihB?Oj6QzKh<=MhP$Jeg+#0&XA-{KmRvCnn>GVb26>vfXm_$@+7*3(l=`U#%u<7L z>fbYEVo(2W9DBSHO_MRw04RTZ1F>XBzx05CcF>&=t;F@zzlJmR4C}bQvr;i~uIi)o z116e=l+3mcuGj;J>tT`ux8N~RxDN$?^isUtZOB?zdtB#$H)0%gv=7<}{elxu+dtQc zZ1bSk9;*9$u8NIbmsV)5Z8j2`QR}(-`(h<+_e}Eb(1TQLhIhv*bKtxOGvfWeV-m6H zV~5`OT(|Mv)CPtJ>a5I|ZF)-b4;SxI`PN;N!QqL}6GNvGWV; zXaM(!J{yO#JaFZZ6RTtWm<_BJ_fm*fB>9YeC>%#vgL+D59$@sr(Z0zY|CK-%s6FE&os zUKgJxsmEUm&m;66ZZvL<^ohIo=utq3c0x=pEGgL>{q5DUKwd5J-5)Ev+-#A&xaG=K13r|>E0EP)k*JVQ}7V*?#Fm0Qu4fn zI^(z>>me^g`+2Q8@clnTO1A6;%zAz7QsIbg6g+;-u{TDpyA`)jIrT^nSKM~wj1_dU z)*)71HFC1oeC>kSYf|-#S9QTCnLd2NI+TE^c5dzi${kNu^Vg~$!m0gSQk5PEeDW#g z&@ws^FGcl+h1aSB2VPo?KT|7G%iZ}U0Z|c$_+VXw!O9MgMpBs63R2E>1GG`oWzO!p z$@4<+*o#~oWk$Vq(W%OsuwCI$jh?h9uzr7v7~k@;pNc`wGx_PvD*T*}9UIG(G}LdN zbj%5T-4(j1@h#|dd8V!aMbq)hR@8#O*j9dG)xS~&PEY&F2)Nf$>WzmB9|15+Uxi|r zHd;8NaSQ7lCCon`R6JE2qZ&tgVO`3XJfjxh5Hw|+YtpciqL!eQM`Di^=9~q81fkU% zWDZ@0wLj<6=iUI8+ZdM)bTzRiQ4N`heNtJ_ zbrWC>c@dfbR%$P~Y=e!sVf0Tgz(T(MSu=Z2duQo2OT*#-;3U$1=xyY{?q29@9#$G= zEQrlo9eQb@TB47~9h6xnLkDdgv>1U{OpscZq-pWD6B}8^N~2pz6N~htzx?oQCp}+c zPLT{B83tj|lvlZ|&H-+Nfr^*V_-I>xd*l8@gf zNX5Ix@ff|`25id{7w*?e9hk3~hh z`dms=!8-gb$2X0&dQ$<1jMJvfFB%>9^vga7E{)Q$JlFTmK_OZ$Yc+{VJJz81sN2T) zAr94+i)=aKgjWLULvX7sIKuS&dl!7b&(=4_8VicSHxy=f=t1Zn9~J4X*?21{k|anr zIKhY+UB4x!y`9@J?W>Rl$%Ay!TF;jtea)YRPNtyNH?4-6-)gs7W@oxP>wCMiXPVGo zE~u9M7RZ^7Dtr#O(-)cNz&aND>{cJYxcPJ0zL3nrqRdc3XM-W^kRY~|rp_O;M3+nW4K=7k*`K{_gp{9@yPn3bwyJuwE3kL@K6MKY@R1|~@H2-RiU_`WjWlpmpNLBgbAwxj&}Tr45`GPT;UPv`D| zZo;A@pH{isD1z|rj+xP$Pz7A0{ZsDi(SG%sGs!*#dM>}(2tEOzdQ)rRQCGJer zh?_lLKYO@)8KU#dBT?=q(`glfxM!pvySBSgmNDRsZI>f@F(@mjL{VX7#ep!{ zeShqBzE}~k%M4QxIvA6+#pS|W|7>iK$`En=5!bsg#+oi@PkX1SZ|LZFmub@OCFD=0 zyJTPv-*1pA4C6b|g;A+37fwHGM9Bur284F9SBOZ`qn<>L0Kd7d^%bcOlu?BwOxp6S za!#T9U(JfvFQMr+hM3Q*=qTHF-s%9DYMat2)&}N6o@|WaHXL^w0>?`m4HVgpbuUF+ zJ%Uy+PkLcG&ErYgD$te&k@a(ByF6;k1VITLuiuz9voWB0EGhtBIm_~hB zu~tl;gqc~p849it!oWF8ZJjfa9!TVZVz0vn(sdq7OKBh?KSYL3K^^DshEgY9!p7o%-jnb}+>HrK4|QvK{VlO1q!k)y-HF{-mMUYZ@Sfcl$ZpRErnP5Xb1l3JuGh&dR4TH+ z`8Ajd{DoQ?NB)3}c3*0T?8fySD%phk-l1cjuWVE8%d1a+i@$0w9W}a*LL4 z9CxL-ihT8^39GQ79fV{36s6j+1$hN;daf5(*hVtd6(+jyZlHP=g?SW8Hf@5C zam^J%n@eGcvz%b=K_4;<*yWWO;$vxly;u7ML&0hVxoL!iR@OP+G|l6m^jg) z35!(THxu11n@$H7wUGqeK;1*~j<1yYym^v7`Knz!ui8kv%m${Lt@ZEcWgSz79OcoP zzq%WCNp3T-nb*18RKW>f1Kw$M-Iq+iTBWbInmy)-&Ofd^ZIjw*mdMfQSEhrq-5jXQ z4l+6{3j6RLd`Ynw%As@aB+Ir-a*f^lJ~fyP?rz>=P;=?KB{tbXH08 zM+~i=J>keiAf;~kznI@@MKC4d1)}_!)HP)44cUInrhETs_%d#7HNg21LJ?!sFxj<0 zD)(}QdA+pT`B0wJ{6#|Or=-|y9)x!Y*D=)0?!_n)8+*F>FRcV0I&W z*%h)-ShZ*!PLdAwas;_I9+kxA;pwToNLH$7}ou5-K>>pVJlJOZ{dDeHt20rMxcvtP@rvw!(c zpSQ~&|6ArrEI|Q?U8UabTc|_VruAZvY}YF|7Ek~5+Em{wC+Nw%n>}{~sf9Z9r7l%L zNwnPN%JN%qvfSyar&?sZV|S>&e(alk#hXL6B@j^s)~}a)RO39(~hJ`A#xg-IWA;&F9J^{;#+2{Q|m*BT#_;3&Fm=})C@I^nDa<-hj z2AgDVm2P*y8XF9G-e^DA6-k{S1N9izj7)kq(EV%y7@m{O^veA-M)f+!^68Wm^0i*8 zrF*#tuP$nV=q8Q&6~`>b(Csr)tK3{7+$SzAidgSeG5A)_K$OXTDp?0cb+ePp%9CL~ zoFyFs%)Qj*yV7RbyhEJX%hYFbQ*V~fHXCV0Y9C!`+8s$m!7YQWR^aCPGyGEiFS-Fh zx4fU!Iw%mbp7@|?BCivqK4f{TsQOABM-UajGaU#cY`&9#vkL_iL(H{leJ0AaJ{G0k zPyRNRx*ju3|HFmePLBF8Q33##p|#ZMPy&-DJ_RE#Z+_tt)4MB8SI)aQo1DWIOWeFn z`MAzD0*UZ5N9Vx%?{?S&y?05vHICb$N-FA^D(A9_nRQRc?k z0i!+qzG&7cq^m=tS}wY-Fh5OQ;U`N(dwBlho2U&fo*I6W^^?Q*e)h`kKU-5{$u9js zH`g0*lyGI0!<^;JE~-%Tr9xty?=D|Vc?K+)>g-P2ok1^MY;$>G*JsgZ3T5(}jhE|b zCV7555mLJTSmlq{xIxyC;Lb*z2W_21nL%T-o9X?P&*!&P|BtD!jEZwznjPGOOK=$^ z0Rn{J?hYZiYj6$j?hXmg;1C>w``{4V-3jgk49?}dXYZ5qkG1AszfE^_bv@PdU|c99 zoTl2)(YLDF55vhJ;Dejj z_>u1(*SX&MO`CrhNfcfrMT@6cQa+nlhJ^5Rr-wfq>{+|ctu@yOrJ7GV;ka*okI&V~ zZhPpWuc(Nb=_qfi3WETp0pkkhSSCCVvMjsWv3BsDtnskS0{LT5e2 zJh|9N_|p;A9@W?MCy~h7eVL}qdBg~w9ShQMO*-wL#Jqd>n^|RwP*BFA2id9`lkW46 z^P~o*Jz85&*EHz9iPAY)6(5S)aI%UVWxMbLKS<<(M86pHXQ_ce*LI4ac8Mcj2aeJL z!aa-RV`=+~iuha`{Ar3#0tyl}FvP>4-H&@FH)O@{N}TI{@NP5HVwgQ!wC$B7`e}jD zWWyi9X7l4lfQpAXoqKE&Iubme#Ee>g%@Ipu`~4%JNb7`@o$9gB>$75@cnu;Z)zYE! z(6_x3_$QD|+57vmo**JRk&X+xTf| zew~%&4DLlA@2y{K9~BIxtP#~7Bw*bM83}+P zz+Ryv+BbT_&`;wtrI^dQvCI?V4%}% zkp)fZ-4(ax)F`a+fVO8tO8`B9^zcl;|9Pv`r*1W$l1a%dwK-&Jsz`-NWw%WzMP2RD zZo*|d0==F>RL+ay7CO8uQhS{W-pTj)?}1HwcIB1k3mfih+G2jM0nI z6d@uVRwVGppF!k9(`jTVB{e9{!XGP{#{=9i5Pid_1%=dHLyN zwk+#GkGTgWaQ0i6)7Twph;_RX(UCaTsK@LyWz=y%C;?VlHrXHqWxCa|s+l%6$XYXw z5-Fg`Y|;9Bi|Cf^9dS7f;t@f|Nh?y$4h=~a+@030CAzkn!Z^OY+r}HnAV)kd=O~j+ zS?sYlZ_VVZGV}cUb)oQos?Mg+Q-pK3MwfPi+<&zm?*Fw#n;MW_ZgBL#4?HRscdlHd>V_zLQ{l{-r#bW8YD6RMy2YD$G)BZz zUPHXO)&mMj`|X{rkX5KKR{)k5JvX;LU zSnD2tx>DN2?>w(^Pw}FvUF9#zQDAnECXE41L_F%Wgn3XmwY$lvdYGv=0?OhMIl)hOv;=6{*?=jwI zOLwbnZJELbFv#}*^q`2ml#t^d&0*x+Es+7LqV)Sg{bPe)6LT1gz{-{gQ(0P`#odZHgSFtq34j@Hzyl#U zOPKYAt)f+3%rDNnbPD_gyC%C+Wj8}dQj7bP8f&;T%P1e$NnX9-PED?-=KbN}(0Scf zNtwjf%wI6Qf`!~_Fs<&HYMhK%jej$^Rg)!ikps3N{ugh*SX0MJ&8NZO`;uQdA?&a# z=~oHAX_OC%c6!muWTo1`WOyT6~C9K z+qpeKlKGj}2Rl!je*xu%>R&)v^U6V^_@A{cA61Zmu1AX3)%k1rs%M$ZLSo>rT7~UI z2LPtbR8P$)BX~4UqtUdz%W(R*S0=yr$*M3?ef6h;d;sHD3Fe)7FGz-OfHt;8SXenw z&H(7{IJf9UX4T04+|9UL#^W4~FjW;tNKUfW-C?of0_nIF#A!w@TkF=N^G>&Za0~H` z!#%48x^VUQ>sg-cZu8|Tb2EM=iq9?N=>cOk-`-GRX=hX9=7ZI1cJ*~ zXNY6@Fbl3x=l?xp{|8i<+8*@Inzl2Af_+KACY4TN|DeVmk-s=a@cs+H zzG*pX{jq4hFREh?0f0C|V_G*TNH)(AZDS-0^NH2)ZLM9}VmZPWl)*FAaR=d1_`?*? zVq0AcPviuj_k^Ptf#;bX$WJ)-4XayN_K0^8-fb3PaQ&Owfs8^z{@Qk)F%*3rL^{nC zm-Ri^TB&!(^v+rS_Tvpm;LHKZ@XBfr(`9;j0WO%ec5}`;I49RA6SnL@I$L%cvS%pW z(^9K@(J;~My@V2ewF1b(a9}pHh+@eo=g-u%V#v8*mj@&}|A`S+KVwf)gs(!KQz*tk z@Nw8|0p~h_?abPF7V$RjdFYzd1U`D_J6zarHZDL$RoH!2t>$}xu6G^}{kFK2;8|N8 zIx1;{Pm&?PXTzk*A?{48eD+FcXhLY9B$Yr|P;p>^R6a`J^ua(0+u1__y5U|V^8|pL z0YOD?h5R14e92h67BM%8TL8Chwt8o+V+#K&jgkHT9jkM4{#k2?I}&G#^}-{+p|E45h?f{kO{d33hC&JqN2z*Gc8 zCg29AobdY0lIX^}&2>PzQ}TxZ0YCGuf#6Vr5ZMD7rG=G8Eq~z`5NH`bX0_{JNT4{y z=smmEECbf6?HCh9m;Z=AlHK@K`|aafhvkI>G|Z7c*&h1#ub+cMSWTESf05Uwr`@%o z;muQuEhU-?jlM|vY^EuUT^O`vn+bymkXhUpb?(tWU93xWpY>&P*{JUxpX$w7Ts(~k z%&{H^)1+{Y`R-ze%q6`jZ*}G&^RA(~%II5OA9mLLcSyL>-QxIb1{8D<1 z^BWiT5bi0XY|&z{HC@ z%O~?E1pV=*tE2UvcU@RFIS)v}>p~pHMI9!)A7?9@#draQ3*Z#6{E~kB0U1O$ub-SO zw`ds7E7paaqcmb0^6+@iUn>u1%)-fJ`t=}3Ru9+dMX=QrnpLfjllW&t6 zcoLwSbt**>lKw~{-$PJgTV*rw`DK(Ind6#61xLT~;HJ%9kNS}^$TEzEF!<#J>s;&2 zBxWU{5dHlkxz8{!cKQW*7h(n+vBHF>Y2(9sE5<`M;S1!fE5rBfx>4y->}U_b5BV?B z6Uh9F^tQ{2VQ*tv+ix}WA+nDvN-y8V|F|sk#y2) z2Kfhk@-_dQAk$f_AkSY6;mDB8a9X84@1|VjTf5}Vlj?zR}x_fi_Ba_U=S$4`Y9Fm3{ z8e?LGIVUT-M=@J{a@tkufP53hnMdb=(wn=CZJv70J1`eIvY!=3yE)86Xz)v0@`PSg zuzpK3!Z3P2$7WhF+Cs{15^lpM*g=l6^v@l`M99~0Epjgw0yF7B-eYB}zKN6{`@WKM zHL4k9*Rjy2)iMb#t*F0>hy1ORpXgbOFrora?B48FNUfKI*4T5;OwwnRTfO=s_r!)e z#f_X6=}ldT!v;~*eK$&&tsc9$;RxdYHHXqax+SD%e~L?nrc^^T_0ck+*<3_VPrtUV zM0VlSX^MR#*UNuhfQi6*${uiAWG;w-x#q`AazpBD99i*- zJ2rE_kTq;0Pa6?nW|c?He3J?ih?RFDoBqa^IwM+ml~p{z8^BKmtj*%61^K&xZ#fqu zEJAr%!M0{%^xovIOgselN$hCP$c4mv-8h#u}k~(`Ju|G)amZ#s#BgCitMkN%_Is}7|H<>ratVfTi ziW+MVizmp28PsK2N^3S;*Wplgs#MFBp$VEiCRAa6!s-2$O2fjeBPYm+5|RaY{C{x3?_uQyL!e9r&Ng$S(ttxu}6}2sx6dgxXhq7)s+CaiD+-8P|9d-SH7R?5rxV{_Or#9=nf3*O^q++5P9qY`w zt+7#`o$$uwqlm9Lh1P~I^pMpp2Vi=%`2VEo$+LI|J|Hs~;-SVwf7@^4%IuvF?K}}^ zpS3*be!>#L2-`5aqk=)Zbo2pf@3WeK5I%2qPFsch3n zj`VEgVs#i04ia_SkYIT|-1#upxJ~T&+)(*)|F7Ms0kO=K6kTL!m9gvCRWNXaIKVT~3KOwp{9 zj|Uyd2}slHNxIfWgrp$U6 z-pILAG|)~617PEkcCH^dbRJgvUG}~LTwtxtZ0UD6%8#R}_+W0<5WLBaK41X_`pY1| z&*=}AsH_e)iHl@ixPE@Wq%ZoB+tfB`Pch>zsIyxaZ{sGipANq*`R`}=)xHqc*eL&` z2tXQfAUbUqR0L{f+F4jMW_1I;`3zJf7YniKJUP4j1oU{Gj~8V;SDV7LQfWah(S!Or zQX>oQ&5g^ovYEdT2RWoIR`rE8gWf)hdb7srM6NS}Z(~1qLZQ!-Ugu9j=U;Ti&fw2q zwZ*yr2wXSI|Er?J#I-%L0=#Z`;9jw`o9}~DKHxoR9;SPmp^u4(*vJi=vcoL1C3XJX z%5XawD)Wu9>O~#0x6{vgwq?0vFT(hBxYKtgrU|lUv$!@>n0f+hYOOk34*aMK#dj-d zY?*Hd!4^W^6<*m)wx5LqAS4&tLRU`!R;?J;Z6bi;1+j>(8JJ}xG{n3a)rLfCr-xH4oy_(pv<^s-{%&EH@#a)1A7sH(w zXN4oGjSbrzT>un352x#5#oJy)7Qd8crr)HvAA*R@$c19 zddS5t7~~*zSjaQL{^>trH0)v1!Q*0wcOFI-T?G@wZsV6^IKf?L#Kr|)$aoR*vM07X zQu`6n;pA&gkpR>P+VNDT=wh6NMXl1*rcBOJR^`>YIuogkU>A-|Ne*&-_pShgW>j@?ti&Y<}z;9bv4$WJ*>Og@68+;)W9*vST#1Y38XeYw1C>3 z?(E`8OD-7_QBkOM1N`k6X#jR=C>%q>5-w#wBBy-%)w=jfl%R?TpDQeiw712_A$IJr zB=k6W!Wv6ba&MHsfDkyOv>5)XIfj;Iy9(oEh888@ zkQURAUX~wIx=xKZmg-lov_^`@{B(e=qn2IiRH@#E6*S*CS7G3m!j5j+cv@9!rR{7c z&}_=EWR3-S+a<7N#6|~|ziS#Qdxaj3UzGKoo5HzjbNX;7Ilec-l{wSA=H{uWi^$HJ zHMb`i9@@L7XH!`t2>|DnfY6CgGE<&;CYw*be}5ORWxy9O{!NdK3iirz8w4ZZh>hY6 z&{S|j;9JJ*v;8@;=X>HtXN>Ca=W!=wq8-zkeNOs%Ni3Jk>ouf;d!CZ?=K{v5#hTPu7Zro*rDq|GsM#i=1K&!)Jf#OtEiJ;*82 z>VN}2w@6=m4(`#eZN&~vYlY8L5#=eox6?A=fz?LQZx5VM&dN`!?^}X=MnCV{emF!r z1-_Rt(EfI;{ZQu4(1)5rJj6UT@TRw@FsUhK4R@1S#IlXfjP9RwGyWy+y%!p3e zA4ID8{ZzyN=D75 zG+KYlFfWtXu^x<7-iwxd!##K`&u01hwk#UNL7N&jwQVfx%KgrU3r!np9fx4e(+{Yr zmHC)?)M$#?#AST6YP`8L*K&<%LMG!&yL$J?uvrvBhq#3i;=9&sV-Z>8+%z9#hCkw@ zpTve;Qwf)3QI})f)@0!G?5iG;=L8kl;d^qrJW$GtsvYCS@bXWI8U7>Tbt(M+vpb@9 z%^$de`=4*n!Qsf{ufl2j2y*Mhoi}hh87YDfk94VFVH7Q~75~v~Vi;e7mtB%B!;jcK zf%jWBr?Sj(z;f(S0^eSBdO$Q&S;1B&_)D}Ztu&Ua==#H+ld6sr(~(@@rT+312reMl z;AfZ@%nTC6?vsM?Vw5?j%IngHr9(=-Zdo}m71g%i8`W`-TkPG!dyyady3AuYz(jeSO zP&m)~a8tWqlle)oZ3r;(G#^Fx`F$iUJTd;CCAqr1v~C52F`h~fhCK2|L3)*IE{$pk z@<6W`iqrPP^o;yhrTBdc66mOq)2m;gHM8}c{LLbMg$cNQ3WyVx-CrQS!dgojShlk5 zC#4VhopNGU>(*Njc=p|%`=?}hVJA8${a?YLPPioqUShH-|JHfXe{|8}=TZ2iNX@h) z==^bV$l3D~++iVk_+kQ3my$E1Nr+QM6h5!Y>P7YK^l(<{cXyzpd>ILWb7AE2?AfTm z#=Q4d7j>-Thhq(`8___O2Hc3ptY8-mANh|g=yI(%&+;LBxNzQa1ui1=(fJ(F{;u?^ z(DbJXx?-?BzUHLLiS=v?s@K;i)*D&h$g(9uS|C&8`@$%lRfi{FE8(UAT!$pRpCwS)G0kwpcY-%!`_cu3nRu0- zc=p7P&qz(e9NQ%6nLUm}2762#38JqN*ce3<8Zh1qXWLtnV*WHDXjz79U=FiYp*hjO z!PuhXX4ocE;FL)R{`RdkJym*tuemY0^tR;APpk>^$!SIHve^4v-23!V$)4na^E}hB zxy@6F{69q^VL>N8*z2DhZe-~Hb4d!YU#)vWp(l;1GVo1%r!QlgxMDkn_A^mDD$ucX zw0LOt2#A;KN&rla`} zv|wQyqX!#lj8RHnAccA(<@Cx+p;2z0u|o zWOvIapWu#~-Lf|Z#eLH2%cWn{DN}`N6#*zlG~sSe53ml5>54N)%<+bDxc2P5Iut69 zz+wqo%Oc4?eq26U^}Z15wMt5Y`jJGU8?7LEqT8&egn}~@k1r+T2Gwd&gnG7id_A>w z>ASW2*O;csM4M?jqu=-MLrX(qdjvH%*XGVc=R3{39Ngkq;M@;D{PF(@at#+13iLm1 zFv8xJAECWzayXQfg}d=(A2`YXv{8Px=G445tsDQ*#*Hhts+Kj z?2nSo&u&w4g0!+NV5t%h>aVDCbUPDPQt3H=9?~D!k$%cVj_Z?L^uNdt`(!koi%xKMJOe95Tcb_5e za6NugGth=)(0m-V0LTAT_k;af64JjdkM4h3Ui52n;w3PPUBbzUT{R?AuyKZ{(v81vg9&f$#r@J{tO*}&q zjw2}@Qc=lU6v2v$Frrd=Ony3v`4yA#_$uvcV4^-ziYuRd5o;QF-d2ISMN(*7!H=T5 zGhTgk_L2tLz)=}+2*Rd5y%fu|DZKv zdoi5Te=73&{jJv(bx>oJCFw;`j{y=GrS&J+<=Terqk6EchT1x(2ARxam$w@0paaBOedExu$!y3`vT(})+@QPt~>#$H5~51-V~J#3qZ!YvqNF z53r>X30rV+HgXfo^0z*x?R)7Tn-j>LIG*_4)M&mR$llsZ#Z~X~WGL&vXB0i6S#O7+H3FB50HY%bVZ$FG_&K$E;118)jDN+J z`X_#+i??8b_QioOaDznZw=2kNK)|m4^TiDy3C?F7A!?&|y|=wDU+Lw3)ZgW+;6(Nj z7hUAqvqdd2VAZ#Hkth1*Bd1>$5rlu%LdCVJc!e2T=4o1Jy8=ogupDR0sH_FSC}@xn%Et~%%F({1iaJ7=I@3@k)0 zTU+UUg81HB)8lO--Stuw@%xHb#uaPaxE>+(*usjhCso{<()WY=Tt3bqM@wRK_ zi63XtUY!aP^FF&$iamQQ?G6t{3j&~O`j~%U?v482=n@J?p$pni-_V23eRbWQu5b7v zmLiR1_lkn;RdZDY-f)*_iL5)nDl;?-_P8UUmbT=op@jGHPJ){}Fs$Gq)%!ZkTt6_% zg3RK05l}#6E6Z==hR2Hpr&Y{x>iZ4p6t%y2Ogdr09hJLNJ5BQ3fV=_9oQ}K zeb>&S9UKnEyVa9m;Ut94pZ0POU0NXEM3{HjJzyCV*t+J8^GEr|l6R+tD4_U!%OpzS z)|W>gTskYWv^(m4+VjP8ZY4$mdTA6tbWGNEmbPS>r&zgF+G0Pus1&CCQD}3l{z5{q zgn^NZd5Z8*Td%5_FnxFb)fF+kq&|%n6WXxfks+YqZl=6l@kW^>`|fS+3V5jkvwt;F zhgfV5VPjkepU8#gr{Q{DHs<5Xg&8`SeX1gEK@vcE_d5S#I?Ocbf?DY@|ATa;k1PIL z6^zfitYN%;l0v!Go#A4=AtafZA-Et$Y;*@4-=9v2HDanL$K%SgXwHR|BE29TBBQ0~ za=dVSvmJL8>wq1bRLb3A3}@qSs(H!c+!gAjIR2^xQW=;y31-kdSu^3GNsd5JFYw2N z{Cn})i#+Zc*(pcm6*>L}10~5`>Xc}s+2@AC7Ft>5vUEOEFe);Mm_Z?E(8_`*Gx|qpG-4uhviF)QLd-Raf|; zr7U=VZ3lqe6E?~KtN|{{hDh28Dy>0OSq@s{yQ7$WrqdKf2|l(+#@PX3bWAHt_6Ef^ z4K;ntQh!drGc0Qqr(OMthMl>ZGz8QLRh;XnB^kBEOQ5mb!b93iN8&HW3)AQvq0TR& z=^;9A;SwJ*&wx&GchaFw<7pcV2n)pbAdb<%@6`tW1MvkP+j|3^Nzs0PTx%^ovj&dP z`!uXyG34-I&yNm8-ZF={L{Y9|)dY4hx7i$p(3iL?y2&Ct`0n13C(`?`BfeYt#9H&Z zN}LHr5qN8s%)#~+?>@?US3BQjq`+0O9#7s|;jw5~XWbe820$M@{D4dPS&M)^^;zpT93L7z7V4MxN5uxEiWm7m*XqoY6YMvt%1Jq#^_~|6;JFQx zi{?D*r-oc1xg!D{gS2H>$BdHWVwQXL2v>e`o&E%`K_0u>4&_VGg?Txevq8o~o0EQB zyjBP>wyJ8~-|!%ypxh|gDrK&O zJQJzAf@$0B3CZT8*aNjnJ>K;rWdB=o5&oy-8tHzhl3>Yc!CvzkQPzyfPn-b`_JuDz z>qs%7%XHT<9K6o^Hq`*r09{uN~J9?^dDN|7*EngaI59{t(e;Mqr3N|8_ zXb&c&*0Fevv^BQYkIwWSE49FCo!HNzcB6{2$dxlh^pq| ztQ;GDJSaQE9N2huGp`?5ZNG$RK3Z2*Wp;h#Lag_WyHu(99x)=kXdcn9WxAwNaS*>;X_xE$+hdj}=0*fp0;;ois zV|XqZz9UBO$pI4DHUiMq}+v~9-x%lgu|b3`+M^lH}?5n5J& z(*b5GMBsVMF?tBIKqYfV~!1$K3&0ux$ClE z1jW5W^4jm}o4F82olZRZ+?%@*>MN2?Cwm^Mu1wN~dTBAeTC}{aEH7IQ6z$JV%aGI) z6chf4sujhx0|HHn5tSC?h8j_4c`c)}RxXLSWDN z7zV`tAthrP^3SyJ0QfI|voOs1Aw>h`Hbr5X!tK~g9_Bc>fI#=V9X>XiZQ_2u0&>MW zY2@h?AIYoSq^15sVy(pqEM}1w{IEr1_^+)oaf&$V_0_4`>|0*D5Be*6e5E;PD~BNk z&CuBa7V4IcsrJ!~vc#|{G#%K}0L86eNh1;w>(bg*0beS`RxU$2RrKWLsoCpYfXO8w zgP9yj{V3**5LJXzleGd{nmrTm?K5v*+Kx_ORM^%Wo%~$9jr~?1=N$-~1YforBF#fs zJ3r~T$Yl`sV4!Ta!x}a6Z9Mh7T7ac%PtX1nYbEH#f+%v1&VQZN!pNTb4)ZjHdr@GY zQa(MER}d(F`|vZn;+LzWj18n*p+ct*y?%X$Xjar5wa-5dkV@CcD$31Hg?C>*;v3iJ zR1&{uj-I*>zp>|=E8#=JKX>N&kYF(#jB)V}aJF3cZ!_x%{M*bFNf8a}WqQz9ZkaxG z?B}Hslk69msXnD^>#gYLy^vQho9-$oY$f=q5;vDL5M?!;92aYWh!zFtQK`BF5d*-4 z%+Xpaa;#17yHT}0<%(s2os30OyxdoQ2?Qs2n*4bqq9!Io97ZJXtfA zS5)+w%m(HO`j@)&cM-hF0YA;M`B^t`MWaOcq4`?OCo%SItwq=>sWKg=|9R7jvd2ir z6fOhLu`*%EC&+F&1>R%Ti08t#sley5SWxUxArp)sWG9<_FVpQ5Q+k;ij{}(MZoV;% z)8ec*>*dSdt_Ye1);1iOr@DU3G`||N_k_z~y_=-V^t_B%fGcSE&oZLdH{x$7MPEDG zS3vAS-IKDt-_gii{=>JZd#T}(41OFZJ^Z6%s-c@oURNAYxcDLO+hx_})pa$-)DKUy1qbKLfmidZZ4MA@Stu%+9f8&#^c%t!|A=4|b5C3m^3ybq zi}by2jUvkhDvLOAMTGdC85glA1<<2nqT+*lmQ68iMxV;0XMi%Dg#YJo!oCQzEXcRZ zX8Bwt5Mk*h;nJcybF!(GCL?nB+F7*AipGr$0`G*0Eqr_8whGO`+9&;Xgd?hQo!P;A z(g_rgF(0c7MB$fm(IoX6w*90Ytj5h;ucX7^*{wd6yp=mRlC(9yl6l{8g9UG zzM&Kdt<=9-01EF~T7s#ojqfPoS6Q!i2nk$1n&wUv33b|+V^dS=o{Tnh7z6StL%izM+}2 zVJ>RJj6ejrfa>Y8>h|<=M(mkVv)cp>CIhJjz3DbhFjDEGZd=~1vMI?+7_Z%<$uPg6 z+;4;lh4p@(l5>2%%U&X%$#SIe=dxSk{)f~u_)BVeap~7s_H4%q+38%Ueu)PzNt;zM z`olElMHEm++?9%3*pLW}$h4_nN83@y?}jMArEoL)qdJOu2*`jv8a70f)LPyZM|mZy zoE%9Q>M1Msac%6pGsth34e%OrifL~j@je|r47qsk%jkdR@=d#emW-gDfsLuimx7-D z50BY43++NfFnqAIhSfk`=?X>SA@kKhcOh`7wW=-Xy|c;!p%5c@MKp}16+?bKu4eR= zLo5uSJl?Ynu3%N1U50BHwicmH8{cy;x>oleU$&QK zjeR2h9gSz6F}stUP&vUu9A;el=x&k&)i`FC`QwB8!nCN}Y_L_2pB}Rw-A0o@-e@52+Rv2&Fq|g+f z1IXTfBfZ;A6U&oimGQ+hG$Hf<6o!PK^VpJnMMTrgB624It>XUzCbSi~1U%dsgr0CvMD zwhfPhl5TPIw>9XEvfy`XlelYm>p71cqJEj(vIBow(unkGbKpCZJ1A zdNhIb!aySrkH0SKYbF=8gOgyAVtatn${$+^y}rVVMx&cWulh>a_$sU<{ygi_Z9Y-w zGj`m%)BbKOLj*=K^BfxBNFR;!Y)rB-FV6zo)jkmzDNlKP9Pqx7gX-B6Q})+_Pt2u3 zwbD%UlR_DNXb&GH<2_)sn>a9p=%>=@SbN6`<8~vGWg=c?D;hU?in9U*!2L4df0hJ3 z*fLH3#>SqkCNDRxwbH0kHI=s_3J7@`uE7wIQfQ z&#wyUuSB>G-x_fHjSHw{HH5?-a>%4>T(~D0WEBUD^Af+Kbpy;I!Uvr+b(RI?lvWOvMhRbq`eeZkum z`_=x?1)Z149B_w_Rz2K6*$N&Sqv+~QKL~faBx-$(+^f3;gU6AFyqAO(uH5FiJ6=-r z9j9aFO~f(lA{x;CvUJSIX`;VQEo z%o*W4G31_iw356XXHo$EY!9MNbE?j(3}}jz^?hHYaacCGpdNm23a!EyxQZD$+j&kV z@TLtF{uSk1*Lc-&^*f|dR zGrsW0-$aW9_!YM+P8VPeZRWOUd)st0qA~aWPOT~*w5%yWT#r&`Q`6|J9V?KQ?{&_G zC@!UZS=q8+u9+AY?)n^6a`!Z97Gn1{1(yD5fK>|Ai1YoDI?udz;c=qZjysz9b}>vK zB>arIKnD_h_l^9Cz5hyQ;31CB^+9A&K)>TJ1t{@%9!)XP{m_ZNA&^m*rA{O0aLl&L ztcdT3Tki@JeTy)or2X^v2{02kr>j!5t!aM+X?aqcJ;WZxgusw)JOtil<2Q;twBV*;S;j2o7Hk(aFuHWBR=~FVM=l-Y%7WzP6;bj_w{OX zbE>>|YXxqsB~o6>#y-kDq%OyLJ>GV}7!8$9u^OwVUEw-k>?YnUcuuvgNw3blGm@y( zZ$k7J5Y*c{9S+07D)u*5&-N9I%|tpv){?SfYMX^nRRcD`Vo=DWzT9fHV1ar-}m)|{Fif%NE_cOIkcp0G0 zWV-*~p8x)D&*!SH`E^B8oP`P=x=`G5=_U?L!y$4Aoc*yOF$8VaLmyg@-a>21^TDGB zM$T{I(lt7^k-N%=1gDV_gx1VM&QHShEiySO%_G1d{K;U( z9UgdQo-@lrq6JkYT#O5a5(+$=O2No8zx(E= zOk2PBm4)gMp98U>y0?BUzBh~7?B9G=^?#Nvw3@%#jU@u(`m~xmiXw9ILcH(Zl1vBco9G9R;DX;EKv!qdYvA%k-p=g$-61jB|qA8A+qJaibKtbJ6ePHIAI zWBnSIn2iw+3+Pjup|+5X)u26n51;fs&5t*#_6=;fTC0s-9@#`eZgM1wzOYI`<0^4! z$zZ+<3NB*1gLMz9svNSraW0`~g*JD64r7qDi<);*==&KMKA2(S6fu`&tkSO8Yhne9 zp-B|;#4Kuz3RRU1sUv)B;m9gnta|hWEtq-O)f8dQq{pJZNU+!tk6U8n=H>8*0Vzd3 zjO>T2;`11L-58Rp&NuV=Ze(_C&z>s|Js3@Q$r+Ae_N{QI?qC@1{hT0&4H2ZxoS+W4 zb&qqmtqDIjrInZB7u?hPAh&_J&$Enrwx+27N}2Iqxr=Me`o<`fx2H*W&4fxD=MSQ| zL^bj6aS4;cG2R~@j5|%CC2~SNSh|j~hEN>w1!T-mF>yuw2mA^-5X4s^ErC2NzO^Qq zbIjkd#v+1b;EaArn`7A%p(7BP+p@J*P?c)NMdZ4#Au!$i=EdkcB#WPd*1JbrW7o|& z?7pSw55)phE&UxG{)bA0i5~u{bG&qI1$c39jX`i<_gW7Md%4RO9{c44dP=E5A*i>Y zlA+xXeBU5atybnCmOm2l8K=t|!vVorjO}B56$#3_Q?XUG-#}^vI8^b7{;wIhk#(#k z_&@8%J4;=LfvFy5a8}K^Cpqf0t)Hvu1{?0&zI5cy@YY)98fU`4<1vTK?LvUz8Dj{` zC{uIrI%18NZ)MqFg^H+t-Zb!sa5oFI;0ucTXV|yF43N9v3BD$D(}vUDCL~2a+PcGI z_JllUxd+CLA6e+!k0O%d*$oPgTiFxZNb%k7+ znpd?z+M-?8mlzwHpGD>NMNQ0eXO+3cxeTyOs*ZZ~?DTj6r6%9MWaSVTPIPGo8X>*bSGNfWf)4_ycv)fv2g|0D#~>)7*h&I;HuGoeFG zr)MOP80iitB$L)unu_w`L~famJa(^^IMFa>Vtxy+Nu{kwMF6$Ms@0E}{$x#P%Pq^I zJ4}MpAPHlfww7o6;Av_%N~555kj|?dsPjp>JFxj2Zn-9h8)r~6^O&T{)-DERg-6cp1uUUK>9 zpS@Uc67_t?al7fYrlNK#QNiBwE9?EdVkjY`fE?EAed!qOfjo6ECYsM7cE6mYWPnN)|E_IJ2&0a1ohDumco{S=4big_%Dq8bXnenY@%6* zkJ@F}=Rbm*5;w?0pQm$V?+|83)2qWnOws(+59LLa0`Xgl=!}2wxYV~xHwKwhL?1Xll@k!aGam#F#<37xvI#_BQ{j=#8@~t&e<1xaW}u zYXqg4&a})a505!cxkyL0yQngsi-pNP=8v4mA$BD1lH zOyj9?VpgB=!>?Yv)1>e|YrHOX?lRaeZbkP&`zO@O&YN_F|3}wX1+={_U!%oZ+=><{ zuEmOlVnvF(yA&rl#VH=7xD(vn-K89y;w~-0f)oi55 zX@0aq*c9bRU38lLw@{QJR#g1EOWbm{r%3L<xabxIUSw+wVQh%h+!D*A3tY6l|Zz2Ioy5s;H+^V8GXk zn$Fpcl0{*5uZ;K06rehnp0=6_6_ixKUbMb64;F2&6TxTfQISzbJT`sph7s8AEn;x! zx6cyD{;}yv@9fCj-hN@D5aHvtqv@Uc^~?zIOVGFGnG?nkY>kFj$7<8|c;T((dCeH+ z9B{ilovpmcmo{zlP|mWMF`+EUVBw}JxJ3kGEB|e0t6z(-fa-|B51!%WYe6& zIr3Bky&{gklqkp`W!k`YHuDsed!vK;ElfI_b+*j0r7fJ)o=6)a^NqKC6dt%lq4b|wrjj1?~W)z`qB#wGfy%9)Uli&05PnMFMfA|*G+N=cnJok(GAJW3ewPzbhfeV){jryFI`S3TrqQgcZ%Z0RRn^ekAek>6Qhj2CIZ48bQ|@5V7Pl>k;30R zCm?L7+E51Z=1Oq|0A1$nRbD%NL_kvg{L}EvtObl^)?iEYPSR0Q3eu={sw!uOo{x?cUcKV)w|bF*TR~_$y;!zh}wo%dlA1WxU#-9^@ z_iLQn3AEh>ea8(8yB@?Xj$N1Ao*t8b6v0NpLp@h0e~Z-cpT^FK?PXiRzW4BR0$9hB z!j+zCG;j1zH)O6}qP1af2`qpXyF|H^j|O(jbSvWLqQo59la_9ZOz#F9Q;8WedX|1W zAXb|6!2;c-3gf64-+?Pf*DE+BIjH_HV}8XV!7*~6Ev{KXfSe*2#J)%ZErN68`wD<-1b=+1jVmyZQen@|@0y)cDmZD@ec!I5 z6S;M1(az>3eB=zP=km?{wd51R=7XpwH=kgt-ZUt!y0|v>P^3=B_%a9fh}s*oXbAFr zJkkd~TRw+JCIyizfK%@msc!@PDW1;q1Nucz{G2W-;-dU>8U0g!goJLyzOk>Kz>4C( z)W=9vfv#JPQn8kkVWwk#p{74!HKBrk{8kYc(1OOIqd0+an_Erd$Imh=_ef&%aj<29 zSMX<)7lU=iO~xId?Jno=$`L4eCB(+3g{av8CmSg6YL|o~U1r*MzJ)yu6xBGhO!pW5}}WJ1*Oj0nBa9S6PTYbl5zp^7SE(Yfk0U zQF`ATogJ5mp$e?~4lwAWBfP(y_SqJA+T0v+61{{0ON6P-+HI9K4jca7YZMN^d_RC`IFO20CKWew6pY_gMk=(jO?R19JHwpm1Q!42f% zt267&4}s@X81oE}5DJRLt;q4>xnE_1ktj^{qB%@nEGw)&|CdYetpR&vzd(%PFf5sgi%iu&vDrwF#7kd?CEE01-JJcbf9d`l`9` z&=Rx9Xt93_Q#Bc4M>P2)cdx2B`twayEo<_Gc)Id89iygO9nJag#R?pLue1m?HFpZU z#O1%t$OO2^s?WY>DnzVIVHG*S3kWU|Ip-!%go<68!v^d&&iJ#OhnKwu&7WR)wqMYc zy8BepZacw=ejZC3Q?$Mof>qyN_s+F0Bb<0`1$pfcP{n%sZ%K8+Th-5a#GcvOG3XPI zxhPQfb+6pXL^Wz2bAR_*e9hB9&gonq1*yfYP9Hso=N%T=25l{URM|qd zy>VGKRv%cd8B{(^VrpZ^d;)!Kd?X1=_XZ=iTDP~dh_1}k)>f0Ou%|U1ia!~+*^$C* zakTiag)wewTkhC;K$rdB{bKwaNQ}I;i!Fo^I{8Mg|3>uCKh5&ZL1qNUwZiQZz%5W6 z68q|7_Y`4+bI)gqT$W|Nvg<;NO`-QMFxDK)W~yvZPDVtNs$UFa`yH$GC;w!@xK)K$ z?GN7Oro2y#&fD0^hh%a{w5$7JY^Qen)Cwl}O0`{5ynq6C!qk$lz+l!&`Hl20U z?d^0)v0dWH!FmQk-}7pQJ3Uu?u=&Hj@;wT609+Wja{%tGdAvI{;a>9}Ep3>3$J!{QT0{kcnvJw0slOSM8@%9`n8y)kynUdE zaj=@H?%t=r&}7Db8n-{_X)P{W_CDIU?Fwf;PTmJ_9}ERl&Wv>T+P&oB#v@=ojssjN z>I7<9$uZ!=rSEiitaqY+D0QtrV7|9fCi|y_&iUuWsY8h0iFhYAKYtgh;(2+DuE~w2 z-pWjQa=R4TsAB%t^smBqq{T`YvOm;3zD%B{HKe4%%8y+M*UkIpt0B$7F8LW*t;hyZ z)@cfcpq(zG>1pREJv*wYt%59`r=$k;oNa-MWEM z#27!^g8M>A{P*}_m>WEAynE#E4N6Y`t0U*N(UM&NtnCz9eGpX>fZ78m#~H3L{*B)t zV!ExNlpcwR$d&7p_rqs>Z$xw!vIygQG|07m4E+3ww=DT3#wEV2-A3vDMaat0WnXD) zP0=c2-8W`AH@d|cV`Rv$3_;QEgD{)rze^qq!ut+Oq!$pQ(+G>#MQj>}E@%11M1*Oq%r zrHYZXkp$)RLDJA>nrs!qi?$bgwr9nI%vd5G<*V?5AvDo0ZN=J*4AxUnO}G|6{jl}~ zR{M%juta+K=MZbzdu&Ui@kA8iD({^mjh+#5B>UePsh#L?j}2mjuLOKA_+bawo7d3i zIqClRN+P>#_j6N^mHGv}JTK#YjWie?<&Y9htR#ve10ZTQI4J5?t9fZHuloV60XkXD zDS!o>ja?(JMGWdeSMZY>h3^?nOItTkZ@VqvNkin3^Qk}S7yZg>)(buyjKrC1)d#pW zyi6=4hD;j4=HyjePEsk2E?q{_@@xp^lsMDd=@G}?TrkKoaTx%bzAM^v<8p7ixoyhC zRv3?94B2^2V$&R6ms_)G#$@G=f_LS`xe&ISmeDP>(-ROgrtf?%4cXkILe1jkNUczO zHaPyLKYXnGC$Kk%K86S*_#RjOuzuFRw#xdFM`E?y>JV~L**12LYwn^deMlgL4w0F9 zyu)R5h@nI6s>ITAa8%!Irsg6#Uus0KVBRB9u5V@@Fp$`!kI)q%ke_?khZ}Zu2;D0` zEo-XA=Fq?em5mAx=U(&Duh}F^>J!9=k%%0ndp(MYtedq6ufZ6pBX-4nu^;c4mHTSb zRvVMJ!th3jbod#cQXQ1;M@$CEk-7UmxIAl~1-e$o$L+oPdFVIH-+i^cczUc2-~Jhw z12TqRZMIFgBlQYzJTi#B55porB znuiF-3JpxGunjww8^u7iP7g#_n!t;Xen2ZU)UB+B$-&l@G|A1I9`=8_@#KHH@t#1M zKu(9*vmaoQ+(PQZlzx2>FZ!Q{hEx#-4Be- zWxTQSmztUSBWH0dNW?`34U#jkf-S*E8Vb$pMrPe7$>l2fHOAG>?wN=BvStz>Cpn_GD>bJOo-H>}!|?^DA5579`Itd(df{y5-UW0Il7 z7t=RC3bOt8s@m{#Q0m?s8|#pdyg}@m?t15s0kLRJBBQ2ZT zu`O*UyBnT_ROjC|`_wSDkT{P8nZ?()Fmf$3pBFDb9AQ2Nn{_`Dn>$QNv=}JE0y#Yf z%Qh~s=&qUO{rwSn(eY%pu-x|`UE8T%u z7up2pBD{x;|M2Xf7Jl=uGiv?MPd~z47iI)Nt=#HZ_r168?qS9Fh4(r!stOAHhOKQDDkCa^xtJ8?^k%3098 z`C27&uaw|oJMmp^m=?c;K2T6AplV#!b~U_AH&Y2$K;%(1t{*SF=Qls>=hj!8KD*6J z50R5HWmFZbBi8)|L8&-AEQ`A@vV}iVyAEN7n(Tg2j2x{siQ}U`8M-$O!bw7h*f45! z)C~wgZGOZQC(S-D&QuwCsyCFd-=PYI1Gv=;@t~0PZ;vO zkOqAZ<`)l^R&l?uuT0#HkACsRjER`B>1QNhDp0rT>$@IR+^#2YUtG=59lZCnl z>2!tU7(EMYmq6J<-g3zv7|H`VzoK+dOd&XH?YVL7 z+=V)|SFQJUow!p|!258v5yfB^X=Ih}?Hb5YUE#!&$>ux@l4roU^w2cIH+|iuy|ym2 zIfv(Z_piAMPy#lnj)Gb2-}+aaRo8|r{)F#Hy6(~4peaKtNeDd0~u_MB$& zi96V9r(YF>Difrg3Yj}>>bJz(?9}a$d3k)MK^~vN+k(7Z=|Ag|b0R*wA0&UHz~wN1 zb#-K27)b(Vzj!z!Lq}D_YFuHD{Wh(`*OIlJ4!ytvCkK{WH!_mlrBjkS&nFv?Xmh~( z9ZsZsKgxCf4d)(+{-nIsn>Tw+LY^RH|Kno^;&&Q(nVy$qWuK7h6LVaPvn)i38rfRq zahwrId-IFTy62GRSH$N#)Pj2un95ziNv}(6TW}8P;a?CZ@`*X?Zs_|v5t_&!sv)?r;0NV3g&GLUoYM&Ww7>Ves_9V7+^dBM??25CHRjmT5^nRtSA zMZ`a-iQrGr57@jND3nUvh#OLeY-X$!Hwx-HYCZ2~5vK=$KKXLo_45dRuxvtT-9<#( z;&T$j54b;**{~7;+(tH>?T0_mFf@_XTOjYDWwy|HQOPe^Z)hx)53zxw(HCzM-w5cx z;mX@}$NaKWvV5EImNHjMo|Y7OnG%A0G!lUK0;B7+%v)iE!OE#I!y-o~;Tt`EKzwL` zH!+-IFahh^V_V`kX~74{l8rs8N*bue-^~(7YE(8|Ysis#7;gN*>SSEZef4 z2`r^BwORwZ?!o+Egbur<~A`?}tW?SyDRN9BEm@n$V zb!M}{Aa)M`%$GB7~eAYg1w43-^(64wqnbgfxV#&OZ?C zf3zj=rSC{PD|XexL(gJ!gvxJb^reauRQB)l<8I&cCB1flE^sHZ%%Q4!v+2}DluV}# z1;hdN>E%GpczE#FfkaijmQ4wTY&)3aeqGDf8vBWr)_sfnkxFWrsKPd^>d7V!`{jF4 zW-M;k`k2mlqm0`&%D1bOa%MX%{_wn;lErSS4&txzyKGBxtSMJsQZ;S~heTBs^NSiM zSv>P6>aIxsrAp&1d)=GF!Vc=wo15iIwVC05@9CP#Jv)a@1e?1D$E2An?gURI7EwDi#QDfRmsIcqj5)=4365r6QF zy{Igjy1Ks*Yglcq*&cuPn|l1A>*@Xg1Xx`i>R>vx;HwWz0KtZSTsF*L`PRg=%cx>` zO6Q7%$2o&gyJx*7$a6O1D+-O=P82^~l8^PoT_I+6P2z{+pN`zZu^UQOgU*RcWCYn> z91U$GZuN6eS3MYZxPGd0mlYi{H*H!93rUVA71cw8Sl|1Yju<%g{kieh^3&zJIX&yg zbp@7b>`wLL{=}n-|M% zZ^}&V&S1JX^HFVwUIC9wBRe)zfR|0jk6@-Lzdd#5d-z6p)eiS&`9mdJiHb8}IR~c# zKcQv>$wfXLBBlm>&MnDq1KwbjMm`82nJ%%3*E;PTq0ofOI;s`K_Cu zZ`JI2Dy2)t4x?$G-991UJHm*#kM&TN#EzYP7fpt{jINp0@v4oY`Z^%Ez9 za-#rF{M?TUEn%9OKB=xoyK~V@&_atkSTWZN%^?I8so-C-PRgJJwZNmA4yu4_w4QKs z$F(k|o1TX~$uqGWxX8;UMvPLm=C;)(Gw!thi9jT6FlSPqAq>1(?)ol+-k-fo<_`q`hvok;l= zE`ininZbgB08a6(ZNUWoBkDIqFG5}*Ie_DIB4AzC8M z1U|_c8Uy#;#zgX?76s9Xc6Vt`(wn?)wJRZG>O%w5(<|Z3awt4)0wfB!UGJ;h3~T!_L{ zUACt`=SA~n1R7F$+^u3{&fq@aG+(Yn`jvx3y~+F+7ldio5;HfO>ZR|PW(HNxlj+tOhgnjT(?lnPB8hG#E@_x1 z_-7g`NJ>f#HKFmVeJ+y8)0%Pvve%x*z!q6N8g6I`MZ5+>WXEi9u{26Y(s@bKU-Z=} z&wreHmVJ$Tr{T2lYIcBhhreA?$oDaIaY3}$y^hFZdm|YsI#zWyA5LI{teZE)x!7sN z-H(=_9H<#F6MeV#1QDEG(Pp%tZ?H`!z*P^N_OA1GB^LuEeI8t^NuvDD&$jxEG59Ew zWTYzl7-I0=zp-8yR(RTi!>dadT+*`@dUJqwfA47Sxg84ba|QplP{s}EZ>FyyHYgM4t@< z4=-ZD!sT0x4+tvI*rrFh+Kb~ezMPme^nI}YR&l0d|4^#0V++t|Aad75(BWV0Zh-W^ zNG}~~cw84>dB(>XrGI=$k3fKBNF=soAaw9$ zbSQPpd+MILBj%hY+!qHsU{x469fU@T*Iw!vAW;M_MHObxQ}M-4yz%kdrV?PeTLjsY z9aO`cN*y7zcV{X>+^-yzGZU45cgXn6APkyi(Fw49d#dUq+@s%QbdcQ1J~BZPS2_Dl zw-MsAwkzX7J|NI@&i5WVNHFfgs8>C$R#HB@dWf{2LDp$F#NbaxRdfOsiB5-2a)mICo!0~@aBGIvbekUVK0L9My|cXvoiq5IR)5tQ>a*9 zB-W9EKP}e|7brVL+jA zR7Y-pVXqIT&)GMr)DBW`$}ri6n;*d?>(f$1MX(Cqt19CMq`n(Y-nfggXwg9mw-B3} zn3F}i$RcF3?W62|SUq29p$t83|KWK`I>PWu+4r~rI3Z9C?BBSKu{&u(jLINZ-KGZR zAu09B#E~b=DoVi&#$h>Aj`QB@2>9n7lYBY_UfdcCbzypJRMIf(Wv|3RCzaZ;4~@7x zM~Z&S%V_5Mm}_Lrfg5L<@S7V!>l4z||EXeM{h>J?zs6uv8Tl`h0I!m@%~YGxY%q+D zcCsukQA_?x{prPSSTX27)C01jnI3-X?s+o�b?2^6 zXF01n-1zm)3b2X-RVp?~aNwu14UohKuq1s##P(U>aelmoobY$YeK{ro*1*?F^bbFb z+IU3^^|2J27|&IEaQxLC_YzmcTl{%#I!@92C+SSQdV|=vg#7@QxYw|}aVKOc-Wwm) z>`4@qMzLW(e3&0KlH9AypC1yfHm+h?USc|*WuHbvy3gM3o%h66RNs;1-0b)4yGqI8 z+U)e>3+w<+Gz#>A>M@H;8E=7MvrcjfvytaitlT(0!^}B?v62bdKYgW@^xdNrM>BBG7pykRZYWp3cvi~3Bh4*jWRr~cHk zPkqCf^HctJYw08Wqods4^5O|=DX=uY#Lo-%-G+iVhRKa!4-TfE1`%}-w4PW>OYPN zE|S;2_6b@8(9~cdv}S*5EJVC#K<#S~_sLNU1loEu(EZ-7d~L#6$viKZDh&jITk+Nt zMqXv?SYvNDbyu5NcaI2?3Wr*^hCi3YecGH)zbD1lV>SN0>=A!VIn-G%O$zeEQw_X= z9w@M!i1(?#1#y+I_~DC7i@&;remR%%cPj79#)Abd(&vrQW+Q25|A>_;F_quBqRj$Q zas6HwZ4GMcac4{srqe_A7Rs0(TdNOyh`ipEI=k%gK^ksuV7%@_qYftN{>`_5F{b=6 zx$!o(Ku2{BfCK#1t)#FOi5`-sA_pkz87)oWjW10mB|E<7!4^m#t2 zU5hiC-0!x`1#-_WIK4+N^03H=Cvw&H6w$HyKqWj(^t{`o@hPb2OFXPlc_9CJuaIWT z1qCoB(H`~zt(INRg5}~vL}Did{@~l*g|%xU@Lng+gZ-xSZC}IK6dt>Ca18uj^`qN7oIW<`L)c4azhd=^;hqS-BvL3pBJ5>)TyDr|8fm4pN*C6BjPP|@v zeY%;IOFPw|Uq3Mqi}@)KDodGXK;IH>j&P(nVRcQ=jq@P4@b{w_Of%Rfd`(7yL}|<3 zI^v(Qd7jQFF;$*Th!etcdPy3@SEtu?&&KMKvWYtagpnQ?X_ot5xJR_Lcwa+~Cu#T5 zH(~RgjM9V>dA&dhtTevEOljRR-jY{}jX~RMveh^&%O{VA4&5XgPM}lnt; zq;cQBS`Iy){5X9NJp?4(GYPAMF82EBS5fss1Xga3SDHdedJg%Pn%u)Fcj3RrIEGkp ze7YY_%CGW0xXp%a*lh^+#;<;}nyLEinA0d|qDODMFUE#4uvB0!e*)g4GP$8$JW9uey;~W3DnXUhT`+#lr zOy2f;*3=2~l~O7+*+8t}1vzc)Ry6#~5O6d5ooM+6+FJv<1L0f~B3WG%BLlt!2>nCB z;kw55?C5OON)^8>VWLX0o`_zZ+Z1%egOHPy?BNhA=>tcO)aME!qchmll7mRVL0?ah z@y##e=G&%)=$Bx71DQrWW+u{1o%7|qc0;U0)MD&#*1a+%fw9-T7j@wPNopUK9l4)8 z%XOe5LS=ucRf2AYDtEP>P#r$%aJwBU(8=oiE2-Xkj1Chn-muVx_k&=p!xZcTp`RMTrq}@-m*@NxO+wK&f6X=K| zpz^MD{4*`<4uSVPv;AvA2b;#E(v2pzbyqX}kr#XKgQ>i#)1iQXgQ1R?Uu4}v&!*Zx z4u=4lKe&j5pLVV1dVb9LsA|j`sB&&rN?${g$F(OhzpY`9g|a(mMbCX&gu|WKCISV2Or`deO`;WatJ=zX(mCC+huV zeCBI#oy>VmI);pMgDr9^_{0Sc2ee=UAn{8^b4v9!6;&mBw*Db}^||HnRG)-kAUQH( z+!@vGB>&rSyXC<7pK!@wf4m)0x){l5+zpTgfaq7nN6VfxBp)?la6Vji*0AxS=RR{} zyV|o09t-TgO(8$OWE4f5Z3ll5uggRNP6R&DeHM)$gthYP-8=gokL}@TXMiSqFluLG z3oPSdhE}Mmc3wSloH;BL4uP$4A|$hGze`6JKwZBZw0Clx#dw?7jyMAjK*Ga7H2cfV z=)W6!QvZ95lk0`5cCS(m+=9vzhU_^rh_~opf6p%oOf2hiQ04sn{QPMo;aCC~+E-oeWEOpxO9;cXVt9_}ktgVE1=0kGr29v@f|AcR}# zX4{gI_jE%jxB20vYYuI1HH}w;>*-+RA>h=?FKL=2(r8#;`XS}z_L8KI@u14scyTdd zE(fvKArH7~RR`?WUw1ROY@L~qHDrP6$SjE}x;RFTmJe*9M+u()mNO!KRVZ0Wu7W$o zQOlR4+qYx`6|Rbn0#HDW(<(`e2u?Njcs9;cf}!RI{IZScQK3fOfGB@|_y)hhV{!~u zvE%>eB>A-}jGQ065j1qk=N20|@~S>w!mNBulqwVGqC*5tJ-B8?x9EVZbulr}h1chP z4I&tRKNvzn#2ok-fWibVILse$M}jlx^~xx$U~HuDC=$vreaAMN5z-B)WB3kUz}4dV zU2$uER-HwE@F1%=QaiJ^5MLj?hOYqZK`XzTkUgawSdxteW5K`ria$<2dmuiglg83< z*GhQ2psfZTzW|(4^wicXnO8$SY}fGV1hYbl?UXneeB3g^9|~#SB_Z zDc0S&>Cl;W<%d$pH;WEkE+Cjo`SP-1Sr{xe{pPu&I(gDB>n^6?@uGjZ`*#$p^RtKP zlyrdd8|FWg<3vCF&-JcGsEwz-AmQlp+T-b}o-8&?`N=HTmG}Ocdl5CUg}GDmXztJx z%IznXzP%-X`dsAHB#K-b^W9@!1PsO8#J}5jXepnP>-SNy!^Iz=*OP0%MkA~4dHnLc zzQt+P;U)O@xV5Bhlb1Ap59%z*v{cYrLa8blDJCeyC**1kHkoZsAtn? zQZvIYncw51{aS!duOL)b-N}n3PTz}zIO!*agMz*GPlD%WG01s0W)Vu-1Am&E+7Wwz zJlTiT41Ej^#UjAwOXxMDcYG#`H{^E7Km{3U#vnSx z3oJ#e6(7vU7wPO65s{A(F@zPy zZTIiAJyHepl->y^1WR2u8F6Yc)5>p7lR+z zs4#kuKP~0h9;AID)gjbclU$F0o^=s!JU#_11PiL6AF24UE?5I1^ z$-o;Gf$`(Il5{j1zp+aZHI{+px?oS3Tw!8m`TQ>)E9n7l6w)g`x)aT1rP=IeYWkdI zhJ>R=-Dic=2Z@ir(_HQIPW9FZGPURtR5TOErTVvPd|y|0T^~}K!>IjtO3n(z9(Qrw z`WuG%ka*mMBInsPgC&#KvvAnmXwQX9IYr^`-gZBWdxiR_oG=u~>{otrA@TmaMRY|-_U)q2slK{U@1xvnv5RW8JJA=(#LTq+<%_>1 zeCo#K|7+3TXXHj8{cINURFExua$8C)>#^6Ljt8W^&9OMNP9}bLWOS%-Rtw4_g!P0I zZ@h~34c$b?{qolUd-5H{oGXA{{{5ou1EFHGo7mImTY7%jE%a;JF&={P?O&F{!wzT` zeRF?jYSBNIPB=CHwZ-F_HyCR1oHM<(&3b>^Wv~d)ZpyFleI+uoN1GB+H^J{1TbU1dMd?%FWTm?DoWVa&F;ptdcUsd`0bKMwNj z*rxv^72TH2tvGvjAc;M6q@^b8qK7LjTq?{NJE) zD#vA2L+|Pc*OM`YJVf3iwT6&yAIzu*MbhPlUk(qGnjhCIF8?~Q?HJSK(DuGD_(dk- z769c~P3q|2;vWt?4|yE|d>u`Sl90wICo4bEpvG(|kHoww%INV_2a8&QbZ73!Gh;}e zP;c&bd7cUacLpOXPJcxa*=@>5IO7DrK@Wbj^wdh)Tl-hgDrXnU2ZG1DxvWOQm(spF_R)5R=fA)NTYAj7cZ{%!kl(r3_QB%Y#sN~c1u>}lP;cG5M$gJTidbl zeIh*wM%`{p+M@!KY}FRT_UmrK9zKBwR9fTPhqcB*xNBek@4V1Y=xAEe#Bou?agVKE9v%7An$Zka)U?;PPcZ9gnGz~e zywF2bs3&bv4VAMwrz4!!ZJiaK7akK^Y|adPA6*m`GgN*0R0_>LBKzF75N%pkea}m@ z=i>=YQX?{~_?EsR+67q;=w9T8FN2>@UBg;l*nJc(?)N$sGBWp4I8)NH__6uCFEIT? zZ>uS$je0=!C_}5yXMp;_mw$Hc_?R6lo4CXKG$*X-7s)3tAlI@JTpirm*T8|6L!{`&+oP+iPu>3K zvvn(2c+3RPXM`f|OpVbe`hN!(Z1QCB7INsmX!t`)#Ajlp;GvVc?%OOlv~NfSl0M{f`}++?V4MMqtjxQLu51z*M1y7bRfqRaoyu&KHM_CGub(%T=`x(jgOuW}UYGQ)tM;~R98LiWrFTt;TiEYrAg_Lpvbzw}%L-Cj!C zV7=p;9hTbHT|mX%n{yQ}oN_P?fqIF*=Azj&5;+-6JR7&}aXz?c-OCs|FT$F1#S&xp z@naLxj3fG8*N@)b}(`aWMC zu3%YsC-_@cAe#KQv`zZ5F-q%U6!g=GErtNj(NRm~7w7+rI~4KUkS(hJ{HP76PuTLm zP+T-8-9sq$UMEC&&HY0#D6ZPwxREs~LA##AB$v%~j=q)q>mO3mVMer#m6h%CWI;=`mg zQu`{i5geV;m{4QaUGK#cTZ!I>g{GC6;$7e z)7kpEDFPk6n2U&MQQd;JPXT8eMdr!s;ncTf0-8Zxo@VUk&y#nAJgdFIeM%!euNRM$ z5Qj%Tky8@zpPTEF(Fa0P47ZE}L zPEYT5IKnOBIXhv85#f`su7Sag>qG?#BTZDn2O@iB8rEPEV|XB#*}q!~MKV4^bZUr! zgLH14Iv2jd8TfY1Yv3! z^;3g%z|SOx^S8#gJ0!p%qEt4@k`fVU`g^mZg;%h7S_yedoKuP#Y`>0?cG6x{OkSGi z7XPbpKsaW_*Rl(vwW7q#P??e0sPwyYG>!XH;^7R)_?qj_d~tb2`r@yKg@#|bdELf6 zb+hk%t^Aa(at5;?>{>;UGxOX|uy-pv=%~x7=CtS8$S*ijU3F2U4DUL>0R@4O;}kcDFSIrJ%vFJ65A^>yPvmpZOm#h`dRLf z3=>!waELvWRTpK>3qye~iXAq-#w2=bE0N{hMaPpVMqG5glE1$MvCJrQ1x5Qp?5Mm3=%h(zxOfbbLmH80(`P zA;pivE|$QIH=p)DU#r-M$r2c6CqOSM)BAEr230Uu*t`y;A31Em2eUbrTftb9`a&PW zDO^a6uD*)txGp7Lys=*7hyOI!(oo^h9H)%Rotk9Ewuyfu%LnV8^S6^Uk0(o&%*zsf zwcq(d+OH9*(TIMkja~NU5$H<-#>joj-FF$2D#0Qzi=1v3rkQW+4K6dZ9a3-W?F+~q zA9KseYZM6psJC}@Bsu@Ki5o6Z4(OkSTqJV_Q28uCuDrRfyNMB+;gn2K1ru0L6m48Q zghc(qE%m*@#RZWUAYqQ1*QQT`lUX?S6&nSaaRs?2X1D?^D0;c1pD%Jhp0svfKv&i49c4pjfXxnJw-=mpY&h#mLu*`=SjGj{cMeQ&y3 zzLumUZ>$awFEUcClrRj#D{t=Z*Zq8+ce7xRgM`-Fdh@4P+hhqvb9O`_vrH`g4X4px zw;Lvzw}&_lsaRd7L^lV$A-;u^iQ+4Mz@RrJHU^-GMu|GE)b6X8#lnKlrC_AE#ZVk5 zo9nSVBxvrXF95C@c&npgPj&I|#ZeyD<_aG_!Se7K4gppy!OHD>o*V7v2RCfnM+0|v zcjajR#dRn#{{-Q+F^BQ5+;z{jlfiARPQ1_gtV0@zt}A}LGQz_kXtW*kGvty~jaT1X zJf*e7hs@LuSki7aSW=U#Pn+2d4vvM`z{iDP8NOqosTt%?c{rT`3E5EZ?N5^~p=O!W zL@+v#^J(OEb=yVP*T4d_lmB%zI3jS(mD zt&CSUOd+|ftRDb_-}lv|65K3w3#M< z?e<>GPOGUgM{ylYmbxP5a9WE@9H%L{VVw=J%pD4MW6m=n3-7-^m@Yo*N>bZ3MKDrk zaJ0u9UcQsTONOd076l$r9#jy0a;XBX8wx(b(z!ZHRGpS^$#A&Yi=NhIND(`ivqE}bH=wyLhsRbrP@U?E ziNbX}@)P)nwV3N8ul{-rAB57})I}}ei+gVgKbp6F{o1X?l*BZ#&yB%>V>k$0M(gEh zp3LQ5iIWJWUEwU#JrB-r*dVUEcSJ6W7ohMo-tvG|8-5zG!eD)9q4`gieX}I{KbI9` z`#f51mtMS9!6VV$A+tZxAJq<#Q??isu#2oJutRHtn)4(>}L;!yT8^ z|1J}PjYzFuuv?=Ibu9;qjaOS=i(vIK_&oBt6fKzru_M)P8nl+hGe348uPBRNyna5q zXV@QUSKZpb9#E$Gam`P~6-p9f&mpTJ&~w$(fHt&_Oo1jAeyOW$o$MdBLTY=`p=o8P|I+ww)fhbmDnWjq zN5pXjq=DpIaIg7I38chM@PIEm52yG1VmxB{qIzsKKy&c#)l9ahkF>D#)ei)9c26@W z%Ou>;dSZ2=#&bPRV%D!7PLJMa70TqhmgS$ZO&le7T)*GGwDs@&5wS`uQAT_Hz0;%7 z;Jl7EzU|}-Q*e`>P7|N#XhtgW^1!1ThGU=fS$-<}6c8;Cr|ZejAxEC|P_G8H2RFAy zI|#ss_M}5h?7RkaOKEUYGfo{Pt#g>{+A!pigjl;^kq5fYyS^`k9&bsIcyc4KFNAxx z`drDT|MQ-mF#elVd)68cgM>ZT-Z9JTp^CwHdYO{7rhh;RNE%ptsy2iJ(m%1QqzYY> zss?P5+DPo^MftG1RuxZV3XcL9f_2ZC9Yt60Q|reCdAJfFkeyrT_~w+ zUy;Tiz|Xyz*ua_=B^}g=t>72)n*$5vB5nqdkMW&}cuk2Mt6z_Jkbrp)fEcZLZ*VIi zW^i*5rZ;p5P(nShDYyKT(tgbmf6B5oK)oo`J#lMBCZlWEH{e7XN4xsMqZjUHDl+D4 zKXBEb+C`YT(9GtiWqj8UVWjn%P$zkVv1{5WTV{-&=h`#;TFyt-Z2vgJ3HYbEs7S5%M;y*G-N9GMJYU!pSK^_3x$)k?vp{v(t+z8r@dTYPRi7`m7Sy{ z|0%U_75*_6EAEN7YBK2}X_=uWAOILMbhSYJJ) zZ$`(4>=Sjwl89(tZtO=Fe_h@DDps6EmkE=eWH_IAw9jrZE7-=%4$|24jYq@$ox%Ezc>Tr~=c504LfzM7k z5AWu*UV0mLKd2!%0K+b#2L@iklJNJ6CW`uuS2q?>i!|U9*PZ4$Qwsy=bg^_ZLuLF{ zE6UvBMwF+r?3>wfBBt&CkFBqcigNLy9ZE_;1ZF@$6p@mY?nVV^ly2$Hp#~7?Ar#Yv9&S@1!{+E14{{QHm3a44H)lXh(ObD%Fr@I)ll@ zq8>`LN1JD~RbRL8U_VG+D`QLV`;Wp=?hQuzC&3NX{Pz`nk~q_`qwH^LLSKxS z$Z1`3=eg_m#vRmW;e|6#i!G&@E-!2>lCU%Ms?VDT!j_{BJz@n+ABU`h82nG0LtZ>M zY<=bL$sCWfpE})1k)d(bz2kC*k+#?)c%9^+Y~kDTq}XUjt8jVn$OThLe62zu10Kn* z_qhqxOiDRE@75-e1Bt)%>U8A?AiqlJZ{>&z-1$IlQ!^x>A$FI2rd5AuUkR{!g)yOF~T?0(iU>? zUX9&*_O;UllZVAj^5fYO201x=h#9?f&fXnmDfu?3ezk?+9g=tOn@G6EVVo+XwCblX zN?X{GAoNme%LZ`XYsb0bcl9{!9QIDGy|3Jw%5_FGO2;z0CN#V^qViY7QkFnk+V~Qr zp#0_J7JE#~t*Y{2g8ivm_{>;u&>DJ;;>DoMYvE@0AA@7n4@EkZi-K8IsvXpG+ycs6nxTNY1?QtN zF6~b0cG!DjqW{ZO;(h-gj;i|d)$Ocw4)nX}1ec4SFRlOUyww|W$rf%3GC|p@Ug#Tg zzb*DtlQmX;Cl9VA-Aq{-P|phfShl{k@q3=vJhJ+T5Jmx9KL6pgv#pk>0n&`$6FRQ0 z23of2dLQ2v^oT+t@NiTSRqE|iPAo%*sV$as)bkN>7~h4l&P^90joh80GNKKPNy&U# zgsCi@=vyI;g9T9Ug!91`xLG7LYrG}%Bx&Y$v0)eow|78gBG{6L=mb$weP|P8R`V>% zA;F(fbWPhEVs3bIZLy%0gQ9!0i~>M{v1IQiN>i3Wc)3v+eNaO3`A^GWQ@mU`(CCSH z(!i}C7U@b_kJ^b=3m%BlV+nfX8;9@*k<8b+YdP8ss7cYzq3`b%)jjfe-SaYz95vYv+7haE#dMnjAo_%AleY>T5^MIM^GQsGF#d^*UK=xqUZGt3FAb0O_Q z*1l{DG)|BI_8zyw3jJUf2a;4+CQ(S(VcnTo1}#%hoz_VnCcd#;G~2H zzPh~`iJT8KJzSneKM#X0{~UKek;+4OG18M(jMF=K8+^|{U(J{yP$4ocFsX;HgK$P$ z$lr-t&p8P!XMSD~ARE-r8liifvwGBzL2kG6R*Ol>8nqv&7Nj8;I#Q9E_BC~>iA5@( zMTxN~*t*|L?IYO;S2kdud+96=xF^*o9(0{meSIOuAFEHRV0Fl73B9LxKoS0~+qaW0 z6QbmnFHz~MCW7cB^g|F~)_a$Q5-c%_{Z`fL-I(Jj5yqo{=Ri44deriSu9ySSsen3~ z&%K0Qk}dIN9{D-W+!usg!dB{6FE)JUo z>Ugs!z((+WhnV(Kvv;FHd$COglYwPf1;bmHrIs$LQbSJA^%9DGASMFpAo!tzv@(CHG{f2cqfK01p%xMv?_8{WoEIyIw|pcWa;{BTGH>7fnP z`?$T zHvCII>f{v623TwXftc?5;f0tCmITjD!b#&v394fdmP(G1!6cz(8dxk^W8`A|A-oH} z=*YJ68lj$w1f#ji>KLRWK>%8g=cqGm$p9l{)J6}3vd9*Ck01*|HRz-#Lo?@@MT*&y z5XbW*Fo1c)dGKO^aFxuMxfD@M1fii7D$YM!bndo0>?%!CJ@&Q^`2Cd4I@ojt+5vNZ z*_^y^W>p;?z9mLv2C*F;eNEt&G_$GJ7p-)rqsLd1U9mWdiq@Xzw*L*wYoJ zxQMRHu|t8&`n6GHJnt~tPd(K|?nh`N3|Uh)#HPrVhRM?hW`Z4ZL1MUg z9?&gYArbV`82hsHR}C4Lr$GoquU9vOe6z)~?{;#oZr@ELO878$`$PVLfxKkDXC)TE zavw`^xkOflD{>*rThRD%zIyn80@!rXtw1iTy!{6)ZA&XbtB!|y`C7cL<4uj`3Ic5_ z^?-lTAKfelqEqW@7$rnp8t%4vNqO1YH)|U_;ii=`6tj0$x>LcoGdeUi;zm=8c6C$| zy}3{Awf>>ljV72*2-hd~zKX18RM~LozKb`Etg3G{N&%(t{bJpK8abSw>a}fDf|ARV>gniUln$D}e=zMdFjVi>$5wBLRN8HXFuL!} z1}!v!O;U-^Zp-%>b%_KXzP4Wo&Z)KQ8ttQok}3;PGNw7RJUQ9yTMqoHET=S+x`*!q z)j4!}MOgss-ZUr{pRFQj+I_qW2nlh;S2=BU<^n`_zz&1zO0dBC#o94rlxj78-bM6ED@K9L}#4QvvE6i^}p z5tvG)2lNy*Sq&(NS~Q3LA{i3~IS3AQxzf?Y?Rgs>?ONmwa;w}-()p=IK;Z0V6rCe9 z!Q5=%H^i&ZN0%RA8ZC}t%FII7B*ws4?949_On@LeX09zG#<79OG27X0PMd?yA_8jI zCuRK`9(42X&qp2&i-HVq=PevZgACDw48vd=en0P;v8FapP-*M$S9*{QGpfZM3vOzs zbv|2BYpN*0OD$^eYkZIOg8UW1r6Ut+kDpyOT<@(1K9hTvz|Jv?-N>dCA{Q}ckI2ha ziU_7cXe!A0_53D-Tjzb$Ni%hyG%LlYy7oydX!`>{ceiR}EloJX)T6<3Rncjr$OgyB z(k_7{MD_6dR1p6X2J_xgS3PZO-)WJ87)IT^&v-CbJ?Q_PyC?8>R~C^dV(zQBkZwOLRrP zD-4FhpEu60N)gPO008r@T_*~Ct0W;$Fg}vcHo*w8emCZ24irS;TrHu!vdjIXM-Qn8 z$_vzAgULTtMGc99mm-JL&#ZiI1CE?V;jICCa~wSn;D+2a@H6dCcA_QX=;g4tqKki&<|;2;~6WjKfZCPgy|X6LkZ!9pocKP(Cd)17mt3^yXMPu+hziE+h*`J-&X*3K|M4Ysl$n=wzf4l}` zbM<|%3JYPff>n{F3=R=+#+`V!e#V*O4%aBJc1*SW5t#S1<~3rR3wuOco@+mS!=C&ZN0a*zLRSAb#G3sAdq{qt?IpwjyDF9Q zT<_u|CX?)BYVptf#?TNc4By8J;~lGdyT0}1!b-w&r7PtPjwwv)A_; zngbVnA4K-h;6pFABy3162^nrJfTwv+hXohscZB#AVlR`HuTuFl1@=V_O$b$L%Gn>u zfAPAHOFH(k;67kD|G>-s zLX%o;Vg56Izkyb2ecMh0%MXxCI|qwTm)6KegtpSp(x=R7r9BLkHaP)B6H+-YqyrXd z8=rNjubzGosSXty>DYXvabk!)9XCXh@#u87!D35`+G9l04a5IzhM=WW@HoX)m;`MH zl(FA{d*^sMbAGUNM#?@ahC)UU?OingTKi|(s6H_i zzQ^o%Mi40N#5wgevCjyyZqiG?NG-TUr}%30ru{RKwpZkd=-C947nr6H5xWuhls{{?_nFm`o@|eK*=L=CLfa_%|=XE~{jxmzCGL*sae!cmC zOI@%FtZG;va|#aEq`CcetNTw`-%b0I7z0F$sCfqADNqpbTkNAtmP_7*tIhca2^T+P zZgw3|spjW>61Sq1pAV{~zUJhz6vOd=E}yZ~Ed+6f#rTmzL5iV9=27Q#0rZM^$_;5t zW)s=zbS#7HrsIgA_w;5cFZ28F)Zi_>SwBDCpJMHz0k|*Jl3KJO%ML4|eJKoYSCQpN zltbxniYL^$o<-w}Q8Nd@qD850TRgd|#j6hK9 zI&1T9Tmz^`3I;%?Yr$q+A>mOib-9^UiudWfI*y}6#;-vWe}I1A;c!Wv07Y-jNDDhJ zJCiX5?-z5{JaXBZVo#tJO%KA`8b_z^S4}qysCqc@c2{q2M2ViU`18$gF5YGPhaEm{ z9gvH_zhzE%9~iLD{;z%~!01J+9mPE)%T($PYrv>kHB``v^otr?Z@7u>2*WH5k>)B-@mQrq&;c069u4n%0 zAJ5{cD&Zor4;(fL?@kSGbg$P_d>?_(TMPB)t5Bzl4bA0ko6-(9e!XZa`$mkV>t6k{ zohPTgC3W+GE@Ho*r5?o+uiRbk-35Z$*1ZpdGcK(}w$VIdPr@QWx&3Ujo}=MXRsN!6 zu!I_vK^~wM52StG?qB=4gYVs@ZoKw7C_$u{i?h*bU%ilvCv}cD;O#MYmuw1Mxk;s+ zWG--1mh|quo^ql>1+~{x#>m9C%*JBGe+aBBx<5~JSqvXVcI!p@^Aj*<)J!7;)eOwC zg`G+Iw{mB3Hmnw@AUzYRnzH++@_slP$SiE0ECg9bVj4xp(y*@v*`G`Dw0;-zWA09o zPFrmig4W!eoNOTO6jS&$MIrGipJ!ZAjS^4k&aZKhYkt1VEdqDr$kpT`c0;ot`=q)= z9u!KV8)#3ch`!Y=i}WyL%d)3{^_;4}_0Q)|4s6)g#s=8C3eYD$)Rl#C$~w**Jnnj_ z9C|g|uF3U$MtkayD+e4WCpFg^cgngZ;reKzZ@v!u>q6ar_tN!-@BzQy2~q0dG~>#9 z@pGZ(OE>WVHr-kyvJMPf>hj~}3%!h@#u2wzM#*ZULbFV0*h-FBRy1a@wv#Rf&GxU^ z*vAf7Jh&n@IF1&w78yy6Ih{QZ%>YW|yP3Q+ZVtp!Dj!XMGg5076@(F*ZCoQ@FYtdeHsg5{o4zE(99eF7Dr&A|p z{@-&Gp8sT(OL$arg>zUYAQ_WwoS;M9m!2&+6|wp-cGg^4<()h$yGt5;zvKr!Vf5uD zMN)P49wP4DM}EPjp5bE}!Zhpqgq1*x1>NAQcsAX4$NAoEnGw&$4&Z)aZ5L4RijCn- zXUoT`VmTNkV}>gM`k`%kY^$e7AVrXZ9+%w1-Ltc^^2jINyNC7H{R(F#q%R#Wx1t*i zk#73EN9lKZBKsG^H~_Q=WvN^I`X=Nsi}*$q*0k)S=e*T(_R<;oS0dd#yCsMD?i(7< zarG$!Lqx!fo}edA(+s4AUj@5|TI>_pMnFtnRs6{<>@_Mj@|a8Nh@UPfZuVc$%Xzjh zB`pn;oJ=Fx1v*;w4U$9;jcUUU!ODOi6*e@E*cQ^K{puP?168&XX>e{?| zWe~-Gy-DS`K6OP)=+fy#9JzcO$S87)VP!E8&*pJrV-U3Oy)h80)>%@w7+ke)sn* zS~|1Nm47sM(f@l$4E4jv$rhqep+UtT*xzTiOiEqNlaaYed%V6~QVWz^SSs}zjGV&? zfW}nLP=e$)Y&L1|Y?ax(zzVY5xSfmcq|dyI(%4eQv=Wboyc}*col)^oJpxhZ%igyH z`+o(Og5mXUF2xpPV?>&`?Q6@ zeHj>PWH(i?sc(Zve86OzYf+J{gUi`O<7nZMu0ior{Yi-xi>U`^ga=9lR9udIigNFK5dZCfu8UKFDbh>jVWwa-?!LcYu z=Z@i>kjU*}v~)kdpha$KQPbV0b$n$Xqih3I_ew6D5p1+r%8ZxUx)Q zhbSf2v#a&En-_vc6LUK|3m>}j8FJ^u>{`&4bnj^PiOxy^qhIE2)Y-JTEson|j8*R+ zO@grkyZ+G&|EnB+s-^vLuBtU^X4ZT?ZcsmJd%gs%u!wSR@3C#C<9VcG`%W#kYTub$ zs)Tv4FME``oG#yz$RsoDvKc5VM-cC2-LWiRVczsa%{qva@@mD!qIjXEMv)}|VI>HE zFL-zk&%;Vd6gD?#{J zpIAcH1<12;rRPXl|K+30J3YO30ql5YTJJ_XiK2+wec~R!=TRU#*-J0BLSe6DKSc88 zSom~knD=t954onQHMdq%pZP5PHxG$W_|5$TL6@|PSt}P@qOFg5y4@RRC>g(!LUU{N zY~>7CEfco8We1FLHSXsVrm=&|7bd8;(uDI7noXw{EppEOT-54}7n_&uUmJ6S*lotch_bs@a4{$g-Gg@xa&Ic`EeH?e&9BV)9<_Z&2_DuAr-Qi95?j&oktq_wa+OsYZu2<>jP@DJE`4y6BXD33Ft+xNr?M~?>dLqa*)^0 zZ?A{%Fqgci&ayHTZ)B-=YHEhu((lV!AF8rXgqFW4l)Z19MbzGD^O=X=)7dcK!|nu# zjG_5EJN?%DI|uue@G6z55O6wdMvUh z>g2e%D`yPW>aN&26Fu~grN6f+J1KT6Q6*4so&i=5{K_^^Tzm@5+we*r<;(M+V zqWdQ$^h;KZ&;Y(o%2aWh16#O*@TI_YnfMil6Z+73M@Gxqp2wkrVMHo7wpRt7zeJ78bZ6!?=wx?*VaDQ{WNb7jFTNh{qx-JoMN0-5lkj4iFaxEN@D;mpUr%%eaLsd}R9n9tVoJ%H+QL8$Om- z{^Z-8$&po4v@|3GVvs()XmrW$!jJOdU?f-UF?rRjBF+&|BWBDR&}v@8FPRzuQPwPn zmi~InJ^$%#f3R6y(dN*7F!?1R{f17=H~8#FO4Mxd0O3lDJ5BmUgvDCl(zzU3Sf<(WLuW_`#@YZUHXwD zoHdHrf5hR$?)UFhBxS{Q1vdtU*ShEm;A@ng^ zk8$Oj`(hWrTn?kax0D8#EgaIdsb^7W#}?qjV5f=sb9T5^oQV??@pm-T_C2b*BP5!> zF2}7Ng5N9?uLY7u`$^2DzM=s|LG)wZelV)RlV^S`{gv-_+#Cs;i?405ju`b9Fn9Ux}+pj+P;mxl==N&QZC)* z2%gdT?t?6q`gg$5*uo#bTs&#)vIN&!gr7L}ut;)#dufoxC^&R)2OKk~&;l5Nmm-n5WQcfQ3g^ zwKcSr9`}7^+h26Y=Zp{8H-5$a4ZVu&X>}Fy#NA^+0-Vi{l+1WUptG);Fc%OBE7I^o z+lT+A{h;q0AT$f+s62Xd!xiy$rR8e)&B9Q}&*yLpN!>TAqK-Ol7h;5{KfnLK=_5MM zsZ+nVdYOr)owkMzE9H`dbn>+lC<1wTrOcDHseEzYzEa>-m;>tusM0!m)fghl%vKlhm5QbuEp(}upX z+p)6My4&cx%csY-g1-=wlmql$`2*TueEO3Z!dzg+$-Lvu1B!v)1(0JWG)UBv!A3(n zXb&jND4w5vHv;ozt&NiFW=MV>?YG7s8r_I*n}@e|Zr`2jUy?d~<```-t-T2cU)3$TnV`-nM%hny-T1 zeIkg6Sb6v(R4Mk+2Z_l#LLM`fo~uYolv!A>KyoIFWfCf*e3`lkTjep(=690U+XLwEBIA7ul!|zLr zaJfCqIUjDjpdxnO|2|Ric4YO(sW&r2JM?LTQ~}hq;ihunZ1xnNF)cS%$}RSd;eK(Z zeF9qCX7I$nr>-9E+QIyOG{xky`#Q=>@`KQbH&_LUK6%)oJ`|<=^+E|Onq%hCcbTlA z>61{;nb~(rNGNgE`Tafd{eOn6f5cmo<(12=wN^*>gqy)3>VVug0vD#^__I*D>t@UK zL6wOuVg4XB-ihjGUC9+Zl=wISm<2sAAy4WEcGRv^uimE}vprwr063DY7~|3eVi5^g zZfWU(_uRfN+34Xt>KB-T=B36o-6v7EDLJ=>H2@oZn6?^;+U}O_(Cf<=P3WI{cQq zpyVk%RQ8qFXCnQ~Fh5q0e!9H!qwuTJPV?mT24t?%k}B)bY|l*GcCaSN#B19Fu8;8(_IT*MggDa+ed$K?4Si9bLnRCex5+BeSIYBUi z1=qLcp4NXgjTXMwc7|B$*>AZ(uN>(wwFeotJgl3q#LCZb5>gVn2z;9bcfb{V5tpsX zkrxgx017KIOR~QDzAqGEmK^MSC~IgqYoFjv?Kx2!WRgd)rim@|E4Ny3057*uxRd@MH|lFy7><0|8=R+aWj>&Jw1WY zN9fH@#~{s5g$xWw-?$B=^@MZ6l6J99U==+4KA*x1_0j9O7;17z0u2U0Uxp)HX^Xk3 z$gk7A1iM`hrfM*d3(GZ4u(B_}eyYe{H$3d9BnDCU6^q!1BK>xdyKP}%+MkmF2OkGV z%@r)coCgyI{`e-_akB&Busf<>Qt#fZ;u)oS{Oq>9=U{}q|wh+5M|3lSX zz4_CkV!wpirtmpTO-GCc%7uGNKpPW51&353!BH-99_!fm^Cs{TY*D8j;hJMB1Dyqd zu~hzYPMk#DO$bsATUz;-6Y{EJFXZ4q>2;fi8!g+JNX$pXFOU1!X`plVOP|ndD_OVc zirw!mTnk3Iun33Tb#~RmCrs~{L-+1--#(^P_bX|l8vD2l1QW)omEZi{3cwS*P|c@P z;IgQnID&?nf<& z?M)S><2c>-?69g!2Rp|tut(FP2yF}YXKV96xS#sDQ+qty`&@ex%JG2M&tZH1E{N|8 zWJD%q3c}yzS_HDvQO7K=bW&K|393yB*oJjoN}2s(zcsM)Y-pwsgQb#AWVa9(W$~>7 z<+&Y?)VqSCSq=k=A<8%S%=wQwa-vM%8@4Sty;{N#{c)8cYbjf=E~#nTP3IHnAW%B( zE7*EC6v-t4aJ)37#m#JhLoH>-SZ(eJAz_<{QkacsJ`l@2VfPD=X@sZ_B*ot zqXlS?trEbf$n5z!Vpr5xf8Q2VH>wTeS!i%cxK~L*s4f$eJJDItoZhO*fHG}dVxA&a zB4bm^97K2+(oPtQlaX>^6zdu`=1T37rn@7$&Gs%jL9LsY3*jE}%oGZvo22#cz~;@m zFJP4qKR*)q625nt0}dYOH2xKMb~<~3)GvpdXE-Cqx%yr09be;sf&?66$W zS{>>OFB+tfohp`h7PF4Mf+{ zi7Ef3ozarZw`rAMLkUUgNs}#6vYqIASr0ie#wr&-0ddAHTvic4vR82ozMV8n`30N5 zenv&aPb%d*W#}PlY06^x@F5>m$D5WvF-(Chr!LXM2LGIBLF26$z2g%rV_51?3o7tL zEm@jM%w`^U09WA`Gn^W%Of#;gl56X6r(k&bd+fAve(ji1tf804Nz9$W5iamd*q3QO zR@FLP2w52YVfFfuPT;D0xQ9tif0yj)LUq+M^+peH09E~k?Qt3n1-L!K-1FW?Gy^uD z|3k+tKYRXX;A8Yc(`IRSWFyi~%uCXTY3LVhGjY*GAh^QQgR*HfDf3NideW@v=uxtZ z*883a1|fpPiV`Wr(-Mvuv_9)@NWgx2i(l`j|qSf&MlN zK!H4nU_Wf4(l^}&V(cA9VCU$lo zkn<|DF8SQ4g#5YSQSJzbteocIf?hIhx>(tt^{4aCfnYKHZ6H0CVKHyhl7K8z`B^~= zYOXL=I5B)L;A-9R&s6I_y`I+Fk10cZ@3T@VN68+vlqbemk7_hJw-oO#pk$M~Qn|Q+ zqxwNAcGAkf?cM!aI)vo1z7Va7@vDCEJ6F>?5!@H4ZExN6X@t^3dVhBofER11kbalf zziiw_ji?y{g!Vt*eJ0<&&)3-#!yvQcpbw8XD=*e*15DLZ#Yom;ZVtkFHR~k;EAi#+XU{P`;Hee7M|tx zx}_|3S^T*OzjmtPp9Cx7`aj~DzW%IAoBYThsE+~K?-Ym^%M1Idl$FZNGMN9JDv`cj zZ*_bSEa%rA7R9U9ON~mSkxp=SLaOP7{3z0cRx%9zHt!^ykX)EQok|A`-=u@~-1Nkb zJBS;u;vT=mvZPm}gc9r_ddp-ra8w0O43?+koMfb&+3mljG$cwICwcDsoqcL666g4R zY$2W>K9MsAiQP+bEg;AG4oy>XmLq%M_8QANRT1;K>BwauB3pR`6mC|}xH-Q%PK%S6 z)GyY+!2=Qj#RA1Y=T;rfWYng}a6;cC{XeMXn_fwk?+2&^1ILe)$hscqA1*0G?wMi1 z*E+7}m*<3XP7`#oZ-*Z%kV;wH@_SO_QIDdeGH;(TJn6)~j$@r$=svAz+)yXy$8Z_$ zLU!^yeQ}~A`v9kuU0b!&VycouNaVarKVHvgqe<9HFbqg>Xw=?+_F`Z@e!J2BkVy!* zJ;jdbjQ{Cm_*4?-_;Hguwjdb5+SO9crNC@V-REV`jFWLQZ;?>zy=@iUBT4Lsy}bId zcP!m0L#+g3WaC4*YfH7@FQyl_L_exp+67m$>|hEh4*tFU=t58Pcwybp#H%d_e?#mR zZ$xawjJTT5o8O^fq^92ID@JjNUE`Ug!=GsWFO8nKB35K#;1F=$y%7auR+q7kfJXQF zlis&saJRNM3O^Kl@$ISE_WDLe&X0jKxcu#7C2G69Ec1`*9?F*4XcF+e#RW3PMBU+H zSTFKH?y11l4edlmiKDeVevXSiv|-zGAuxB<2R6+VdrvI-jm&l@&e3e28h~A=9tS$? zhd7$9@kWj@WLyqfX!Z$#Otr)%ka56fv*U-+ztpUG-s#tOOwfpjR|h>ub^i1r{`qE8 zz0d^G>&hq>SzIwp2F+ndHEsgMqyvf0Y?VAujXCQeAOFne{`>Y;p4=3Zn+JwR;BPzU zKYZIEq-!L6H2VoiyA~uB9@*-@!K6q{JD?GD8X&QV?sg<9!gk&iM>s*kC$3s17k z^2gC>nwRNEml8crEn0%h)Pv3{m{h`5o~a8=5NUZQAojg=imsru>{N%D1qPxuq0NlT z7ABQ1cu5wq`c}~N_+qycy~fprzBaaOFbCOsX)r^uH_L6vVW%l8kFPJE7;RQbg*g(G zYdgyBD;^LEE85LAkvaCLxFz>SpoelOr>qCtn~!_qIPt*R)w0>6uP5J-Zf!a4J6qQf z=k~0);wP5K2LEF_AT9exG2LNJbXLQA+|W~}LO+OV0*!Cgo~#;Heq|yfBBRO+3ni1- znmB9EG7%!WD}ko+Nzwa#7a*U< zP2jyLzP%cDaLm}N*{^Qlm_7=-@s15tgJ!e}k}yiOR8=o9ifveJR^EN<+pA3xp%0iVfdNmFm=xSOl6X`#ilD(GAqITc|j@3 zrPnV1VcBkln0zDXi`|$4cO$6@z>1DCb_^E| zJOnUS68F`zjI*&%2UwJsrN)e z2brHS&P{TX-!9BtwOB!BWK;+eBjQz!$7Q6DUvPB>@43LYW@*A-K1uo>@R7Xx8838! zZr(=N6~NILAeh}YyC0AWnO)E*QK*IC%Tt3-4!u**l=74bzITpC@ys)=oWkt z`G)JYFx3AQQ4m-ozM$rcGH-fX;%G0=QsW(oe)q`k70H@FtrsR;{Z47q!J)FRtyPL{ zw8*_-ua2_`Sqj>-)sSZ3VGyV`PiV-365Vxsnw4E;x27`cQLKs#;A;&O^xosr2_xZQ zjoHo`@J*cxEXfuzbr5*n;DVjlyK<0D?0GJ4V-0ZhXsexq>~#H2t^~A@{Hq6CXTha$ z4h?tDY4}uhM6F(Tt`%gClvj4dyq@UUIhw-NRIC(=>d`}vl_sTS&Lm|TvOWq%8D?ZC zO15#2>dt+8E5SLFTAY9dc(dl#s$VH`V3Y0=H`8CAZLcq7C9 ztks^IE3a8;QeJ!ngp~X-^(p_)w454+PBG5`Jo#Sa5~G1~1VNYutDZ-WFL|?Hj4~73 zz|kN{#zw3EjT-|L|By@tXDCu{zCl`YXzTm(c91fg?Kbx1MEHB)!HGuC=)U(5G5X4L zf^hKNO4ptL#U=Hcfc2>IfW79ZK$3zXCsB@2J?}wF?*XT|xt~lu;0t&79^UdgQ;ytW zyE`5|J$=4V8`5E^sd^|d5Ps9GZ1rhz?DwcPmV}=&uk{=7^XedaL)^@onajC`9)B@@ z3om4?!$IP&RP-fuB4U_TD-0)JRVVks(h=oRE1*0Swve==Am83atgr2e`{8>3Zz4pp zJoVu}23x15tYWJN@1sP|N6dYO3T~bWvLzPMF%sshp#?Qh*yLsdQE_UKQwKuG-17pL zOH8umJOfiy_c1K+$V>D}+`Wr1Q!K!oNr&fK6vB3tgZYK)+p67+yG{@XO^$vOcvX4v zl5y6Tc`}mCOeQv4Ry-^F)fb?0o&psespj!5=F#}4c)zmM3bXmohs^k zHz{3QlY6KASq$}86!+VI7wJDYxhwMAJQb3C^JN!;(6{-hKuWkZfAVRJ%rImOyMMjA zKgngt+v!{9^XQG5PYmhKPX3*&`!$V`dsiyhWj7UmwQwh|xw+d3nKkpbPZTw1^Nc9& zK@*JQ-df>kjMBY71v$%apYxvy)xUYx&KZ%sVh|n~8D%BUc657oJGZ}Iv!{8}Zev3a zU9uXLu^E*mPJbGZ^)3gZH8L^$B4Y{mc`N;l7jK)(a#X&RqKx3YM1>V;;Iz;0M0>K> z#1pCa8>$^t20xsg@3?$j%ZBwU2Es<77=A`G_#Wv2ea*zt zUCm~nI7xo`X?@rEtld12p&AAtVVZZyaUlPFtB(k~d93Xes(lgkG~lWuS-uAh>; z{9+9y@u68pVN2GA$bqjC+74dm9V98U*`38m6Lr1x-3;L!r~VKJViOB>37tVkJm|0e znk4~ppVDgA{9eJHgJ&){nQ>Lz=gr>)lu@iL=h27%jyf?}d(I5 zC%6_s-x21$g14AyKne1EXxQM%INBI5<+oDXC!g(_h*Xyq^wz3)gxzqGc3ODhuAcK|y|B4^2qnaSe`>(b&-Y(6!i$MJ3) zK@tXRZcb=~@5KOzRY&HxTz^zWzn2*vBH1NWcYnJu&=aVIQJ!BdHogKoPg2$Cm`r_S z@Vj4g<(yPWTgX7uz>QMuppJILH^u0_CHsW{(fbHmh0*gQHJ;B+;jrxV6o`_d#YQQNZnQ@twwB-?@cE@>{+(;YMmH8H$^Tos5SZuVa6z6#%jFz?iCaDoPP zt^!CUHf`^?z^wSncKb1gsr$w(pT8~2{WOx@vsU|P5eK$lV2!OL8bSnStYie+h1c>D z(7$Lt%=h&wGd;a>@I49^aVG&V7nyof@9gBgVdK@^JdbB+_p8fl*fmkQHNp8f&^?*4 zyOQuKJK}Vs_o&Wq2V8jG`tzm1k@U3lvgBl5`WN=Xg9e}@qb%tUZN~{@b@Z#WukT~( zJhegpe<_Hyxc$Vo>`2i?-9XRlPkoCsCF;Bs-gVoA(5%d3QinZZ~(R&}VzK zVc4g8*H)*)*U+08ktgv^%RF|(m>R7<LV@1Cw^ ze*{2*+s-A#j(_#a;=adz>@B^sw%m)yM|bkjNufRuxel$_&T!a!I|rqZ7Ao_M!~L}| zpW;X5PDt$GEWq%Qj0T_##~UQ;b7i2Xn;PA!CHR#`8t=ao#Niz3{AJ# zpdB+9_%$(F1#2(#WqQtE@zsHu^9Z_@wtiF@a?gf1N%a4!9EY-(PQ3G`!bSc@CPu7< z8r~7#IhD=qS+uNPIi0VBXo4@EvdQ-no08U}*STp&)xhsECkYSO-5cj|l1G(6B69_@ z5E|DEWrHqPWB(>LE$Vti{ao`I-OXyX7VF@)xWk#GBk}(?8dhf|L#Wl#8eFdnITH&H z6fICs)u8-pGI~{QLM>B2weI&CkGxm1@?u(D)jbv_{GrC`5Tui$1*0G`dW_q3du~aY zU3xTSFqG8iD)|g!bLSV!MC&c1?<3v1Crz&N?n_QozH8`ZBy|0db8}0{q53(wRYFLC zDQQjQNSfWJ#u;HcguGP>gt?OsO*W*fRc8Ol?yP3xL7&0GQSYmz@^8YDIf%bQvZ1HG z3h@RO6orrgb3LnFLu1VTsOS#A!Rx0NZIl!-UMOf zE^C3KvT8c$jQv8#!D!`VbVwIPxAo<;;8Zd>g=U#OW{djJ{#u$?*fxj}2QWQbRssPR zAN)O*^P&1rRmnRK{Y)$<6Qs-oDgkje<(8g`sprOhnCDV_&T%gZ7+-9!U;Tr)9`4VO zNgDfsa}w!@v(XaJw&6wip z2wiT(x0TK=OJRWhoF~=db%?{_j@#nFnnUMQPV^|Xt5MgRi%_71EI53D=40hmfC;~0 zQIG2=0ift0oih$tAY;+gzWOyc?8vowm`yY6$TD!qq7JX;)}1gT?Ei4}-r;QU-}`v1 zDr!~iO>5OCYHx}Td(<8ktEd$r_NY~})848mHL7aw5w)pRBS=~+BqE6I=kxS=`u<+O z{FD5}b)DDKFFj0IV7iqKIDh8oVXMu5#KVa>Si7R{nIlHn&bw6GHSyBne?8A!L)s<$qCxL=} zqjWcVtm~Fwv@hrlMKoWuOYyi6zq*^Wb=4IWuVsgmIyO^W@<%;&#cR(72|icrTGmgV ze7$zYf4Mq)!HaHn$4{cz>vy1M!yOlWLP11A_v$tT?A+15ke}6z^z-ijAWgMbLRq*SsrAP2B_aUve_Q!km(n0un5S4p4cHl|2&h@QJ66Yl84?Qm*qNw$cJURx zp8si?E_FTd`^S@WNrj8TbX))aKm`isJ8y42d~@sJ9><%g;H&R9x4w(_Iq*{XHrfSk zmS~Ju9r&et0c8bkwI`^()p8tU3(eczs@!11)3|v-Rim>Ms%5vXZ4#TMes4oR5$811 zMcpK<>`0UCS@W4T7-(8*+3(^;(7rL$L2jHoCL7*ycBdoW>xh5Z6-m3D)|d5+!H=%! z*j6Sos6kl%2{sTg)=Pzd-K}}e0pNozfAlV@(CLwLAxb%I&U~IYC2Vv1p(L#VK<2{?M&lLV?yqq!5 z$(w6`I@7JJS#iWrAJ;JEK&UQLy-r+?Vy+GpXH(G6GrynIF}D>q;?o+gi4=I&2C`s) zbY1mKK)@#8=mWl_Z$t2@bq6>NNhP5Fg?fIuA71ql~7>&mDw`@NQ z1~pWoicTqa2~CnD5=M`?=X>zt6UXEzZdE}`gT|vbk7p^#(Aj?fnx{4H!%E&ie&rj| zbU02V)^WDFF>~u{290@wbS$vU+h)YVN*Ch7MzPd+TICNJN`L=YM&4J@BlS9s!um&t z_aMElFR``T6p15jc%WpUZvVn~2?wtEvEZCZGTTe_OY21Ks5xtc>!F+J7rctwPt)^# zsI0t>hQ~q#;RjU#$*!z`;+Gp=gd@zuf-z`EmtxaR*Uwhhnl--wD!}K}4k{O#hZJN( z4oEL}h?!qu^G2xfP!ZZ+euY`)hl29X?fl4w^J%+0?7VR<_Dl5{i(RSkd zHdYIrZF4p7xSYBB@mZeF>IrveMZ&mJ*KtJek_%J0z;vXF9p#9zbr@&Nh`$%-XkM~H%{-W1L`-rQ ze7c-Q47qDkg3e||;kr#0l>^ZK#f^4CTYake4f zE!KTfUtBJ{`p2c-R6gud`p0m;j(mS`liGN%zQESR**N^1MikY5PoZ~Twd(6B(!=e} zhv6RGlrwz4JP=OOxg~c~6 z7=d>>4W`y?*W|M43y$RbPTL#qi3f+Z_PfVmQnXnD3rWZM#h@O*?ad9baeh;(Uj>)a zw;qzb=+^s>3-C`ID}DQ4tjGT5GTeFdSWHHI+Gfo!%QzwlvD%)^I6lS>VI=(7`6fy} zmAPpFa!x(%8J^b8KnJ=($y!a-P}4TbSGgshHZ7N_HU>@>pe7p;D5NxMakc*Ln1Z(? zX8?m{i$F-FwlkFN%eVa1DyW>W%^|y3nX@TY57CnWNlu<7zx`3?Pb4KzMm#yuUd+ds z2(md{UKa+sUoq-;apNxr+736CWqKggVmE{zvwDe+R5|e|AU!R;G(0eBIqC3Pcgm3> z=X<`+GGE|ka=3F}{u&D?vR ziSFk60Nlt%6?GgQ(tk`8F?RVztmmbBbsf(8 zbGLGhlK<2RGyCt5FAkz4*<}WjaUAU`nc1TUd7r}c)&)cObsCo~Kr=ItWgpBA#THT> zu}s#ywW+h-j?{i^A>+la9(Hsi_0B3sGr%lQHgM*65g8lUqJ&%1{l`BaqTl{6Ad)tj zS2yknfsa243`3L3p&pMtgdWZYd8m^&dXFAauK3}-fz`(o#qA7hp!@v}-Cq;1PVsPK z>;`2Ljy}H3N{N9eHiEgoTY!e%qCSYroRJl=Y)C9}*w3CCs;G?Z%ZJPahMp>U^})in zRXVNUXAEs(iE(OzJ%v@8_25oOQuA{>^XGcfI4@8>zqH^SWF9(xfaxzynGS z`D*O3nA%g2F{btYy60YBf6rR^-F3OXKBl?};e~9`Gf#E@yXr(W1Fu)E9Z&-nTdUr`YtfA44h3z*8PpiAz8%JC1CUW&^L*y<7{3=jQWFKzv6gl(;@ zVU>*(usLr6sRd>3h)%{Ch^{%58s~~aYqGEK7o38Oaf$2aJmP!B$*!Yr?inpQ0=bMxmO@n^(kAxnj2=&&o zvaydJg)POEV}tYiO3dRO@)RkQ-jW>z^Ic5al0>*ycdS<8g04wC;ro-4l(nbAYYN#gR@E^Tl!&lrL|&kRdufDfT!It3h@zA{ysLK-24B3(uexi1RpfB?vJnX#gWHmfAOC(T1j+p_MmylFV^QH|E}0v z`TfB%;korKjTbJN9u$yX&FES8|i#zj1+*haZ*>=9Z7R<#?PEW(roN_?3 z4D1z}6B|K6(n?4-fV~An2m;n|z4|hsS^>rOTxK2letFL_rxAU^ZBNvU{t&Cwl);;? z=f8IHmQZAIx16*}0bsIeepI0D{T_#$2bv~|3A6MO-dNFtpvBnVR`v!nKpHO*M~!iy z+W0CwAACPRMN50kxaKp*r6ZY2Sv_KMXtbRg{~+$)ZZBB%Z&Y~uqW5cHU6+%YH5q{S zNi1PdQV4-bM3Seqw4Rfrj)x+P=B9vzmX9G|*qC|L-qvabdsibv#!$S!#bAn)(xgx` zQj=J>7l-IE-_V;`^n!e=dnVMIGhXDNSWvWOeZZzIVoeP{`$Fb@{POKlzt=gHwfDt~ zQTf@93)V|Y&J*sjp8Sr@F|%q(4lQBhu-;np;Tt42;)U#dpBTA*HDuc_vjCpk56Y%b zyB8(vS-H~y$F7by`!jn6dzF0x{m`mNYI_YIpLakB@G68^%*S8d!Pd*|UcM|#?MuCc zamt|-6WYCM`p0rOO-^P6Roj(|g7MkH$pKA0nT_3cQf<|83 zsrRS##qJ#pxQd8b@IQ>=)qjS?{^G)K-A-4s2Hhpjvr7prG-7=q-DuT@VW;c!p&bEN zYx0T<@bt^lwDWbkrVXmF5lOD}<@761M&NahyhRgoXuxX$kS60-CB||I2XCbwEt!NC zC!>Ny8#*!EU-sK?>M?_G?_3lq?Dn1P5-mqB#&%UskjpS|9C$@j*!h5fcLebkt2;(r0WBH$1RBBheest~^8bRDbTmQeI1ub|rXEr?-f@ zL*cYqT3rv}DWw1hTaXp08Gpk|2_cHGGp91#v-*Stew8^Zx5%DNF zq1N?`L`%kMh; zs-MdIPXBQxg+71lT2n<$hq-gG3qy!spOsedqRg+B4qOYibv;zz;z<8ur4kGPa@Bu(H!w1;3CfO|fs8yvY})xMLBjam-}UhIj42cDeX zEVj)^rb^7&Xg*`C`CUWz-<=F+}*lfi-%9>B)QO;JRck!V5U105zBKp zS8ge0S4RD?OAre@W<*X+w2TE|NJ#Pzo9q%8!@C$!dGkYy>v>4ds_b}$7Us`Io&n1; zwHb7H4*X5r)w0f{Cv3a&c!MoL@jiu6*ocjHK3KmujD&Te?rr&@i(9lj+x}eeBjS^v z&kqJz$d5oO*(jATeu9->2c)3~57+*MmK0Tuy!HR-66C^iuB6ksN%LCQEQNVCXad~g zWN(m1PT{5=+CC`?^82jO`tTNskvsy_tCe)Euxt<~mH4_aV*saNat|-;srsRMujTaB zym*%TWJzmn{~O>C!5F((i9Ic&1hUbJQD?e(;@17&sx~{uG|F-xse{m@|CgKDWiQVt zFks5C!*zIsOUR1k?&U@2B{&~e{$r8XrI&5Zudvwu`tDZPY~=x5C%OUD*9&Q{(z{f? zALKkhq-s^k?d}>of5>+Hi&Ev_nj(@Y|7G}j^J#mkrBM4Wz1@rO-DmmC66a3atOPX~ z^CPbL+!90}jRZfxjpi?r&pqxB5>7OVHd41TswT-LP(f{infnCVzLjxNFKeIK3g67# za-u))hTgVcA~t6~b!z3%#OoeBD9Ko+f(}o*4ey?Itj(il)DH2w|D}EESAQQrXR7Re zXGL|38n07=_TcbuzXp+-6jXebZ={PJ?u8#0wTpsc^9w!ugm=RMq>ImnvAf|*Ic?eY zzmo}X7psyd$Ys|*bMt6t0eSm}hUNp|fnv88?_}Ge>!OiX)G6VKbTUybi-zE$X56U zZh@J$N73K8&%ISgrJ_?0zPHw}ayx(T+0jO77(p0@I_y+Ab!ph@tfxAZKJW5Md-gES zJbIL-xHfD{4HF!iH{%3UWTNCH6#g7MY=E_f^fwFJ#P)=43jcEDxV%|4T{r@^nxm+r zf6~tsHNXLDx3hPsoG|Hbzhj$$I5H1GG;L%B_`s_R*Kz#f=75_7qY4;B>$936G?<-` z^s<5HPc$|BpNB5`45*bbSci_(12VuiQ>J$~>pS}lCz})Tid7m{3c80mJqB0_IH*>DIiVAq*8(Wj z87QGqq*rZT>i~P8lcWhag_>2+=oai!_32Z^tr-POfbJhRRYtzpHMKjOmW6Chg>u~a z;#u|RskOa6q40D42h_=?J6gRw4qCXWuuDRP%{Ju1<*_^VD?V7>_7Y>um?t7Dd9^qw zVtXnUPQ!V2mb$Oi6lz1W`U7Yk_uV$W3VdDo?0QAiATY&BK7VRl{ZYel)+1(~5J8U3 zqL`aW$L>XlGh*gbmhsXXjWD6Q^9n}i-o+EC?U;kC@(tw#Q{QG#UT`oih7QoekM3yN z0JDA`8YmNca?kv}_s`$d9r@F&Z2QXzePN?RHi^3Z4I2KN5vCW8asr5nt=$^fOqPZjzIh06mJ7O%VwLI>ES zr175Nye^#;kJ0!pm5Z$KsH@6oJxoQi$_ee~Rl{Pv?a?oUiY<*J-*yc6+T-4FP!F9y zRc5!lcwgOlmFrOFVQXl96-t#|4V4sam9;8v9n@KH)rIxa@3we`dko318x9yvfPPkz zRV4VGIj^;A`R;1&?Af1U-Zg#F=|DMH>~bfNh_>Sd69`4F3m^7Z4Y^@z=6Xw60RdIG z(3H}Qme5b_+HSIZV$n-ZX-}>>lq|O+qG{r)7k-Mx3p_BspDYUPES6}79{R^fwF5`E@v&(V zMVCLDDNM4>4=x z9=sW_Fmlaq@1VN(PWuTfT4t6V06$BUS(REo6H?qH)9+3XSxrY=%`ZNlo)2F>K)2y- zTlY|xg7DLam}Ifj1hW0N8D3o~t)k+y8d>+8}YSuv$NWV>CGT`!dTEGu?DGQDZ+?2;)kHQlXm9<$MoosjZ4v1GUor z${!~s&bTzA=K2#0j`LxyOQQf!m0xr2RV}tiIRT?<>;6ekU!r}q+6{IWOc*oW`vx~f zylLNO?ue6e+*PxM>Y1qsPV&ab19>xlj&x8AkFX^WL{OBGs9ldEYcRn2(sl_45y+=x zZ?u&m*^6sk2x`QiLKO%`^yco@Na6kHA|Dtw_y?r7!aK;@InQ2m#w>7v8Er4*@?S2W zCEK+xLxNxmn67|uOaS8a@;4sw9di!6V*HJDEuRO6lQJrATxgzlhz@;OQ8^nA??hdU zpmymlH~Ft7jvVN3Dn5vy*6oURs(_B=A7)M4!lUs}#mA8e3*soYcuyaZSOE8zl1AQk zh>e#PJ=Um2G~@Baxko5LQRDOP!Ww7JZ{gxo2KS48GHar3r_6xS_6~4QSC=FjL^}#5> zFCvsXGNqwiu(s>@ha`4M&2nDx0}J0Z-Bl%5$aqZzh8Z^nN|H)%FiglYGx%kcj3Ne2 z?kQh4*XDo(t2=Nw1gq5z;(#K_U$;J8GWT?{6nfufI!Zuo8br`Q$eXWdQA#ll= z)Uur1G4qrZ+pEPUxj(=c)8Cmpua5p#FqxU6Nx)}5IOArM5-YWPe9^6UGo_pq<>cF~R^DKVZZ`4@8mjE=eJMeN|RDM<%)Ad&0jLfzPU$lKexrGz1RVQjs z%+=CMSYIx&>~ly-JJH`UDPIwdpr`R$Zu=F(87p&noX`HnWBD5!d)vknkt{CcmQ-1xd7jv2i-zGViK97{qM{15gLqmU(;aMml#;F7of%9d;$2icc9uWnmouN*=0 ztS|fW#^rIx+5Xl+R1}41BMo))Zdc<PH0YsdMoz@sT=`)VE~A@I$$~%jf5nY&2a=1t199Y`!B~2MtlHUz|`03 zglZtr6Yy8m#jA-M_4e%8o4hWc+8R3X*YWO-5-tiL`!w|Nfv^w+IK_iJtFu_4AhxNL zb<{g!QZRn3CW&n;NWu)id^^h~)X2HNob>jXEngN_UafxY_q&s zVsz#{-f}-6p~R+_ZCl7lZ}(#^8>16`b<`!AG(-mg{wgBhpR=@-M^aG0nMkY12;(Pd zJT1q>U7iZ6B;=pJ!fy+n<*#wmlfxuEMU@W_C66nm%DMHasTk&FsLI%GRh zng0I_$yELPiT>S6yb)u#gz8F^RBDNV4buG(OfufX0dOduR-Q>je9KU1dnxlZdBk#4 z*u}r~z=hr7wAqe0dyD7rCb6C2Dap;6&L#tbp3HtSnl6$gprgCeFdJ6MjKF2 zlc|FR9im*-P+51Df+B%eLJbxzP>P7>QrvkwE!Pb`{Wfr?Ga|2kZ05sSL?~43&IkS` z#xrLQ9Pz%%NcT*a>vqXTlNFJ3OFDVOWB1W!8P$@NM>fjxhXiBuRU~sdPQC5I>~o_+ zorEm95B#Vz*-IN<+U$i zC+hvOx00UChN|ct+N{XB6Zq@e?2?3~w(6f-tJNRJ7qy_h#DIPTo*})w)Hah807b%1 zFSe7v8S}q>4;CHR(srZsrAA5{x<6ZdR-)JGb-lpg93^?OU#3?fk2r}zXEEsB3oET2 z)4BDQQ0K2aM&LMa$xkVv}$0oM*7=MH;0-()}XIJBQ^` zMK?4TUn-l8Z0EU|ru)yHR!W;K=?!S*6DEvpJ%F1O;OoJ`i)N?{clHUMIFL%5kwuH+9%@M`!p6ma_|UwpQH^JKhSnscK)-Y8^XmQ@KEEhuz=^h3$aG_N$uK zqw&i+T%7uk=GaEm1U|ZlDn0~2Qc@T9yeq)9gfu3NDLz{43|-yJ&-160x>TBxEv_EJ zSKC8BnyV(vFJ}Nizw7r0ne<<*V^XbyvMCbKg!bxPdXCo-$UGXPy|uT?{m~TGT*^ev z#D~I%Nu`>N4Luy|^&c(0dx||3{GJhrOqKjcaRK~g1#wc1I<#-5SrL?Rk(a5;^N19P z;=TysgN=zNs)e@C*TzE^%1GD=_NAvM|0(SAEw{~}$TuhBhNtL&xVt(0vcD-D zZzoC25Z5%_ACOH?i7i5IdAuvf`yWWnBKq<#HIJsoE*)Ao^)D4J{B_?t{l^6up$_2; z+tUtb>NrMqoXKSMgz;5%tTHJ6euhb<4!fY(jkn@#3(^N{rzu;nvnrn(03LLGJFyg- zIeUjP@0M%4*t#`)`jneb>kJ@a>+%Aw=@^fiAX7GbPS#n8D*@H3NSn^)BjiZ}m3O%8 z85HsFle)VdgX2zXyAd(lCvTN-oq2$_xgp<5RX0vb>DmJhDQjvwgKz50oCg%XRq`k^ zqDbgN0K)WzovlL;re!a?Nw4OHAu#U8q5KgTm0z9z)`I^VYsS4G#v4(1HvgVkeKgaU zEWtKoEGVEE+alZC^;j@tRnM<|X4{&TZk|G)jpbbxTiadIAf{O!(JY*#q^1?!QcGDP zN6C%I0ORS0I*JQRxqV6VfUBOca`nUYgm;D>a{+MNleMkd+okL-(?p>+Rkgl_&?$Yd zYHA5gqO&yt_)%s_?R;QT=(4TrvJ2dE3mJ3?=dj(M9P3lKLd`pl@|Pp*E@CbdmQRxH zdN|L}UP%4RU)0MW(zD;xDv>C!MrN61-#h3=R4sJ)ZC+A^p0(VJh*pPpo?Mk>M6u}H z`1|L_2OePy0vQX7Nu?CMmurdvSo8PdGRWp(r&&pZVFq#SwwI_jl#b+~3&=+z0-f%R z(l-oKq~!J#9UWW_|EfhsHo=$ znD=2yL$Y3eKY2s6SVtI6M@_6D2$r)844b3%*neC&(!YE`>?+s9_NrM9SU|%l7mqZ!xw|IM7xD2>J4`X_rc*Zaz(M27HDL8^=Vy@*&Hg;J#w`JnN&N zZPP0km5_X#M;KOn9Y{ClzIyg)ZZvrFkOQE2#Lc++Y%E9`j>#P37~M3+1o4OMq=(UZ z9Vd2lE-asz&GC%es?sgIM13$OIT-=JO#2pAmNe+@4 z#k!6ivE;oyP1cO!K!5KG5?Sm$e=w1o?{tfb!q_r?vUa;UiRpt4{)mNa{VDI0DoJ*< z-#$2GF|JRqNzte^H^Ry?P5t@*#~coF+QB*gobt3TqG6mJkH zHy@2<;>k+XHiabo_t{loj`Cs{eGeTwRq_%#P2K4&v+1ehS5 z$$_E-8~oJ4Gb9M$AA(|M&kMBuTqDl%UE)I9WQx&M<4FdMqFI48l$+xej*&#?6CQix zNxRaJg=?+u-~2r!-1=VX9)S97H?ll!6SPb3ek3(0JSn62hP2%;Cqq+{_fOc^7P_;~ z)qtSl(3N|XtsP0owME>tai?1v5V&ASW3Td9Wsnm7SvNnF>YEL9Pte*=34kc3K zzM>>}-Z-d49^vE5YjG5dF=Mg;hn!_4l{2v@g}jf~L1HA07ya40#wsZqvT|Mx=_1v`2l+^5aa=SwjTNywU;+}e5#WK(|kaG>l*hKAt{U-wOUhY3Z1+Z|d2%(870Yu@td zMWcrX<^XlQU~f7i(GNzip7RZUYeS}?tMJhLr`P>evN)+-3wyHbzi@RedNa<;rMFa} z0~2O$Ufg@i-Lp4(dUD}lY$P364uox(nc{b zMvG9VZK+Y*$28No;Xr&&wi(eU)VB(li`{F z=WHVDabf+oZkx5boov$&i8gWWBd73X$y0riD=!a6sPP8R2~V$ahxpfL1fRNebQbl| zOq41%irpXkx#1AUShDs7o+zvefpV*<7Z%0XUQca3kaqtXM5C{~|6c;r`Jd5V^nFo4 zhYY*=Ka33A>>SM*tT(4KUGXbi+8YprqTK_5`4v~Y_s~pTxNvuj=jQilBYK*12OtkK zNzVdMG_(p@m!ep0ih+ z@f1YZ9Y)5=+rQy$d&f^%jw&zTM-SkAalDR9pqpL!-w-I`zlfMZiU}Q1OIJF3WbOCe z$u(4Do}p~_Gwp4T1s?Bp$<_74r8c1u^5*MlRk~c8QDS{dV-YR0g47b}Sog zz?qRTE3r++w^Mg+?f4M4NRZkgco+vW-hj})rM)G3k+Q+ysvN*bJ|U46;;W}Um1vV+ z<;nmgZ>8y!Q*;A{*~sSMS)TcZy2WP*7PMGRN)Eugcs^r5Mg=vdS;JPn6#3XH!emWp6J9y_jWc`!O7-A88{ngIOxR5UoH=spG@2d}Z~3 z?C<>1vwMLl;NOJjRYWq2P)Vv^4{`nq>_OiD1i0@$xBufRfpY>|L}bTvgO(^|s7o<4 zsUuHE*WTT391$!a49ZD>yFc~#C4>r-=p0zv@Lambxae4dxX#fXjZ!q$J z$z4M&t5!9U>_@HO9oC5cZj$CP((IgsN~HM{_h&J0&MWah4?o6_DUE5UvDNTZ^hqwZ z(0giu?XluXm>29ab+UBCIMa`cA;ZF2-a4()>xG?b@kAhEx_t$n-H+{8pVXfl>A0wx zDq-R&%e(X|Y1+$?M1j|-wuxTq> zq1k?kX}z|h{-u4*V!uK*b63u}b(U+8aO+p;eO=m)Os*C`Mn}1Hp{<}7)hbn`w6yh6urL7!5E}q^n;j|*%q?P`geu^ zgY?!LwRy;eDT)4`)77()C&C#O>#ryj9pc^fv=s|#l^kmy6wfH6rO>uWA|h*2m?oQS zm(y=x7bqUH!9Ehc3bTwk$qlmAX#%-hS4=w)w$qQei|~Ag4d66&?5AvmFhkN_a~Uv2e9#bKCpJOIDCRPZFSz zhB|r}Vxc|gs@0ItaTBOdp6)?d?&%*ut{wR<#SefaO<2;IDj>?O+JVoGSoNuF>E6~L zhy=~24!*=Q*_?)A0%pSw;^q{e9QFn`cLXZ1QG9K4-1WUUR>lj_M7zO3b7sQM-gcW=&~`bM*-F=*HsQ%om6s|b1+VXUkhfgKRxVw% zdkPy&O@C^(9}8SL?!9KABW%~qNT7JRv<~VJn2ZkZ&1$y1kq3H&JzYBb*cpE5k!{{v8L5SCbsZj0d zt67tP+OiB`wDd-Cyo2d&8$_2ZR;pWxUyvCmv*r&X>cGB>TK7+!Nacz zrj=<-6KZ%ly}*K|}0Je@r-AYjIArDaIC z5#e`lqGQv!schf_#I!{B302t!5130*l554A-556QUeWphY~xP2@k?rL$S` z9@T^jwt+S+J13+>D+sT#c3vqo;dtQpWEhA5OJ;ouWWXyW`D4c@Ez+(vIG&Ayw%D+u zY1Ks`&rpS8X?K^O!A%fkmvk1Iu*;X`C2SOnQu$^{^II-`;=A441`|pkxBQ89Ku?z( zdh=hhKojTRp)6kR!L61nkwR|G%fJV;_Y%@H%3e?`0{cwOr)LG4^&ujvKg5Lr-xwsS z){6kv{mVf5z$LdTSI7WqfP}h+VX#kC9!L~mi5K3^t?Ua-5$OlNVxLom{hJSuzjif}rdSA^kW0rzK?j7jPQ_qP7 zEWvvBy7>e~eDbyxJ%%5xW`c=Z!WnozB4Dp}KAZ!f=jLpyp*Z;p)WXpn>+#Z-ywiAn z2owur#WPr$X|e5S58pL%-g%QM{*d7%DCXUOuFP^2QBxq?lQV4NKQQhuFKG1X+Rh;l zpn=nOTQ?Q@>rp58^}HrR6y2XJF4R-VzW0%rMASpf=HB{Jkv5Ap&ByVw*Ss9hjryfb za~6qTgEvLBz}6$z%rtVyRz{uecZ~9T)NZn^vMI#4leh7%Tc5!dCwo$A^-{cgb4LM;D*jRCU*omgYs3Xrwf&fJPYMH1rxCgie2Tmdd~|`w_agJO zmq9a^CSMJAJZhy&Hu5qnTxP|d?7P}%`t@Jd)EIYG9z)c)n}-9GDhcj>Ie97l>p(6$ zL5bvPzTI~7A`wdi(LP)Xt$EZNom497;1g@H!P9BzqTF`wf%<+t?}Z+WZ3n<^vXCrb zd!BOot~YVsIx9>JRV1@NbxE*mHhL0lOe`S$U=A;;9O#1zUWy^@T>4*8to2`gw=S*U zEl+q>xflck0V+xKpMNorMSUwr$RL#M9SWU|t$;e8;ZsI>UJTzAy)&%LNVF~FG6ml; zmhm=p^2%@Yew=G5cZbQu41IsMLC&bZ{^)5-DtM+SYLlieQT|(S`m`L0JO4e|*k%T; zgZaX)XUs0lulY-Wkini@*kf-Q;>HQx}xQY7j+ij53 zVY}0rd&S)6GbS&6-}o>t5c_~RxcVBm3CMboW?|>+ox-5&lg=$A2Gv+04q|n2T7!mr z)iUX?QkOLDC;4rk%wrXON4V!=!w>r7Df1FxxK{LgR*{=-Ii9_q9sEpPTnnGk*&M zYPwt~Q$m$Lcd`XY6WTPJ?Cta2G#w-tydI4dD10x0w;!7G`@;k3IJ3 zYbNvlG$asaKe!mEJXKADHi0{pKn zzU?GGrDeCG15BEE9?AwpPYAZ5ve+Tw=^Yot0!+$5g;}Wnz`NRkl(0U2L8iz$&SMGsky=iY_ZgY(vjvb} z^{MJ)6ml2uNO`MB$@q2m)zbSgjNI$oqf$IbB3MN{4DwRzW0di9Pn*J=F{;V>g=lpQYWr7`O969L&c_Ktg3`P+Cq5{3YS7`x&l3uBi?%16bu@k;A&Ns^)@ zh=b9|zlpTM>8*sclNq_;>Elr*cMPwp=5-awk}*tI_)r~moyMkE;!qH;G3?2Idc`G8aV4gnRRtq7TU9%eFpe+g z1rmA6yHQ%eeK~mXXs5u+AxxQrI>JTi)r)@@Lh1e+F66YZ2`4OJ8~Ic6H-k!cwYxGA z{C!5v!PC!dg<+O`w%y3$JL!7(Cg=yvB$KDbPsl`_nmYxz6824hks+M~Zh5*{$Q1}S zN>IhB<4r(FJ&2RnCm9d+(H*XhvFW}AXn2TN^BY|>XG|iM7fS_6Li^2WZ94Pq^^NQe z)~uE)hlf$|dIXtGnJbv$^ z%$`qi$VP!zPLvW&8=^VL+~h>MG?FGRX*-_VOsJdCO+Y!ES9>r|x1U+vlRpIY{CsU} zHw2ZB@=>)ezT)4$NEB;h`yViE@!-Eb(Z#DZ$L%*m)x8jz#rUQ)ZhL70mISgKi7)j8 z^y(yhzTHUiB7k0@muzLC_Or51s~U6TvFf~ zesnkRLUCGx+yXfL;%T_lWl~FKccWM6KD+EIh4r1oPa4#E|MYEgdVjMCm4sKrEC9OC z4TA?|CwL@Q@~RfuQTi+=4Yf4S!`yfgx|BLHj9N))-c`TX|19RXS{wFM>V0Jo1>y3ZM){>L{x#wP_gw&adJM9DCV z@Zxe&E%60e@k$n9E&ZQsURSC3-1XgK<^~eGsV1_i4v1rHc+Cb+APN0!`2bnBRr&EY zwb_6iisqq!rDAIyLUx?&lMu0&BVE?BhGHQ%BQ!Y^5YygT{Z2(+rrUe>WEfO`%iIJK!5fC8E!|u>BkZ9}1)_wc`=z7bz zCj9Pwe3X69H0EHeY0f z@gdSVO2C0n`h3n372Hx1Up5;$uf)ok4l&|m;}#?pwhGxB)OT5Ky>d_a=mdq^5p_>L zRgjB`d^yQH%_mM6;ZOF{i?;o@yrXl}b$gX&E(O@1<@+nDnGRxLuC)(^PD`7qplt!+h&rEVWxUhMBdo`V_n~r$i=JgBv18SSm)U%0`4PcQFUjxeWQC> znWyVdX>b05hRHVPn}ow#k%k6#48tMss>T1ohfaS-?m*WgOjR#)ce{9%)x#*|rk_Z| zB)@(>eqKPiT38R{BG9OOZ_ERWlm7JrA#EeZ(Oy@&%YEB^slpmqVSnS;K-!@BdDNgj zz{U#a9Qe#ai^zg+)?{Jix2CSVM~Jv-^;HuCxA6{EVBE*2;Kx{Ov9@iFdK6e1oHWd& zI|Swzk7t5Z!U8eSghj}BrX0pS#6~E1|27-gp6D%&_vkh$5dutC&YN($c;Qn$k){72 zv*W7JCM~ApI5fI{0_b;(Xk%kOh2&!bP}TIbV%64&NNU%v4tjuv&yf0cC*-*&2l5*5NW3rqhnq>3{+n{_ulpZ2 z+O8KvZXHU`Med6P4_Xat$;GWd6cLE1xgqwQXfacG26l=y&`NT*OY^wkUS>uSH3**o5NCnfK?H3FEnMhBvQs2-}jl}7QJgKzo=;#PV%MV{ihT}DQk0M zk;L7*z9w=+YmCI5hf_uj?5B(pmKzXjhR6x$bOTk8Wn*7T1JaGI!#sRqgsPNKq<=@k zr%HNAqq&LKP3cwj!yQS>S*j&)A4+rP1hHx0gT4M23Wu$+dM5MO>A6i|mR@cETiD+s zI_58bqnJy$@JWrb=>pMcf-gd3;P{yQF4T6Mnk&4g==y2^%uGt2Xku;V3nTNg@ZBc3 zpjTUrk=ug#YePtdTXGb7CTsY*$)j#XzlF1V&{fGkyW9`W)n>x16n?F6m?Q7DM)?hk z>Bwj_OssQ?=!35+8mszLI}`{{#iM=Z#(N;6jrx2GNQi*OLUXaRhB zepXRCB@#~!d%&BJM+a@8KB=hb>GLf6?6i9hSV_caMWy=jB+J5`r=hDWBA%METrYf@ z&o=H6_IxQ$ozwLCl%g!|y$F+*8qNOgE!;D)DC{S9CTn`;rF!0Pbnu8YR9R+T(EI+- z#HR`Qa6Q(lH-gNC$-KnzKvtVGugo!Z(d>^%?89K12MO#`sbGGhy3_p9!&?i(=*fvmj0z>iA0T`;+g*7#hBjs{4@D zH(^WO8{FLCDJu}g@5%EN?#?-;$>T7)QXsoq*!Hw%Mi>=m)c_w%?8#rsS`1D)Ii-WE zHz;%4UU@|06h5XggoW_SlrtKt@HS|!R24sf`@gD^o4N351zmLKg%_KgE+}m+gaoRM z>hRqt78Nd4a zi(@u~lc7-2)4?sstF7Y7#;PKOgKtXEs~fYc4soroM!qf8-vl4M=uplXpzD#_4<8stwG>;sD`qhxUWfY@H;0nZwvy5n)Y94V4 zcMLlWgj7#3E@iq7-8{++LiRK@5TBn~Jda=2-V|_>N?`0((&Uw2Hd=z3jBn0SO}ISC zES1h(RUqec&e$CxMgnW6ueT(}hC8i-hw9`<8H?=gokQr@ldm;m4xeeriFXwerG76x zW*YDYFOBDHDXn49EnobRss?zTbDn*bdBRLOPOjZTDGe0FdtKMcs+GY%$erMijUj5+ zVYCxZ0h4Oaez+z-(no1SO)IcC#81bGN55Hb==;O}p4!?>R^X$8P?9RYoW6(ag*;s7 zk9j~^;Q9zdvAlVVKC&)3`9~1nN1XK1{$zCx!#;ZPL|9dWnw&;nKiw8E{N|*A>)W>r>ahf$4C(FLqnO&m%|bA z%T_XvQBl$Y!JZU+6>$4P`<%)BwsqlyNVCjAP!g)@cPx2pioL2BM?deRZ~mbYs`0rk zr^c91!iM>&+*AyF=N9}qUfah?26fnhkH3 z_2QYQm)Jb$%Au@UIZ3~&I^hkJJj`$c+?Jpl{Me!Tm^0U7o@jn2|Iq$H;r6p{pkSvo&cwJobnoRyyly&=Ng?Z^9Y3EdfTk7q zG}L`_{Z^c(u`KWM;9;dE7K1N$s>OJ2_*o@KsuNl9lI;&D)ov>BpfHA8bGmcA^$C)M zl6U;;i^$i$eKoUQq_79>%Fg(*6EB-djm~b=(`%IdG9pVK(`>qx@9r@N59NB^M(92i zu)vn@+MKD+nq9r&E0ZVK(#u!X)j^UVa!D9fnTCLvDK$_!%Iq#qUMQIMPmjY=N7>eG zttE<-7*u`O-;{f)s59aEWRH5;+S687MJ&ftmu>nfa8$UPwCrZ2%83vKWhr^{+*Yn4ljCe(T|_qyro6zrl;&2xS9XXpT!G#ZhHls!`26 zk(RC#46GEOo^>edV)rFM!_+9f&~1DB%|i^&*h=l%Sz-X`z|#zliQz|j1ugZD0h&wn5kT`)K{l$ca3D`rp8AkS43*GRT8#gU;%&aa^B_z;t+ zofo7m<>DZw+C|Pv)E#fwkik^jiG6sB=~x@MB^X*f?JwF>rWai$CN?gsq)ub4R&HM* z9~_o6hi6N1e9&t_#uM}w55N}K=KwvO%D#qA*Tb}(t0(^?CJ^LrzNbcQ*DpPYJ*yj^ zPfQRF&1|@CoT{$Mjdg(r?~%ZtysCz{KIq=R6U-@7vuHjtKI0!b$dY7? zx?B++*qT6i%I_D6$81hJeu`!Rd0rZa4~b!ZQEemUQS1hO#v-anwB=s=2i8kt#gEQp zIkSO!61AH;3t!jf;4E+8Jv%S)8yzFayXw{yZ;3HlEyRh*CnROX#FWHKR}(FR&d*!w zZx7R09`J`Ya{Sr?g-5!#r@h&Kwy-8(o3Y=qq2nbcCi zjMpC+g_ippPUUBa7WS@T)OItQJ*TURnpm2Vy0q`2OTC1Ru$8~b{g_EudK#DR4+-1% znu)3f&zTs8Ivh(ASM#$&9=60Ke1FCAC{}d&2^k@$6{GVK##^ulVJJ;z2fM zXfmogW|fqwn_SUUGlPh<5-B+@j_OuRS83P?vY@IO6VXi8oUNj5vnV5 zagRoP(tkn$%3#?#b|Fts&Pn7zcgM$_Fs1RV&mZuj?8*PZ{x|;ufzI|%Ny!Xn7~&u?DkKBm&wd10znQAX%N8un@VqjO8Jm`Y=!*sx^z zE?YmQ9#ys&?89I0x(@0^sg`+JNl_wW)(xBM zfyv$u4W=OmpXvQzdIvo#BO}4FCa{JhU9rvNqVUT$%Y7g2(xB`-`hMf9_n&lk`(}C? zg`Ii$4SkBZ{3&(h?>(qwf!pcbqyygtG8YkQ2F4^iee;nt%=|!otCKG)EyJBMOym8| zDMXLqX8M?zn&5nMN-vP!O|0e=F7w1_tkpNvu@+ogjJM98Tnj6K77vtp=GKN&C=wYS z_I~6uk;d~YulyLep%mBpGD0(BcpS2R&i}sD@S^z5wi}Zkpi5#GZKyL??Oyg=wd?6j z6v?K@4wg84Hw6FS;`l)A9If{I_;Xo{!b4#CCtE z66wT;3(ql}P3OS5Q0f*tUtOV@&PrKrN)I*tKa(nVqOyrzjja#Z$eQ+BC~q`Lhk49< zRZLFKLJqEjDy4k`q#M!5ob+T{DeBCBoquFlhn>i+`Xr7CYcXBo@=#Z^pAL)h#0Ya3 zes|CarL&bqme)}mt1c>$!RRv773{+*RAlNmy%ieu%_cMF)2KZ<|ixy==Ye*v2pVqh#o=$%?hZ0hf`euw`ccGO{nhXPd?k9 z`G~XbVH;B-I#tZtk{iItqL853?>mM)AHAFsoNWbq?pJ5_D8iv%kKb!VEF?Fw#S%;c z{7?V%Js`jSa((VQc!MKv&Q_Bfb45dQU6^#8Wy zSP_U4SLpX1F;YAwx7-ru(NLlF^zvn~wy^ms+Tfi5PPs56No0(we#6Z8E)KVBW>+5s9I!0f zDcv{zp5}y>{Uwu|wbw6$4(uPbvsyssmR2-2?CX552bt`D&>I~$lt3tMXjO%}L@hg1 zJ9WX$BAjIU+f`H8PH%>QhHC^GS_->e-;~$VCu-7U|Wr0Qc)oQiK5OCO! z{v&*$P=n$;-ek3upe4iH*TikL?0@E4(ENYX7&8lxAN@j~o-daR9dFjl@?=*agFIn2 zKZ?$;(c?=wQ-6l=axLFxnMZSaeD?hyc$o*;NTTlUVuN#&*+WgBMpH5WM3=g8^0d3L z`t{=T;pz$;pV|zTp^rv=;pzRk=w0r{JD8tT`i1^C%M$42Jnm#zZGj7VOY*Wx)x z{dxlI-GC4-(Rt zc{Q!P4*X$b_Mv8>MbcEptIw6}k`pb%xC*%d$qEH8A<7l=>!Hun*N^|9r5*pc>fR3) zS{UU|59NTvL+EkMFFY?5`kfi<1mRl>CV}{dZ}&7h37>#Qp1$xnPjtvLd;(e66;9Go zUO$wwO&Pk||28=WfGU6Y>}PK%JHl%hO1opV#nQR%hK>>4eg4`^8}IFSp|czY$n(h$ z`IKEpDFNjx!Q%j(>Fzd1P@{uM4%Mo*rH~t_x#8yq+U*9w0b3Eafeu$XkTbIL%l7z6 z@NXuh+52Z>M9IRgyZ+}%!`-uKlU9fssBc#}iAJF@o5VwJbIk_Pug*ZimvY^BwAqdG zT(jp*$(bDNSSPxhJnS4$K3D))Z1%iU5uaya zy1DCwN-HjKk>$jTWuLC6YUElO{Z=p9MSv_oZnJ7>Rt4IbMSzO%|=aibQ(AL8hWpYS?s z8)9D>r%W$KraU@(@@_yaQOnK)kXHArDFOKUW3d@VJ+bZqTb|%G_kve78PpZgod>I8 zj}j>+38Eum+wY2crk46&1;fh3-%K?hO346Y3&WiiKX3e&$pR`aeAo1VsDbZ0enBzw3Yl(6eZ02TY@~S zFgwVCS$K^BpgsdbfJPVY&h^P9gt87t7>lWu1-SV9uFc|UYdwR)Z+E?9t?cPfMAcfV z&#Mzo_r=V)9R`C?;lsNY9j;#ebhWT_O9g?PecE(ynN``!#0_fbek*36@d5J>{M%e= zcv(TV3PiT8K!mU7V(psf-a$eIHJa~fz_zG+4%Y`<$}?tlDoj{a+N-bcO|V{ZyEkrMwOdNhdPN5%@31d;7Td?F=aclwys(V|MEKJY z@eLAS%v}*rq{UnNA)8hmKrO4%BL10ltE4;o;277Mu4nt*|GrqY z=Hg>Kl+N%%kAR`1Ro*p*gdeQ0G8Ehl^VOnB8>rOYGU1;@Qh4GeCvQX$h!N%yX@8h zZKT&)QQ|w90$}sbDdG-XLGYg(9YLomYrswUi zzYx9)Cveut1$=-}%5Y$l-IcFSoE)pH@lA%eb+wSywJ`n&ZuMe_GV`Z9FOz&twee}I(N_yPwEB)@nuPr%XED_b3C-;mI)_uIE*4(6s54x8V(GBBr~)hn-h zQM0p;=X57a3wea5EJc2VB?OPM;6i@0o(0#aF#DrftTW)^0kx(pS)XSmBsN1T9}V1l zrtsbz18?ygnZJr{jnpgX^J2e#-9yd?R<7Y^|2*}f&9Pz6&5X;%Fv{>G8Y1%O1}C9= z=&}bkj?l8Wm!$dd8>yu#LxzYSVwW8S99*X2jSYCS3Q9Io5j(00yANQ#>yM?O&rcf= zu#w*u11!^XpNvgj6f|~P&;-)-G|)1o$y(XZOl6t^mJ(G8I&Y`vf553jbM@ugYV*|x zzn=h280;=Drb6?PDez1PbFW;#9=hpx0wyf@c1MXMhP`(BR(Zhm;m!1uhh01rgVO=I zdX%8SGWQ7^b3wdt>FgJ;`s>MP&ZereHUg{|CKeIhdV4yExu3Z6(WJsMC^tJxt01AC}EwT6(s_$QK)g+|q!Ac!*9jLpYgp z@O>UXmjjWGWD)4vtYchth#LR^u1ESDX$!559~!k=3DjFO{IP>g%_0jWJ^ofE5$Cfr ztd%yk+F(}N78I;}=0x$VMLt&OcF57zv62-B%XS>9#Ou}+*^RhE+5UV_lFDyv-T3T5 zkzo*WPI96FqSfzT*Dy%49RHlE<|sFW^mq!c=o^$h-m)3C#m#8}unvn5nX+Y|uqD4< zu$y!CmME!{WI)n3I3=WSvlN|w)(pmi!*RxES)i^>787a(XDqcACW~;_?dqVIJ2_(z zl66LwVk*~t8m{;Fy%lFoT$1700}}>YvND`6k=<3D9sShic~*=NX10*t0h$}VpTT|n zChwFH_kmddF?2F# z!#mI#?u z*j2^F?ZM(87CUji5#|WI57m5Px>(u#qHz!3Qdvg^{+e~6yX49~XE;fk;qzQTUN;cj zqpe_Uz_bUD7=3;}8LvMHok^)CGwg~8mpDHP*v-T7iSIUtDAzo~6r@*H)|cExrQwu;2&Ue3%&1Bf!27wVBicmtNOxSEtjlm#Qb!CG zHML#6{?alQ_d93RM-J;ql@{KBdFtqhYgR9rIot~734VfZzLra}I*lvMVj2UQw813% zj2#Fa1qqAk%}C7*=+!+9=mrL5dC?F=Cr%idJ!UaC`kTVTMPJfva1N7c<(6z!FA?YP zy?>iewAHElQ#^hwKOu?(+Pd5#+0YT$!*=%^Q@%+MsLp--r0n3{4oo0NQ#5iB=ikRL+s`9%Nc{NasHH)1O@nr^+3Ne3Ak5LKxR zQwN*L7C#R|D8#C>JWe4t7t=ce_^^|ch9DH3T5O8HFSE`}k*-ywYg2NF&z{K&Wxv`B zis{5TP%fl&ir2(aJSshL!&~#Iecm4E5xzkyAl9Oof5pA5e-{oa=4Nzk=S2n-!^iU< z$l-oTj;(So?kOD@G1?5Rti)@|Oj2*Zvd>97L5229_LL+FsdjCBm+V!2T+zzC;#l}p zi91}1LF#VH_fP2$bj6gKG?iA<7FxD}9fvL839a#<>)S^QiaSBcvFNEft9d5sx|M@~ zN?syFwlD2M6f*RN^;raa`@26by4F1UZ_+O<${)i8W5>Nh0v^dc(t-dE9~ z!Q%un#4Lj}Yk`6Z_FZgT9a~;ei~1g^=I&N~mC@OY9XHGv`S{kemN4&8*in{CE6_s1 z{XbfOCF?rH=a5$7pE|lW!D+o78Qz|S&3gKS2h{I}OPczan#4K3A}P?7XQlG`<8eYT zLPEzQ`N0Lg*_m%LM%7D?@bf>y*%qms4#;eAF)>u7o8A{7i*FORy47zs1|Bus&{sAp zKv~D~3yWsntMJaH%e(c6_q@+5ncw79HIR`!+W+?_c%=_o(Gr#Dl2)(80O}0bE4qDu z`4yRswo=~%`mN-Pstvt}yjU;A=2kpCuQwqRSN8GhwDEdDEZ=0Yr!zZtahnsJvRZ6%#et(JZij*O3P|)V6BN_NeCbYBRABCSX>Z9~I3~DLp~B z7XG7G7MBRp2pKGBmVS__KaW6jSr94Y7FtJneyA1?W`}=F22*`~_QF@Yh2f3NiBjZz z4zmN@-LYD&sA5I#HhYq+I#~R9>1d5}ow_WZK^4C)``@ zkrc>;N|I>l|8Nt?ss6zcoJiHjlSLz1sq!=u{v+^p^XeYa_th9GngcXYH<>waAAJ^h}Y2W#^X;0({n<^JHuA7MqBN>+fUPHOCJtF3)~c zyN9@&Ln8uuF6tHbc=03<>g19lTAo(y%qgfT7s)E3wa-popKH)3Y#U!zKj1WAGnE#t zLPx@3n&Dx$Y>>Xq@D^zO)z1y4P3_=TZzNtzD^q3X53jsKD6mI@)!>0mb;p!sjXxc3 zo}3P*S*`?}OibUWAGb@hmA=|@tb5IVbzKV-4Gt^v{DZEN=D7cb1UZ3nTqnF?D70>P zqcp1yuiqIAtvb&<_$3=Cqgg%()8#$#&1tWxvtRpMwEywba6_qh*~MKT{zP1Afce|- zR8NK4iO%gOsoMA89fBunv#Y|1D{=meqlTBhc>;Y~eyASiE&RMgU)| z57n#Zcp*x@532Ea^+f~Q_Xn5`3##Ih-UN|zAXIOR&)b83!a|)@EWt73FQ}XLTl~uL z$!uFR0gu`yy`i{OMLh3A*v}iyy9sO*%K%3IAdYqg^hRYAhNUttCByuxk%0<*xf*Qo zTw=j$#Adg!a!4p&Sy_!O#;EOM{+WYuG^Ks)F`sh-%1w7;_waTA(etE$= za8Lit)XpuK?K#NyeQ7IP4sZJ<^`_qUhc}u>Al73Y8Lj8^!EkU0sh*4Xe~bL)j6cEF z`9SWFuK#qQ)1wm?vb{+*^+~2zK+TDH3%YI>8XSNXqq8fVlpjP+sx=$=-=ofnCzFEO?& za<2<5`N!fY0$g>L^aOs$jjK;(o?U>DXR_QVcy?D!mI(UI@{8d2&$3%%Re@nxY8kW^ zFWydw^%N}_>S!eW`~ouwY}v8*$#SIpkN zuaF%7{LW1VQPnQy1iA2ZA1uwsQWsTa_6)Uvd`=CvOq-tHJK*O8=Y$^!8#3!r;;Q8;`Nv8-r z()5L_)VNRztHN8p)Sfv%_~5Eb9OzLNNU>v;wV_zs${9@c;JCtMNIVS>aRaKv_NJH- zDcJf_^6NHkVAqYJiyd!B7M!xFw}Ty$&kYMJ{-WLDjV?cWIJ4`nO z=g+La+G|wziqyNy$k=B@^jY!94}N-Y9{{gV@cQJ z2*8smx5^Wvk+2D)++ZC+Wc${kS{1ttG)#!O@?%b$v@!wCEP?H+vxSuW zEy%xXu6g7>&>~i|&oeh@f9hGvlr-BTdziE!hdkPuw$Gn6IToZ|q%0d0nm#>GFsTP{ z9-P}LR6YUk-aAOhL;a<#RQ?46d7*@Qm+}~9qz1^*f7~EgwQ*vHo!S? z-cAIB7)^87o%y52b?C|HRV75=1>D%U-9QA(kKoT}^qSEe{o8dZ~ofNoi z-IwUY5P9s=nJsj?>InbC65$#QtIq$8sQQP9mof`G_ImXg@tyM+b^h;Kj7GKd+KE@H z8BGCOwAoNAF1A<$HO{$L8KA~b$2}o!NV=dc`-h?A30LncpJd+p^svL(128jbg4MkR)=rf+IKHO=6t5pa&~rj z7N)~1lryy6CWX?=vY2c}MaT&U$-1 zS(9W!aIC$0wE^cRh>^Q5@lyz8J08DR4NX&1{6@kD7{DKh=VMN`HC@=QC@NAi zdCsh^-VeB(tn$-?h;(4N?1DZU|GH@fU3B^cjPs{NuxGQfJ3_PTkhFHUa^Zb9FS*DB z@MYE4XYP|}Ivy|7vM&f&UxX@@6kS#PQ{LVW{GS+cxPCA0uswGEHTD#Ny_wiO(1>sX0c+VdadYhW~Z7Geu)M7en-6Py=9 zW3&FG7YB)&-EmCrv8tn<5sMDSQUUA)m3_Mhb}Fl9rmx7C&VABr*V--uJUl0`&ud|> z6$Xv zIt5_&qd}vmx}C$h3GF=u{20k8WB;?oJEG()TUgg)m(^3Pk8+*!#^?xm%zn#n4**=_jU|W70=?@4 z1B-+>oaaYtJ0Yl*V$oS#!a=Z)s#{tLL5N1>pe>@VL0bCIHZfb4;4ty+opay?$=YODcG(}wlPZaPq-dZYLMM2|m7J6qbp94u-ukuboW)V$ z=`?owTk~vYBXDx+qv7=4QYWx=&-nkxrl&m;#38%d*1Elp#e$oZgCw^o=vkwptG)62 zqBDW_I5flE`FL>-)R_#B5kJ9&WY3m7YM>8C&>S=`0RZMLdmsPpPM5{v1BPLR^tSrW zIm!nVly@ufsLp1#ro@K^X4kUmf@kVvFSC>K0x&^c0gScP1XT0ib8m38DRJ6;mZqRD zsV*Kp%->kJ*y+g)mWxQ<$>0tMOQfvvQ)+dJU2RO?KXtZW3VoD6oiCeWPdlZ!*swCQ zbNf9y;>>KFB=%na!9f>bIm|y;B$Xq1lCuZxZfwRED@8MR2E70#o-MkPe*9whAw5Uv zfN&r&Qp&u24`$5)C^jW3gWfHy}~gbZ=g3{ zqNO~PT%4%ZsikT1&Bf;;Q}-DL#3rAN>My+4pDwd$D&dKn?JNj+xI79!-`By&5?o}4Vsi-DwBosgV$gK$)lJnver0NDu) znPVLPjs7^kwxgK$`oRV4k!9mpBkJ|ZnyE_9W+uP8=0&dkom5AoU%$TfzTKoSz+N_>|$HKP$DykdZQ84`&H|?-2^RSRC7km;{Vn zpWxuiinS(U{UtkXKUXQxqDe0ZZBgWm%lpuxnQ0uBLS81t8?4%ni(5Qvxr{od`)HQ+ z595ga@NdsSSW%~;1$Wj;$x&0AftGDGzTa=TgPCf0MROmYa*HI3I{b9t`f>ueHhQ|= z?oq{N^&@V)+^(~`g<1e&f6ZUUSo>i>dWPrTQR4Avc&O?O$#u)qxWl|d3k8z6x^PQ% zVW156#tyr&_8+e>6m_1(v|TTpWD(n;mgm9Y(8}whr@=|;Z!Jn=@riMo;2cod^~E~JElP4C1*gOW(I82xrcwe& zd=~~wMkl+k6I^~V2I7;)5-5B1k(*q>_b(436`{z092d+7++V*0xn)D)p4D58xVk=v6*Km2ph`4^Eeq73vqyFW$^to*me@LhlDe+X{5Bmm$~lxWs8m3ce9 z8xV`PM+$$rbXX+Aq*lwPVV_ZD?ROv|a3IWz12%6*c7`HP>5^E>@ad zvh?vEemR?+BWE;v8yr>UbB9)(?VGtM#|qC~KWG{0w8!lM?WBrBPwpRPEW{VlE$MBo zDINt&t|Te}iV>YoJsCTa_~;*+c)<~sqh5SAAN9fHTGPyV{472a8BKOhcWtF58bO!K zGU{Qs3oQ1eC7aBjiakt|&Z8EGfJS5|^3yA618IL-lVlnUhPmQ8il~nzaQ#DMe6jVH zL=-ixvyzs_u-N^%36il-#Vcv|8SIfrGJay#;vxYVV&ey1u}U}zbV^JzS7=`MO{0Tf z4gre`^{$u_H)F4jI4C#fmW%1HB4ruM@#*d*^m<2Z3TK93JFw+=*8l0B z(xTb)U*e(Rz^HRXI`0B7f0;8+1d!3suY+r)4!jKS(%EQ>3?RBOz=`3snW?+be)_Cd zy(36!nDA#(%bOM70BX4vyt~D7_~+lulOqN+?-2H&sFZqWeDR52Q{2eNo@osuEjnZH zV$G2HdaX?wuX*D_(5Zma?#IgxoEru8kbd(>bE_<;8iY5%%}%1jzkQXzy{4Wv1~m#7 zsyM58c0g<^p7T4_?v|4*)}b9wBE|mIpyjV_hHX4!l7M%($cw=YSc+ z3eu;Qc_TtE>qJcYN1Ufhne=!pQ6yKwBl>y6EYy%Z2WpKbDHL3O4o1A`mTNVZTP30i zipsl%z#RaPrp+CaGojEn4~CXGKX9=k zw27L~j?|W$6Ugma3LGD`*uc~8CNnEaP{6GELH1Yo+X>}?H-DE@#6NE{YuFz}>x%#e$G1@ zAcS^T??cejmzze{i{mCGl?kqE)d5x2Gr^~EFW#9P@hoJTJy@7CO+Ym+#oT4!&P02g zhJCY7DAK&Xqrq9ktA)@5iy{?|bf9w*=_LbP*<9CGqTt9a6cLndu~T+s7vegXlJccx zemU6s@~6xnk&M!#jH?L(#{cW6-M<~euc!`dIqnuIsy(hqKUt3sX3fW2ywqU*A!L)_ zPpa0Ps~`P=aD$Z+p9lc1i|z(+s(wxWaAf6Z(1{pKO9oS9Z|{04zh*mmO6i@PBgO}> zfWF!!yk4+iG1fty3fUDO>-kaVx^j_i2mhu~JXUDE!Db4b%_7I(944L}JdQneY{L@) z#PN?kE8?TFinW)3EUte^s_uL$VWU3$l>WiZ344IFqH?(>VIQtJs()}kC7#!I*tBCh z_aaYr-Ak#W&mH{!|qZ?8RhpyWv1@C)<&>tVJ z-26t>2IT=v=y-mxVyx*;26|~K;KcRQv|89q^0p_K&l5edM!W5|XaLri8-~rzT}ZG858jq|c0cT3-j^ij zW`&BZs$~Am?1dS=FTPcTliRRC=y+^DMkcQ=ldhvbDqxz*qb5rhi?>v`$c-FsS6mSB zzTN|nHMufm*(1pi>7m?W!&5u%kw&CzD!z}DwYuy)p&6ffGHi~J0a^voBE>bCrRVe1 zZy3+i0c1R1Pq1Rxzft*H9S`lpzwC|em*AK3lq%xi*xg^Hol`Rn086276Wd_VVuRvz z{LhYu5NZ3w8U;R{o9-Y6x{VT2NY|$}_ckFirX~>$8a#>{KLU6efnAV?N=l$Dq2$Gj zgR`B@szo=t+%icM_40r@@K%(H%b0aXqYN= zuRppyuLi{3BR~J{xLd@Tz;p6BoZm|*PT2Kw&PlU-6oUQpai!e znP6LPeyl#Zasd)rx=`?-PnsGA;Ek>0qeE(U1@&?2a9QqKVKv8HC(8vzk?I1OERM32 zt*=)V4td#RD08EP{U@06Yn|Kerk9tFR9vl@7lKV$GeaSF zx_?n{_U9|RN4-C9lz2$R!Xmw&%-6A;K|HKXpLi)B*k5I4NWm4@S?7SVnq3E4^pThh z<0P9#P@`u1irkM5@)VukMDGi0lWk^#-$R#th(-4hJ(&Src)byUP3vpP2{wa!30SwQ z>cWrIatm_xib+Q+QOFNrl;1<*q;@4Ijfb$|xQg;euBa_g%BOs}@T2ch)aAW?eavTbPCnh&o&6LS?RT!ea^M3*Sc1mLA={RJ9H_OT6>L)9{3^i$o*zKc4hwN zAlgn;L$zG*T4{q+>XVJInE~2LTT@!-pfNx-=!qv zhQ3o<`nptoe*7pA8 zKq}3r8g=~z=Td*R+Y~~wg_n)5PCKZcB>%+%oYeqkp_7XoEUWez;7F`|tlvO+@o~x@ zLRI&jQ6hs6W!EH15(2792OY}x_p-OvS>?cWvfiR zc)ZSOFVXy?yG4s;3cN*>TF`u+f-Z+_M`$df%Jsw3Kf!ndlIFD|Y4#~H54N0qFh^20 z8$r8v9KzuE4oNV`9Iw-ZQ3eXYO?A6Hu6bH04FKr^%`iv2?kjv5l8JKokwFX?t}Qz> zz5-3+$5PEqu?@O}jrGCbi=9k{(8kWlJ=Y#Vl;Kj>@zGY|{!-PCLE%Yg3l=(0^x&Q5 zh3m(-1E~PFx=X-DeY$m0^@dd5-={@lIddFK5U!ZXX#{E0eQ1EUICYdVf7$~Wj zbSJyvZi23Hat3u-;)G_Q(e%fE_{aEkF5`*Q%?+6kGN`n!aiD$0RK#3ftLqwU{AuF( zNPvV<__qH1(B`Y3E|B)QkaJ!thLQ^zMn?W)yL#019~1Y)(4+yX>t(zOma9a!|KFVT zKNtOVbG#%Ads5pK>A{X=IYVR0g2pwv;U}GvT}4WfnccO5pEoc_4SJGmRh$8JoAMi_ zq0Et}?d@uHmP-g9Z}+Um7&?=cVwABhwcpZu<97AIGmq}$sEE{~AlOcQqRq7k!|pb( z{iV-#*;YQ6Gx{2@tf0V}2wdRgX;1hh-e z=}))vxa~K1K>?YpKQ__nN_^y3(N6O-pqGsdAFe1A8Vc}VKY(lZfW7FL@Ptv@ChEsT zJ#{S0i0jXs85)YfGD2{f&~zi80>Rx6EcpxfDbKUo7>q0X$N}4lyrW1)(7O_`oi_}; zuyd9kD{NyregW;7`;dP!m-WAyD|a*9$-wELX`u#KI_pBX$1y1_%X)ST_hU*9d_K-% z{7N>#W8#jSKr?%5AY7Kg|F(H=Sd0$~P` z06>dVqOUJ3fF zI?YW|MUJ!w6H+o=HE3>`OBoGg9Qe5Rq1X`QT~wjz$O_!6OCg6f-z$?fT!LWb(FHON zQOi6|=S5Zl4Eh-eVcR%)Y)(cuR&ZQf#@0F(5Hx-JzVq0e;R}?}(J9CnmR3yA)^gGi zuXHN0`#z!V{d*MuK*{4CV6r1@(R?e)Kf~H|v-)Kf?-xPh=c8{o{y^yXxkVA@z+rd0DN?xm^QKb{==OXT3EsmDcRIaD`w-;V*~O|ZHPLXAzrpjJ5{%gCoH(B za97IUPqkCbbtYojPqS8gAq09|Vf=>{^vn?sZX(U16YryEeoICT*<;5m{ zc{%g7;t}{v);cL#tx|P2Q84DNp|byU&N?D+bg#5wneO5O_a1Kzi+E`P_p6W)&+Mix z9&ceBVS`a+(A@wHJs{{2FU_`-_sf2sK7aC09A?^zV{CIta%?Of*#R$D#=7Ks!i`0L zBO|GSyPMGbR5lJlSo)Dq24ct{mDle02R;C0BuUI3 zeFt>-oQ~n%_?jS}Fm0)4H|kB#7J~f<5cQ9%3Td0$d6WL^`dIfTkXVSgJ~Zj0WA5g8 zB*uH_3O|KQ^(p7-+z9}#xbCi03#Z<^wng&>ZSz}3X+Hu>EI%`+zp`!~cm$m1BOUHB zJMAG9$czwK*s^tv-Zr!F%)Z@}C>LXW3ageeZLv)J3TF?oHyh;QXuj(;2aYk> zM9{c>PSBef=-e_oRQ$7ecEv0I3muo$q@pY%+oAwh1tHPZlD4V1@_AtKN{a#CndgNp zCc@>m-H!OWQd5}a0|H_y`{nWRRhmDqu0m8oP@p{9*-H9RaN2|FoH%q5PL-gon|IIH z$JQ@sZuqV-oF9Deis23|ufHhiRsnBf+UKp#h1pmqZ_x%92kg{pV<3uAu)ooz{--kR zUJRlxpOFB7+jR8kbQLUk0cR0JFc;Fj&8BGpW@F|$OH_;0JJugtNCO=yi)gQl=4Dmz zBf|?-ZaAH$J7w1jVX8iyPs7>BnuOQ+bppNA;lT5Gbc2e#q|}(~J?47#?_L^NiEnwL zJs@5W3-E@7r-)4@w=Dl;;J4wiGsWt6+b02=A?uTswu1cwDiugl;~hnn5`etDW0g^5 z66#c8Js8DwkMrw2q$n4V$HHeY(4mX*6x(>Iv*Tbwg4oc|^gw!XU6oAPxYvUhmju1? zlCih(N@ncA*=HHDMW=8{E*N*{^My6r39KMGpgIrhOOtl(vWX(rUn|}LWJIDhkE>FI zsD#fhO5g`8TayQ2KV`95HoiCF{N1+v1vxd84Rfkse1!R@Nq@%5|FE1jR>tdB=5;lA zx#f@8ZQ5#OCz@=oT5?R=54jc{ZIPGQttViuI0@2lo8S+F zSNuigD*oL#CRik)_S3`9CEeFRX8frUPL2TdkA824b~K0qFXoqOOVYm*WAdsrHKIj# zdFfTAaJ1n7BDlCk`||6R+-(YnukI;14%6pZwx9aUuF*!7s#Lm_i)sbeSPn}+lA%b+&xkIC947qSQ^D((aJnzCkjCi z!OmeytA6G;-CB#=70CUzb|U0(U0S{C8Q=113Kso)zk|7LaPrpvtNR?Hjslub2XW|g zux9Gza(VUCdN`PVge?}$4$2Zyx+A~37@z}6K!y&?0#Fn~M==ob{o2tjiR%3p62%pU zo7Y0ZF7Q&}&#UeoYp9H`cWF5{Elz(C4CC+qAv`e35tXmD2WBQ(1Mmvcoq~qiQmCFx z;^k0Gx75$!9qeJ-6N}l1a)xR2$4K_ndrjnu3}6uVgJ)>MS@uvDb8MNl7Ob(oZZSeq z*eXS0*&WkVO{;QqK_6uDE$cII#5J`9HFtSFX=6Ajx}~X|Ki^(K__3sML@4z6X>9*M zHjPQ0E2>75Hym_tcCND(#W+VOLbtGATyR6A~tqZ;L^*L4i zchJ2Rv|s@;O(;2UnbO9*m;Sn3D7zVSmhW_NjWg6iUSz-`k^%Y6`M3y8MRVwJA;`WtGOmkqEF#xZ*|j@R5T}! zg83cLZeNPj^bZ{Nv4ge|Bf;`~RZe#d1%OK=s^O=*a*lHP-SSr!(;Dt8eYXcb!RhDnXT`p4uQkP|F!l zW<`dbF5B4+Q(j}#R%TyZi?kpy6+?xqCP*#>6HYOgfSjAN;OSsoQMK&LFZU)`9YtSU zYD(t)s2ajXTt$f;X-k-Fta`{9l0-a=T%LWLhrBBMEr15*xiO;n?rJdC&O_jsf*ATl zk&FM)2Tmsn@guz!O%~RsCiqL*Y+>*Di;TP2eZL+=uns$+gu#pAZfdv}P|3*@#JiX*Dekm2!YSLf&(w_0i!ylVyEP4Z-L8Dy zqOIflOGbXkhoQS_GJ9AkA2FV73ck*-ui|^Va4r9HB~E=va=s&0J%rw1RH04jyZ+N8 zu6hUN7ggL|Se2{s`qc+RCpj5Ln}=$b9JsQUs9h(_^@Fu5Y*g|6g<7)XH(Z9ow$a zHEe0)INPS8m{*kGajRw6d8$6bv4}$x43R}3=zPwQ_PP;RL=Vn<)x@bZ@O=N~0rV{t zQLeN5=k|VYo}0)Dzu$8<+Nvka2xUnG-X=AsU2H1vj&Hkk&G=;HqK|cCAya{_=o*UG#6Rx9agxNprJml-O0Eg7D@qkk9TE41ddJf3 zC5Z8be9!2={rLuShzh=O>(ux?VPT1gnFn(X6)B)te!9{?Q`_5oHfuz)3tslRhmZ>#0{l8zl_T+yzgoVZVGnRqp;!01SY;{ZZNHhFa#&PL= zp7yRDncr!G9;rXo8*)Vn;c5e|_Crkg%vmQneL@qGb9NdMr+V~kOWHtT^F;Wh;UFC^H z`p+<$Jc4hpTG=7-V*|W&bt{EQ9-n%VXS(bI=ma=ZE-?cGYvxyh8rwA3pT}pD71_nu! zK;IB~eX(Q-Qln#Q|7G2kzw`KRXM6#D{`6Q5pD?D{%v!6-hnD%ex!I;eYE!o_q-Xt% z5q#J@rYp0&W;UE@5_ZG(sqd3H>m*0wXtBKem@lH~YuXYg%DuAG_RPHh@;s`kgfTpY<|C!Ee&+!e9*n8E~xw=@TLE5;?>k z|L>V=_5W!aD3XPlY$LEk24#|D_pz<^IDM#_-uWQRhe_8~Mqj*0kgiO(mnJ~xEz9ZL zAq&>xTeqa=*g*G5<6ao_k~4#>Y|E!Oy~`nZI`-aA!4Ay_t<`ywxhc!DK}x_>G60YA z`X&rAgMT$jf6li0>%18g@l50mLrJUmDtyVo6>lm&bs@tvYeGDW6{CtNBl!vDQLCk0 zB!R5wJ27iVMG0@22TM&!l2<+=&JwgyxulaN)|pQ=&uGl_fGEVTZX! z-md)E24pCooqYEG>qxW*zvygKf6k)*lN}*$yym*iH~zZNn7jQ1CpqpYAwekv*JjRG zdLLIseBUSAtIZ!h88O=puNHszxwSv?KYZwB$#{*9@z5)np!eN}z6Z?mv68#hQW-+B zJR$9UPKtpBjY)i$xE&A z|JpN<9W^YWt#fJaEj752e#&JuIryx5=@28-1-}wXL!W zO?(;kM~j+KQXU-0{8%kaw=L{Pp98qNw}R3LEU}n^b{Zwn+}KCDKlEAbn7OaOhd9JG zxiE}W+OPa>J~J*Cg32J4K|6~UB7(y?2Q#Qd2dC@HLhZhBod2C2{N4UL9((U`xnHE- zqtVIrn@Y;{Vrkxb&D|EJG< zMgM2avild!92>Nu?O_{Pakj~Yy-#khOvM3g?rpqwK~7<+&HWAAPz`lC{C!xzEC}Z& zwb3$qYCFS@cUbPUd#~B2Slq41+U3gIC6Y+`o5-fR*uWCQH0->{sWo(pSLe%?Y@xa$ zu)(s^Tl$0R9xu0PlSYy6PW>2*`W4{6uG6Cx z=vwhwSInypnv)bbesA%1B5`Z`9JLgTq`so~+mr0J{R?&eJ@{-yY3+7PVMNxudmOAd zT~!=?|J4nDn(XBLQWT@&EupZGEP5{lp0 zjF&OI2(_!b76(Ga&-zrk)pPSw5t_UHCDvV=lEdDc&6kp&-UJ2EZE?u)0aUg$J?w8| z1H4gqBF^rTkdcd@48MTq2LSuVoMf@%3#uP?sj8IT(YI|!Tw{Ia!?t5A6Q+EU&iiNA zc4x(Cz6n|%$L@&YKjMAlr5v!aONT?mlv)L{&yPfu*CF#yVry@>aB=A zGA>t_>XShZ)71|Ao+kib?gBCUmjIae-(|37$Hj)H@HJB)GNWA8^qXBjOcSCc~R`X3cLmH(dEf|x~x7@K@;MzT;Awe%v~ zAon}6E9~32Mjl&FoVYqb8Km#qVOcV(0myY)K^Y#QPh^l)ss)W))A^+GShn!XH; zcWrQWfbTj*`4*7NSa9$pU-4FTXm8{}tY?)`3TZw^iG= znUUH6J(>?6_NZN5K(0hJFH|D5GGDkk@K(&;s~rTrvOzUry@h?0%v>^QT4+KokV8+G;i|rH=6?(G*3Pa+I8BA#Olw; zid7x0%Ra;C(L5wrYavr*VWMH!mr6R34|7+mH7BiJexUJ1Tb?{drbd8xKp5C6^6cXQ z75LV4?Uwa{v4R@=yDa_}W98vD^z}yWRvtrxQP7!}wf8lBb?<*m^ zz_jCu$?u~!xAt0!FC;voeeH-d@$^JE=uoTX+Z531%z;vxr*r}6q(`I`7ha2*>b@kc zJ_uQc9YwRXeQi>;#;k!A8#7#&-)|l}O)^{xSmT-F@m>cdhO5Rb<&)05aYxZgA(mpJ za`!7Ae@utA=(kH{4?Q#@u)G#(Q%Q?D^glDbdapv#*BjwD!Y<0lpjVEM=Gsb|!#hSL z=8#2LGYe^{Rc+54?bYgS)z{6uxwNyO$? zbEY&?M<)1$#;hfxd(0|?CiB36I6xY%ivXL$TpQAoyxPj-dzTxx+JX)4+L4FIb$Keh zcTt%$7JEY40n!m4Ev$}eQ`{6(k6II??rPoc@cwl?`ZrekZl@Lpg>t)}<$7I|9zu}w zLTdDm=6)^K6)Gt97Ykr1yWSe{Ei5)uQ1497Bmw;8LxbW)t|gT@cY0kZ=oUabcRUizG^b_LR*wJJ3wf4>zwSeEe(9%AAF<`2 z!$>4P@L&zo`(ii20d|06mO5~_oP@rYbd9+1QWA4J(l)uu`87{~Elw%+nfG}70xib!gQHR#}{g_5!vJ^56vNy;oAP%wn53ZmmEKo7IpR}Z( zH!K&WtKEU5C$}Jx+r3Jo6BHIZ5b{`Js&F4!GdLz*t@IZXIMHR9?-1H>m ze|%_0T3yA$9nq|%EAT|Iur{xd6`xs~yFmRvuP$UupNfp>SL+b3#GTjbd%PtF0YvuS zHN?KEcKEq}ydrEFn6!NS@RxY>LW=wy)1DV2Y?g6!0Fndcw0Xicu$flCTl-Ge(#Du= z`iH0cWcd*DXX(QsV?~ZG4jpUbx@DEHt}Iu=UdU{#2v#Z~#tdjs@zSg?Br(n%aFlIP zS^muk^iNYDQcCq{hGml9a!-&m!!idZyC6;PM5a-Ck-zRbk*{dVqvU4u;8du9H$BQt z%K|QZO&y{;w+_`uZGw6G7_;FG0rT>qn2Ceo1d@VkiX7tYIz~=(p^je zc+Mfe6)%1Q;aBKT?*y;%xMf3ag`#YR?%vi3_?Dq3k+aMjp800A~=|qMbN%NLrbi8z-@`<4*QXnkUDGZR& z0c``~?{%^tj~3Ot$m61oz z=$3ud)qa9vpd~|c=Gfh!$Ac>l-;`{KRA@)G+100O=rp!WD0BsXRYbWT{UiA~&^gtY zHe4IseXNzFEBC+=TS{yN%Mm3|0KH4%{HYO$ns59{WDE`%VS6j*jtz~9ePNyTGT@*O ztu9_|v9asr8Q0fUqnR~REz71yhC3~DD5PP>*$7TNw|yU*l}V}XTe zcT4h<4h9Er6>=P72u|taC9R!5O7za8SXsJy^!G0N$aR5hV+0(Vj(AC~#3$mxzlCsm zcRY9)DNnmtaP#us)r`pL(rK_o~@uP(Kf4t>N7O$8Svg-2Gb+AA*l z3p#Qu$p<{T*Oiq!iy#JcAP>9#S1bb__TPq1UT&M_e`@)2>BUfVrwGT7p0#AFwwFM3 z+oZGw166mg@gam$b2CP>vdPwS4+&4RLYY9k1qm*D0UL4Q4wLzZyD*Ynx>NelimE|Xcz$XG0(|LY8Dwt``S2GQP?T0mQ2dwFfw6i{u zz<00d;>>UAtX(|;m`OzwX9t!`FYmOz5hzo?{6z@LGC#|DB>wCBXLF1pk?`9XYur$g zq!xw^dg@X35@T6KsWD{W)#@2F`6d(04mw3m@1KOeSyb&{HYC?AkZ1k=X{S%H0;?(D z-LsWQi}mDId67*iSkKcqv9^IuEA)u)_S5@=vrYjj14&{5BMsTR{9=f!z&g#CwxF+4 z@7waL?#*c9IBo0D!XxA;nkMHEr#rmwH3F9;#&K^@)`dR^$oxA zelItOTrj-beJUgj*Bv_xAgnm=!oiHAHyztu$X6=#%_)7O1{2(x%7V!+;clDxhPa}y z%Vqc_#3%!scJy42gn76BSp*@&KBFzG->a~fAT*K0sN)(%IlA=A`7~ZCS(PZW#Vb$7 zjiwDcG&|){^!ku+P~yERAz7jmm8h$UHk-S=6e#`~sY>l@hi*Ud*??L(mFhpTR1tRU z3eXr;G_{8DnRbc1cAnGs55!J}*EoAsp zfdAV#0g>StDz$N%{pW;Kk6@$+tR=`-gJZ}<>x8k( z)K98`zBpg%j}4ROE2BU2I>_Wm;+dP}3^Y?-7?talR;!egKn0&3>Cb~0S^pqNwE?T) z?_%;s@k~JQ&8R~6=M2fel&tK3XD(LtDr!5plj%0G7-EM0W#x#TM~S(7H<>@8z`%GJ zZDkqj@I(-^#|tKm6F|OBRxP7cDEkOq%0$bZq@m2SDg|p{Sf1!cvc^iY1$Kl=hh!^6-u=fKdJzsH$?KIH}1QR+k zjKj?m1>MWb&{7|za0B&KV40PdlWzIaaP>4M50|QCM}I9$x2|aZwU%o(zE3KIx&O&& z2bnmYdDa6wCG(m9u1FiD>>SE3HYK{Zm8Fygx}6Q#5Jb_{Q+=Lu}q%-e;6?m4!;<;^NA0 zK!lnq?pJV-Ov{3dia8p5Z-+hY{ESQVbko>*kkEJPO|7AL?V&DxSZva^&4^R$+qG4tlQ7O8f^oFTlXe8G4xY}Ki@78|&W2ywqg<6Dso60r5#{XSF z3}J$JWDW3z-Hp5741A_7ksG$D%zP*GePET-kxBS0C)dg%6&afle!qQ>q`LwIpST@= z`ehpznWj!4vM4JqiirgTgCG3xE7Ir%rZwi zYt||!n)x+@%Qf17y_HK`*KP1U3W~2&S=3m@1V7=*czk?^hlL)P2rYs}eZK=KZlA3s zd43T1F-Rk>(#G3Gr7OIeW?1{Yq)%3)62je9FQINaaIhbV@!_K3me4%) zPtblK`qfhniyqOrTr8aMEJc~lu#^F`6QC7)`uC1kKru>$Bz!-qrqyCku#z^Oo zVU~}UBC(FT7M~jD+Sz$4_)dz+Nw%6Y*K_Z-?iq5GJ8s?+GybO&_Wu_U=qpzS398UO z@$P8tmRj->+@Gt1xlFLJ+`o{%66y+Ss_`qRb4ZoTFNQY49UIFwSYT)z0I$K~Vj9}8 zvsJ;^Y=q+3cghgiR=0rweF&l)aGHgy1JSW%8sI*ZK)F=z$FJ?R1{0 zwwH-OBx`TxGDKt9vaw#^;E*4Bu{XxywR8J}Tb!ns5FFx@#GfqLZ+*ur3_-rZ+ac2P z;Qy1R?}KE^yCR+24Ai;93ysUe7ZZ|)8?VE>6B7=`ZQfnI>tPE49pU}<-flGp_8{Ay zFgGP7X$!rpoty2HAdo6LdjBX6S=*fI>yI^�+{vA(qxh{2{7cnIqun4b^B3Gbyn1 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 literal 0 HcmV?d00001 From 256286c55fb22dc1bfad6032c4d10b75be1ce274 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 16 Mar 2022 10:37:51 +0100 Subject: [PATCH 253/302] fix tooltip of buttons in scene inventory --- openpype/tools/sceneinventory/window.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/sceneinventory/window.py b/openpype/tools/sceneinventory/window.py index 83e4435015..b40fbb69e4 100644 --- a/openpype/tools/sceneinventory/window.py +++ b/openpype/tools/sceneinventory/window.py @@ -61,7 +61,7 @@ class SceneInventoryWindow(QtWidgets.QDialog): icon = qtawesome.icon("fa.refresh", color="white") refresh_button = QtWidgets.QPushButton(self) - update_all_button.setToolTip("Refresh") + refresh_button.setToolTip("Refresh") refresh_button.setIcon(icon) control_layout = QtWidgets.QHBoxLayout() From a11fe7a5503c993b53c72c16eb306d3447ead29a Mon Sep 17 00:00:00 2001 From: 2-REC Date: Wed, 16 Mar 2022 16:38:17 +0700 Subject: [PATCH 254/302] Fix to allow more than 1 shape with same ids --- openpype/hosts/maya/api/lib.py | 40 ++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b46eff5a4b..f0f6bb706f 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1989,23 +1989,35 @@ def get_id_from_sibling(node, history_only=True): # Exclude itself similar_nodes = [x for x in similar_nodes if x != node] - first_id = None - found_node = None + + # Get all unique ids from siblings in order since + # we consistently take the first one found + sibling_ids = OrderedDict() for similar_node in similar_nodes: # Check if "intermediate object" - if cmds.getAttr(similar_node + ".intermediateObject"): - _id = get_id(similar_node) - if _id: - # Check if already found an id - if first_id: - log.warning(("Found more than 1 matching intermediate" - " shape for '{}'. Using id of first" - " found: '{}'".format(node, found_node))) - break - first_id = _id - found_node = similar_node + if not cmds.getAttr(similar_node + ".intermediateObject"): + continue - return first_id + _id = get_id(similar_node) + if not _id: + continue + + if _id in sibling_ids: + sibling_ids[_id].append(similar_node) + else: + sibling_ids[_id] = [similar_node] + + if sibling_ids: + first_id, found_nodes = next(iter(sibling_ids.items())) + + # Log a warning if we've found multiple unique ids + if len(sibling_ids) > 1: + log.warning(("Found more than 1 intermediate shape with" + " unique id for '{}'. Using id of first" + " found: '{}'".format(node, found_nodes[0]))) + break + + return first_id From 9e4a3cf9504ebbb09c30d3c1d2bcd772eed1cf4d Mon Sep 17 00:00:00 2001 From: 2-REC Date: Wed, 16 Mar 2022 16:41:27 +0700 Subject: [PATCH 255/302] Distraction fix... --- openpype/hosts/maya/api/lib.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index f0f6bb706f..f7507d87c5 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1989,7 +1989,6 @@ def get_id_from_sibling(node, history_only=True): # Exclude itself similar_nodes = [x for x in similar_nodes if x != node] - # Get all unique ids from siblings in order since # we consistently take the first one found sibling_ids = OrderedDict() @@ -2015,7 +2014,6 @@ def get_id_from_sibling(node, history_only=True): log.warning(("Found more than 1 intermediate shape with" " unique id for '{}'. Using id of first" " found: '{}'".format(node, found_nodes[0]))) - break return first_id From f130e30a2cd6188a265513663c3548268760c9de Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Wed, 16 Mar 2022 10:45:11 +0100 Subject: [PATCH 256/302] fix missing import of 'get_repres_contexts' --- openpype/pipeline/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/pipeline/__init__.py b/openpype/pipeline/__init__.py index e204eea239..26970e4edc 100644 --- a/openpype/pipeline/__init__.py +++ b/openpype/pipeline/__init__.py @@ -31,6 +31,7 @@ from .load import ( loaders_from_representation, get_representation_path, + get_repres_contexts, ) from .publish import ( @@ -75,6 +76,7 @@ __all__ = ( "loaders_from_representation", "get_representation_path", + "get_repres_contexts", # --- Publish --- "PublishValidationError", From 78ae6c1c86ab5a8accce4906bf1eabf87ba4a607 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 16 Mar 2022 11:14:20 +0100 Subject: [PATCH 257/302] OP-2813 - fixed one too many frame after loaded clip in Nuke For 0-229 range it previously produced 229 - 0 + 1 = 230 (duration). last = 1 + 230 = 231 (should be 230). --- openpype/hosts/nuke/plugins/load/load_clip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index a253ba4a9d..ce1693f700 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -97,7 +97,7 @@ class LoadClip(plugin.NukeLoader): last += self.handle_end if not is_sequence: - duration = last - first + 1 + duration = last - first first = 1 last = first + duration elif "#" not in file: @@ -212,7 +212,7 @@ class LoadClip(plugin.NukeLoader): last += self.handle_end if not is_sequence: - duration = last - first + 1 + duration = last - first first = 1 last = first + duration elif "#" not in file: From 8af535adba3303ae759638f9933cb68ec46517bb Mon Sep 17 00:00:00 2001 From: 2-REC Date: Wed, 16 Mar 2022 18:09:50 +0700 Subject: [PATCH 258/302] More adapted error message --- .../publish/validate_animation_out_set_related_node_ids.py | 4 ++-- .../maya/plugins/publish/validate_rig_out_set_node_ids.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py index 7c1c695237..05d63f1d56 100644 --- a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py @@ -32,8 +32,8 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): # if a deformer has been created on the shape invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Nodes found with non-related " - "asset IDs: {0}".format(invalid)) + raise RuntimeError("Nodes found with mismatching " + "IDs: {0}".format(invalid)) @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index ed1d36261a..cc3723a6e1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -33,8 +33,8 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): # if a deformer has been created on the shape invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Nodes found with non-related " - "asset IDs: {0}".format(invalid)) + raise RuntimeError("Nodes found with mismatching " + "IDs: {0}".format(invalid)) @classmethod def get_invalid(cls, instance): From 87b44b4b14c989c7dce61492650fb54202c37ee0 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 16 Mar 2022 13:48:50 +0100 Subject: [PATCH 259/302] OP-2813 - fix collect_frames when multiple version numbers in path Added new test case. --- openpype/lib/delivery.py | 8 +++----- tests/unit/openpype/lib/test_delivery.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index ee21b01854..b9f3f0b106 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -25,10 +25,11 @@ def collect_frames(files): collections, remainder = clique.assemble(files, minimum_items=1) real_file_name = None + sources_and_frames = {} if len(files) == 1: real_file_name = list(files)[0] + sources_and_frames[real_file_name] = None - sources_and_frames = {} if collections: for collection in collections: src_head = collection.head @@ -36,10 +37,7 @@ def collect_frames(files): # version recognized as a collection if re.match(".*([^a-zA-Z0-9]v%[0-9]+d).*", collection.format()): - if len(collections) > 1: - continue - else: - return {real_file_name: None} + continue for index in collection.indexes: src_frame = collection.format("{padding}") % index diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py index 1787286032..de87f99d79 100644 --- a/tests/unit/openpype/lib/test_delivery.py +++ b/tests/unit/openpype/lib/test_delivery.py @@ -47,6 +47,18 @@ def test_collect_frames_single_sequence(): assert ret == expected, "Not matching" +def test_collect_frames_single_sequence_full_path(): + files = ['C:/test_project/assets/locations/Town/work/compositing\\renders\\aftereffects\\test_project_TestAsset_compositing_v001\\TestAsset_renderCompositingMain_v001.mov'] # noqa: E501 + ret = collect_frames(files) + + expected = { + 'C:/test_project/assets/locations/Town/work/compositing\\renders\\aftereffects\\test_project_TestAsset_compositing_v001\\TestAsset_renderCompositingMain_v001.mov': None # noqa: E501 + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence_different_format(): files = ["Asset.v001.renderCompositingMain_0000.png"] ret = collect_frames(files) From 819e44976ce9338dfd2f835410b512404b2b2e1f Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Wed, 16 Mar 2022 14:03:35 +0100 Subject: [PATCH 260/302] add ember light --- website/src/pages/index.js | 7 ++++++- website/static/img/EmberLight_black.png | Bin 0 -> 59148 bytes website/static/img/Ember_Light.jpg | Bin 0 -> 338284 bytes 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 website/static/img/EmberLight_black.png create mode 100644 website/static/img/Ember_Light.jpg diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 902505e134..791b309bbc 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -139,6 +139,11 @@ const studios = [ title: "Overmind Studios", image: "/img/OMS_logo_black_color.png", infoLink: "https://www.overmind-studios.de/", + }, + { + title: "Ember Light", + image: "/img/EmberLight_black.png", + infoLink: "https://emberlight.se/", } ]; @@ -433,7 +438,7 @@ function Home() { {studios && studios.length && (

    -

    Studios using openPYPE

    +

    Studios using openPype

    {studios.map((props, idx) => ( diff --git a/website/static/img/EmberLight_black.png b/website/static/img/EmberLight_black.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a9f95d06844081674f497e8c75625d5fe41a33 GIT binary patch literal 59148 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%0Aur1;kkT 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 literal 0 HcmV?d00001 diff --git a/website/static/img/Ember_Light.jpg b/website/static/img/Ember_Light.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9081bc7ceac5ff2a307de12e26f7e2d14e596692 GIT binary patch literal 338284 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%0De7HRMH@RI&3UlX? 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 R+%O^wmIH8%4u*i-|JiSp?~VWf literal 0 HcmV?d00001 From 033eaa324ffec6dce7d5f44dcfe84464a20c961d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 15:10:38 +0100 Subject: [PATCH 261/302] nuke: imageio adding ocio config version 1.2 --- .../projects_schema/schemas/schema_anatomy_imageio.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index 3bec19c3d0..6532f2b6ce 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -195,6 +195,9 @@ { "aces_1.1": "aces_1.1" }, + { + "aces_1.1": "aces_1.2" + }, { "custom": "custom" } From 8b3d32c5b26733cf2da44cedc164f478f8045fca Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 15:42:49 +0100 Subject: [PATCH 262/302] global: fixing order of output resolution flow --- openpype/plugins/publish/extract_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index f046194c0d..cbe1924408 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -1188,8 +1188,8 @@ class ExtractReview(pyblish.api.InstancePlugin): # NOTE Setting only one of `width` or `heigth` is not allowed # - settings value can't have None but has value of 0 - output_width = output_width or output_def.get("width") or None - output_height = output_height or output_def.get("height") or None + output_width = output_def.get("width") or output_width or None + output_height = output_def.get("height") or output_height or None # Overscal color overscan_color_value = "black" From 2563a7ce607db3e2eddd83a70832a02b6e6065b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 16 Mar 2022 16:36:46 +0100 Subject: [PATCH 263/302] Update openpype/hosts/flame/plugins/publish/extract_subset_resources.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../plugins/publish/extract_subset_resources.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 5c3aed9672..3b1466925f 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -218,16 +218,10 @@ class ExtractSubsetResources(openpype.api.Extractor): # imagesequence as list if ( # first check if path in files is not mov extension - next( - # iter all paths in files - # return only .mov positive test - iter([ - f for f in files - if ".mov" in os.path.splitext(f)[-1] - ]), - # if nothing return default - None - ) + [ + f for f in files + if os.path.splitext(f)[-1] == ".mov" + ] # then try if thumbnail is not in unique name or unique_name == "thumbnail" ): From d867b872a894986579709718e2894596ed9e527a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 16:55:51 +0100 Subject: [PATCH 264/302] flame: distribute better value types --- .../publish/collect_timeline_instances.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 72ad2cd1c3..44c25f04a2 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -26,7 +26,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): xml_preset_attrs_from_comments = { "width": "number", "height": "number", - "pixelRatio": "number", + "pixelRatio": "float", "resizeType": "string", "resizeFilter": "string" } @@ -183,11 +183,14 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): for a_name, a_type in self.xml_preset_attrs_from_comments.items(): # exclude all not related attributes - if a_name.lower() not in key: + if a_name.lower() not in key.lower(): continue # get pattern defined by type - pattern = TXT_PATERN if "string" in a_type else NUM_PATERN + pattern = TXT_PATERN + if "number" in a_type or "float" in a_type: + pattern = NUM_PATERN + res_goup = pattern.findall(value) # raise if nothing is found as it is not correctly defined @@ -196,7 +199,14 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): "Value for `{}` attribute is not " "set correctly: `{}`").format(a_name, split)) - attributes["xml_overrides"][a_name] = res_goup[0] + if "string" in a_type: + _value = res_goup[0] + if "float" in a_type: + _value = float(res_goup[0]) + if "number" in a_type: + _value = int(res_goup[0]) + + attributes["xml_overrides"][a_name] = _value # condition for resolution in key if "resolution" in key.lower(): From d98d8905afb1ae3a28af03904adc6b4e57114fff Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 17:10:56 +0100 Subject: [PATCH 265/302] Flame: add ignoring toggle to settings parsed attributes from comments can be ignored now --- .../plugins/publish/extract_subset_resources.py | 14 +++++++++----- .../settings/defaults/project_settings/flame.json | 1 + .../projects_schema/schema_project_flame.json | 11 +++++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index 2e3b84def8..ac50c7c980 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -25,6 +25,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "xml_preset_file": "Jpeg (8-bit).xml", "xml_preset_dir": "", "export_type": "File Sequence", + "ignore_comment_attrs": True, "colorspace_out": "Output - sRGB", "representation_add_range": False, "representation_tags": ["thumbnail"] @@ -34,6 +35,7 @@ class ExtractSubsetResources(openpype.api.Extractor): "xml_preset_file": "Apple iPad (1920x1080).xml", "xml_preset_dir": "", "export_type": "Movie", + "ignore_comment_attrs": True, "colorspace_out": "Output - Rec.709", "representation_add_range": True, "representation_tags": [ @@ -104,6 +106,7 @@ class ExtractSubsetResources(openpype.api.Extractor): preset_dir = preset_config["xml_preset_dir"] export_type = preset_config["export_type"] repre_tags = preset_config["representation_tags"] + ignore_comment_attrs = preset_config["ignore_comment_attrs"] color_out = preset_config["colorspace_out"] # get frame range with handles for representation range @@ -133,11 +136,12 @@ class ExtractSubsetResources(openpype.api.Extractor): "startFrame": frame_start }) - # add any xml overrides collected form segment.comment - modify_xml_data.update(instance.data["xml_overrides"]) - self.log.debug("__ modify_xml_data: {}".format(pformat( - modify_xml_data - ))) + if not ignore_comment_attrs: + # add any xml overrides collected form segment.comment + modify_xml_data.update(instance.data["xml_overrides"]) + self.log.debug("__ modify_xml_data: {}".format(pformat( + modify_xml_data + ))) # with maintained duplication loop all presets with opfapi.maintained_object_duplication( diff --git a/openpype/settings/defaults/project_settings/flame.json b/openpype/settings/defaults/project_settings/flame.json index ef9c2b1041..c7188b10b5 100644 --- a/openpype/settings/defaults/project_settings/flame.json +++ b/openpype/settings/defaults/project_settings/flame.json @@ -28,6 +28,7 @@ "xml_preset_file": "OpenEXR (16-bit fp DWAA).xml", "xml_preset_dir": "", "export_type": "File Sequence", + "ignore_comment_attrs": false, "colorspace_out": "ACES - ACEScg", "representation_add_range": true, "representation_tags": [] diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json index 1f30b45981..e352f8b132 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_flame.json @@ -189,6 +189,17 @@ ] }, + { + "type": "separator" + }, + { + "type": "boolean", + "key": "ignore_comment_attrs", + "label": "Ignore attributes parsed from a segment comments" + }, + { + "type": "separator" + }, { "key": "colorspace_out", "label": "Output color (imageio)", From 4b83446230d54a804fd2a509a709abab463c44cc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 17:42:27 +0100 Subject: [PATCH 266/302] flame: moving logging outside of condition --- .../flame/plugins/publish/extract_subset_resources.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index ac50c7c980..d52669d955 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -139,9 +139,10 @@ class ExtractSubsetResources(openpype.api.Extractor): if not ignore_comment_attrs: # add any xml overrides collected form segment.comment modify_xml_data.update(instance.data["xml_overrides"]) - self.log.debug("__ modify_xml_data: {}".format(pformat( - modify_xml_data - ))) + + self.log.debug("__ modify_xml_data: {}".format(pformat( + modify_xml_data + ))) # with maintained duplication loop all presets with opfapi.maintained_object_duplication( From d0a79e31f5afb8dcdd5bbcf7d376b89c98d29456 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 17:44:01 +0100 Subject: [PATCH 267/302] hound and suggested changes --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 4 ++-- .../hosts/flame/plugins/publish/extract_subset_resources.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index 44c25f04a2..c6793874c0 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -228,9 +228,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # first split comment by comma split_comments = [] if "," in comment_string: - split_comments.extend(iter(comment_string.split(","))) + split_comments.extend(comment_string.split(",")) elif ";" in comment_string: - split_comments.extend(iter(comment_string.split(";"))) + split_comments.extend(comment_string.split(";")) else: split_comments.append(comment_string) diff --git a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py index d52669d955..32f6b9508f 100644 --- a/openpype/hosts/flame/plugins/publish/extract_subset_resources.py +++ b/openpype/hosts/flame/plugins/publish/extract_subset_resources.py @@ -5,7 +5,6 @@ from copy import deepcopy import pyblish.api import openpype.api from openpype.hosts.flame import api as opfapi -from pprint import pformat class ExtractSubsetResources(openpype.api.Extractor): From 550f0603d4865da46e8878355208c0a4ff8f639d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 16 Mar 2022 20:47:21 +0100 Subject: [PATCH 268/302] fixing ocio config name --- .../schemas/projects_schema/schemas/schema_anatomy_imageio.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json index 6532f2b6ce..acfd4602df 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_imageio.json @@ -196,7 +196,7 @@ "aces_1.1": "aces_1.1" }, { - "aces_1.1": "aces_1.2" + "aces_1.2": "aces_1.2" }, { "custom": "custom" From 3d426d1d8f90764d35b56316dd81522bb8e6e39d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 17 Mar 2022 00:14:00 +0100 Subject: [PATCH 269/302] Fix #2834 - ensure current state is correct when entering new group order --- openpype/tools/pyblish_pype/control.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/tools/pyblish_pype/control.py b/openpype/tools/pyblish_pype/control.py index 6f89952c22..f657936b79 100644 --- a/openpype/tools/pyblish_pype/control.py +++ b/openpype/tools/pyblish_pype/control.py @@ -389,6 +389,9 @@ class Controller(QtCore.QObject): new_current_group_order ) + # Force update to the current state + self._set_state_by_order() + if self.collect_state == 0: self.collect_state = 1 self._current_state = ( From d14d9eecc86f090bdc4478161da111688e06a581 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 12:44:30 +0100 Subject: [PATCH 270/302] added simple tooltips for settings entities --- openpype/settings/entities/base_entity.py | 4 ++++ openpype/settings/entities/schemas/README.md | 1 + openpype/tools/settings/settings/base.py | 3 +++ 3 files changed, 8 insertions(+) diff --git a/openpype/settings/entities/base_entity.py b/openpype/settings/entities/base_entity.py index b5bc44640b..76700d605d 100644 --- a/openpype/settings/entities/base_entity.py +++ b/openpype/settings/entities/base_entity.py @@ -28,6 +28,10 @@ class BaseEntity: def __init__(self, schema_data, *args, **kwargs): self.schema_data = schema_data + tooltip = None + if schema_data: + tooltip = schema_data.get("tooltip") + self.tooltip = tooltip # Entity id self._id = uuid4() diff --git a/openpype/settings/entities/schemas/README.md b/openpype/settings/entities/schemas/README.md index dd7601c017..fbfd699937 100644 --- a/openpype/settings/entities/schemas/README.md +++ b/openpype/settings/entities/schemas/README.md @@ -14,6 +14,7 @@ - this keys is not allowed for all inputs as they may have not reason for that - key is validated, can be only once in hierarchy but is not required - currently there are `system settings` and `project settings` +- all entities can have set `"tooltip"` key with description which will be shown in UI ## Inner schema - GUI schemas are huge json files, to be able to split whole configuration into multiple schema there's type `schema` diff --git a/openpype/tools/settings/settings/base.py b/openpype/tools/settings/settings/base.py index 706e2fdcf0..bd48b3a966 100644 --- a/openpype/tools/settings/settings/base.py +++ b/openpype/tools/settings/settings/base.py @@ -30,6 +30,9 @@ class BaseWidget(QtWidgets.QWidget): if not self.entity.gui_type: self.entity.on_change_callbacks.append(self._on_entity_change) + if self.entity.tooltip: + self.setToolTip(self.entity.tooltip) + self.label_widget = None self.create_ui() From fdb880c5440568e1f5f1a8fdc539ae7ddcad15f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Thu, 17 Mar 2022 12:57:34 +0100 Subject: [PATCH 271/302] Update openpype/hosts/flame/plugins/publish/collect_timeline_instances.py Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- .../hosts/flame/plugins/publish/collect_timeline_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py index c6793874c0..70340ad7a2 100644 --- a/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py +++ b/openpype/hosts/flame/plugins/publish/collect_timeline_instances.py @@ -188,7 +188,7 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin): # get pattern defined by type pattern = TXT_PATERN - if "number" in a_type or "float" in a_type: + if a_type in ("number" , "float"): pattern = NUM_PATERN res_goup = pattern.findall(value) From 0ea4e0acd4f78899df9e2ba6932a11beb88283dc Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Thu, 17 Mar 2022 12:59:30 +0100 Subject: [PATCH 272/302] improving gap detection in extract review --- openpype/plugins/publish/extract_review.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index cbe1924408..3ecea1f8bd 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -747,10 +747,14 @@ class ExtractReview(pyblish.api.InstancePlugin): collections = clique.assemble(files)[0] assert len(collections) == 1, "Multiple collections found." col = collections[0] - # do nothing if sequence is complete - if list(col.indexes)[0] == start_frame and \ - list(col.indexes)[-1] == end_frame and \ - col.is_contiguous(): + + # do nothing if no gap is found in input range + not_gap = True + for fr in range(start_frame, end_frame + 1): + if fr not in col.indexes: + not_gap = False + + if not_gap: return [] holes = col.holes() From 338aac4de6b0cc37a98e624726611fdd1af5a6e7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 13:05:53 +0100 Subject: [PATCH 273/302] ignore 'team' entities in process event --- openpype/modules/ftrack/lib/ftrack_event_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/lib/ftrack_event_handler.py b/openpype/modules/ftrack/lib/ftrack_event_handler.py index af565c5421..0a70b0e301 100644 --- a/openpype/modules/ftrack/lib/ftrack_event_handler.py +++ b/openpype/modules/ftrack/lib/ftrack_event_handler.py @@ -44,7 +44,7 @@ class BaseEvent(BaseHandler): return self._get_entities( event, session, - ignore=['socialfeed', 'socialnotification'] + ignore=['socialfeed', 'socialnotification', 'team'] ) def get_project_name_from_event(self, session, event, project_id): From 3420c68796a6d8aa6f6dc22c3584aed931c0662d Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 13:06:12 +0100 Subject: [PATCH 274/302] use 'first' instead of 'one' when querying user and task --- .../ftrack/event_handlers_server/event_user_assigment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/event_user_assigment.py b/openpype/modules/ftrack/event_handlers_server/event_user_assigment.py index efc1e76775..96243c8c36 100644 --- a/openpype/modules/ftrack/event_handlers_server/event_user_assigment.py +++ b/openpype/modules/ftrack/event_handlers_server/event_user_assigment.py @@ -87,8 +87,8 @@ class UserAssigmentEvent(BaseEvent): if not user_id: return None, None - task = session.query('Task where id is "{}"'.format(task_id)).one() - user = session.query('User where id is "{}"'.format(user_id)).one() + task = session.query('Task where id is "{}"'.format(task_id)).first() + user = session.query('User where id is "{}"'.format(user_id)).first() return task, user From eaae7f4828ba68b1e4b11f688357a9bd13c46ec1 Mon Sep 17 00:00:00 2001 From: OpenPype Date: Thu, 17 Mar 2022 13:33:11 +0000 Subject: [PATCH 275/302] [Automated] Bump version --- CHANGELOG.md | 11 ++++++++++- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7790894b7f..6a1da69f13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,28 @@ # Changelog -## [3.9.1-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.1-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...HEAD) +**🚀 Enhancements** + +- Nuke: ExtractReviewSlate can handle more codes and profiles [\#2879](https://github.com/pypeclub/OpenPype/pull/2879) +- Flame: sequence used for reference video [\#2869](https://github.com/pypeclub/OpenPype/pull/2869) + **🐛 Bug fixes** +- Pyblish Pype - ensure current state is correct when entering new group order [\#2899](https://github.com/pypeclub/OpenPype/pull/2899) +- SceneInventory: Fix import of load function [\#2894](https://github.com/pypeclub/OpenPype/pull/2894) - Harmony - fixed creator issue [\#2891](https://github.com/pypeclub/OpenPype/pull/2891) - General: Remove forgotten use of avalon Creator [\#2885](https://github.com/pypeclub/OpenPype/pull/2885) - General: Avoid circular import [\#2884](https://github.com/pypeclub/OpenPype/pull/2884) - Fixes for attaching loaded containers \(\#2837\) [\#2874](https://github.com/pypeclub/OpenPype/pull/2874) +- Maya: Deformer node ids validation plugin [\#2826](https://github.com/pypeclub/OpenPype/pull/2826) **🔀 Refactored code** - General: Reduce style usage to OpenPype repository [\#2889](https://github.com/pypeclub/OpenPype/pull/2889) +- General: Move loader logic from avalon to openpype [\#2886](https://github.com/pypeclub/OpenPype/pull/2886) ## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14) diff --git a/openpype/version.py b/openpype/version.py index 17e514642d..5eca7c1d90 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.1-nightly.1" +__version__ = "3.9.1-nightly.2" diff --git a/pyproject.toml b/pyproject.toml index 128d1cd615..af448ed24c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.1-nightly.1" # OpenPype +version = "3.9.1-nightly.2" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From e208e7976d4f69207bedba5d55a0c925ac6e6b38 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Mar 2022 14:57:28 +0100 Subject: [PATCH 276/302] OP-2813 - fixed duplication of representations nuke.api.plugin.ExporterReview adds representation explicitly via publish_on_farm, so skip adding repre if already there. (Issue in ExtractBurnin other way.) ExporterReview should be probably refactored and publish_on_farm removed altogether. --- .../deadline/plugins/publish/submit_publish_job.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index b92fd2fe69..8c0d78cae5 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -601,13 +601,22 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): "files": os.path.basename(remainder), "stagingDir": os.path.dirname(remainder), } - representations.append(rep) if "render" in instance.get("families"): rep.update({ "fps": instance.get("fps"), "tags": ["review"] }) - self._solve_families(instance, True) + self._solve_families(instance, True) + + already_there = False + for repre in instance.get("representations", []): + # might be added explicitly before by publish_on_farm + already_there = repre.get("files") == rep["files"] + if already_there: + break + self.log.debug("repre {} already_there".format(repre)) + if not already_there: + representations.append(rep) return representations From 72f84c52baf7fed7b31fd59a995880a5bf5a41b9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 15:06:24 +0100 Subject: [PATCH 277/302] handle missing ftrack id in more cases --- .../event_sync_to_avalon.py | 72 ++++++++++++++++--- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py index eea6436b53..237bf9fd80 100644 --- a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py +++ b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py @@ -199,8 +199,10 @@ class SyncToAvalonEvent(BaseEvent): if proj: ftrack_id = proj["data"].get("ftrackId") if ftrack_id is None: - ftrack_id = self._update_project_ftrack_id() - proj["data"]["ftrackId"] = ftrack_id + self.handle_missing_ftrack_id(proj) + ftrack_id = proj["data"]["ftrackId"] + self._avalon_ents_by_ftrack_id[ftrack_id] = proj + self._avalon_ents_by_ftrack_id[ftrack_id] = proj for ent in ents: ftrack_id = ent["data"].get("ftrackId") @@ -209,15 +211,56 @@ class SyncToAvalonEvent(BaseEvent): self._avalon_ents_by_ftrack_id[ftrack_id] = ent return self._avalon_ents_by_ftrack_id - def _update_project_ftrack_id(self): - ftrack_id = self.cur_project["id"] + def handle_missing_ftrack_id(self, doc): + ftrack_id = doc["data"].get("ftrackId") + if ftrack_id is not None: + return + if doc["type"] == "project": + ftrack_id = self.cur_project["id"] + + self.dbcon.update_one( + {"type": "project"}, + {"$set": {"data.ftrackId": ftrack_id}} + ) + + doc["data"]["ftrackId"] = ftrack_id + return + + if doc["type"] != "asset": + return + + doc_parents = doc.get("data", {}).get("parents") + if doc_parents is None: + return + + entities = self.process_session.query(( + "select id, link from TypedContext" + " where project_id is \"{}\" and name is \"{}\"" + ).format(self.cur_project["id"], doc["name"])).all() + matching_entity = None + for entity in entities: + parents = [] + for item in entity["link"]: + if item["id"] == entity["id"]: + break + low_type = item["type"].lower() + if low_type == "typedcontext": + parents.append(item["name"]) + if doc_parents == parents: + matching_entity = entity + break + + if matching_entity is None: + return + + ftrack_id = matching_entity["id"] self.dbcon.update_one( - {"type": "project"}, + {"_id": doc["_id"]}, {"$set": {"data.ftrackId": ftrack_id}} ) - return ftrack_id + self._avalon_ents_by_ftrack_id[ftrack_id] = doc @property def avalon_subsets_by_parents(self): @@ -857,7 +900,14 @@ class SyncToAvalonEvent(BaseEvent): if vis_par is None: vis_par = proj["_id"] parent_ent = self.avalon_ents_by_id[vis_par] - parent_ftrack_id = parent_ent["data"]["ftrackId"] + + parent_ftrack_id = parent_ent["data"].get("ftrackId") + if parent_ftrack_id is None: + self.handle_missing_ftrack_id(parent_ent) + parent_ftrack_id = parent_ent["data"].get("ftrackId") + if parent_ftrack_id is None: + continue + parent_ftrack_ent = self.ftrack_ents_by_id.get( parent_ftrack_id ) @@ -2128,7 +2178,13 @@ class SyncToAvalonEvent(BaseEvent): vis_par = avalon_ent["parent"] parent_ent = self.avalon_ents_by_id[vis_par] - parent_ftrack_id = parent_ent["data"]["ftrackId"] + parent_ftrack_id = parent_ent["data"].get("ftrackId") + if parent_ftrack_id is None: + self.handle_missing_ftrack_id(parent_ent) + parent_ftrack_id = parent_ent["data"].get("ftrackId") + if parent_ftrack_id is None: + continue + if parent_ftrack_id not in entities_dict: entities_dict[parent_ftrack_id] = { "children": [], From 9cc9c1afcbcd28557d72cbd50984ef8990eff52b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 16:04:28 +0100 Subject: [PATCH 278/302] added settings for new action --- .../defaults/project_settings/ftrack.json | 5 ++++ .../schema_project_ftrack.json | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index 01831efad1..89bb41a164 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -193,6 +193,11 @@ "Administrator" ] }, + "fill_workfile_attribute": { + "enabled": false, + "custom_attribute_key": "", + "role_list": [] + }, "seed_project": { "enabled": true, "role_list": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json index 6d0e2693d4..cb59e9d67e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json @@ -589,6 +589,34 @@ } ] }, + { + "type": "dict", + "key": "fill_workfile_attribute", + "label": "Fill workfile Custom attribute", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "label", + "label": "Custom attribute must be Text type added to Task entity type" + }, + { + "type": "text", + "key": "custom_attribute_key", + "label": "Custom attribute key" + }, + { + "type": "list", + "key": "role_list", + "label": "Roles", + "object_type": "text" + } + ] + }, { "type": "dict", "key": "seed_project", From 1cdbe4568ee7ea7c4d72b96e3f434e072973f05b Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 16:06:50 +0100 Subject: [PATCH 279/302] initial commit of new action for filling workfile name in custom attribute --- .../action_fill_workfile_attr.py | 289 ++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py diff --git a/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py new file mode 100644 index 0000000000..a72b29bdbe --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py @@ -0,0 +1,289 @@ +import collections + +import ftrack_api + +from avalon.api import AvalonMongoDB +from openpype.api import get_project_settings +from openpype.lib import ( + get_workfile_template_key, + get_workdir_data, + Anatomy, + StringTemplate, +) +from openpype_modules.ftrack.lib import BaseAction, statics_icon +from openpype_modules.ftrack.lib.avalon_sync import create_chunks + + +class FillWorkfileAttributeAction(BaseAction): + """Action fill work filename into custom attribute on tasks. + + Prerequirements are that the project is synchronized so it is possible to + access project anatomy and project/asset documents. Tasks that are not + synchronized are skipped too. + """ + + identifier = "fill.workfile.attr" + label = "OpenPype Admin" + variant = "- Fill workfile attribute" + description = "Precalculate and fill workfile name into a custom attribute" + icon = statics_icon("ftrack", "action_icons", "OpenPypeAdmin.svg") + + settings_key = "fill_workfile_attribute" + + def discover(self, session, entities, event): + """ Validate selection. """ + is_valid = False + for ent in event["data"]["selection"]: + # Ignore entities that are not tasks or projects + if ent["entityType"].lower() in ["show", "task"]: + is_valid = True + break + + if is_valid: + is_valid = self.valid_roles(session, entities, event) + return is_valid + + def launch(self, session, entities, event): + task_entities = [] + other_entities = [] + project_entity = None + project_selected = False + for entity in entities: + if project_entity is None: + project_entity = self.get_project_from_entity(entity) + + ent_type_low = entity.entity_type.lower() + if ent_type_low == "project": + project_selected = True + break + + elif ent_type_low == "task": + task_entities.append(entity) + else: + other_entities.append(entity) + + project_name = project_entity["full_name"] + project_settings = get_project_settings(project_name) + custom_attribute_key = ( + project_settings + .get("ftrack", {}) + .get("user_handlers", {}) + .get(self.settings_key, {}) + .get("custom_attribute_key") + ) + if not custom_attribute_key: + return { + "success": False, + "message": "Custom attribute key is not set in settings" + } + + task_obj_type = session.query( + "select id from ObjectType where name is \"Task\"" + ).one() + text_type = session.query( + "select id from CustomAttributeType where name is \"text\"" + ).one() + attr_conf = session.query( + ( + "select id, key from CustomAttributeConfiguration" + " where object_type_id is \"{}\"" + " and type_id is \"{}\"" + " and key is \"{}\"" + ).format( + task_obj_type["id"], text_type["id"], custom_attribute_key + ) + ).first() + if not attr_conf: + return { + "success": False, + "message": ( + "Could not find Task (text) Custom attribute \"{}\"" + ).format(custom_attribute_key) + } + + dbcon = AvalonMongoDB() + dbcon.Session["AVALON_PROJECT"] = project_name + asset_docs = list(dbcon.find({"type": "asset"})) + if project_selected: + asset_docs_with_task_names = self._get_asset_docs_for_project( + session, project_entity, asset_docs + ) + + else: + asset_docs_with_task_names = self._get_tasks_for_selection( + session, other_entities, task_entities, asset_docs + ) + + host_name = "{host}" + project_doc = dbcon.find_one({"type": "project"}) + project_settings = get_project_settings(project_name) + anatomy = Anatomy(project_name) + templates_by_key = {} + + operations = [] + for asset_doc, task_entities in asset_docs_with_task_names: + for task_entity in task_entities: + workfile_data = get_workdir_data( + project_doc, asset_doc, task_entity["name"], host_name + ) + workfile_data["version"] = 1 + workfile_data["ext"] = "{ext}" + + task_type = workfile_data["task"]["type"] + template_key = get_workfile_template_key( + task_type, host_name, project_settings=project_settings + ) + if template_key in templates_by_key: + template = templates_by_key[template_key] + else: + template = StringTemplate( + anatomy.templates[template_key]["file"] + ) + templates_by_key[template_key] = template + + result = template.format(workfile_data) + if not result.solved: + # TODO report + pass + else: + table_values = collections.OrderedDict(( + ("configuration_id", attr_conf["id"]), + ("entity_id", task_entity["id"]) + )) + operations.append( + ftrack_api.operation.UpdateEntityOperation( + "ContextCustomAttributeValue", + table_values, + "value", + ftrack_api.symbol.NOT_SET, + str(result) + ) + ) + + if operations: + for sub_operations in create_chunks(operations, 50): + for op in sub_operations: + session.recorded_operations.push(op) + session.commit() + + return True + + def _get_asset_docs_for_project(self, session, project_entity, asset_docs): + asset_docs_task_names = collections.defaultdict(list) + for asset_doc in asset_docs: + asset_data = asset_doc["data"] + asset_tasks = asset_data.get("tasks") + ftrack_id = asset_data.get("ftrackId") + if not asset_tasks or not ftrack_id: + continue + asset_docs_task_names[ftrack_id].append( + (asset_doc, list(asset_tasks.keys())) + ) + + task_entities = session.query(( + "select id, name, parent_id from Task where project_id is {}" + ).format(project_entity["id"])).all() + task_entities_by_parent_id = collections.defaultdict(list) + for task_entity in task_entities: + parent_id = task_entity["parent_id"] + task_entities_by_parent_id[parent_id].append(task_entity) + + output = [] + for ftrack_id, items in asset_docs_task_names.items(): + for item in items: + asset_doc, task_names = item + valid_task_entities = [] + for task_entity in task_entities_by_parent_id[ftrack_id]: + if task_entity["name"] in task_names: + valid_task_entities.append(task_entity) + + if valid_task_entities: + output.append((asset_doc, valid_task_entities)) + + return output + + def _get_tasks_for_selection( + self, session, other_entities, task_entities, asset_docs + ): + all_tasks = object() + asset_docs_by_ftrack_id = {} + asset_docs_by_parent_id = collections.defaultdict(list) + for asset_doc in asset_docs: + asset_data = asset_doc["data"] + ftrack_id = asset_data.get("ftrackId") + parent_id = asset_data.get("visualParent") + asset_docs_by_parent_id[parent_id].append(asset_doc) + if ftrack_id: + asset_docs_by_ftrack_id[ftrack_id] = asset_doc + + missing_docs = set() + all_tasks_ids = set() + task_names_by_ftrack_id = collections.defaultdict(list) + for other_entity in other_entities: + ftrack_id = other_entity["id"] + if ftrack_id not in asset_docs_by_ftrack_id: + missing_docs.add(ftrack_id) + continue + all_tasks_ids.add(ftrack_id) + task_names_by_ftrack_id[ftrack_id] = all_tasks + + for task_entity in task_entities: + parent_id = task_entity["parent_id"] + if parent_id not in asset_docs_by_ftrack_id: + missing_docs.add(parent_id) + continue + + if all_tasks_ids not in all_tasks_ids: + task_names_by_ftrack_id[ftrack_id].append(task_entity["name"]) + + ftrack_ids = set() + asset_doc_with_task_names_by_id = collections.defaultdict(list) + for ftrack_id, task_names in task_names_by_ftrack_id.items(): + asset_doc = asset_docs_by_ftrack_id[ftrack_id] + asset_data = asset_doc["data"] + asset_tasks = asset_data.get("tasks") + if not asset_tasks: + # TODO add to report + continue + + if task_names is all_tasks: + task_names = list(asset_tasks.keys()) + else: + new_task_names = [] + for task_name in task_names: + if task_name in asset_tasks: + new_task_names.append(task_name) + else: + # TODO add report + pass + task_names = new_task_names + + if task_names: + ftrack_ids.add(ftrack_id) + asset_doc_with_task_names_by_id[ftrack_id].append( + (asset_doc, task_names) + ) + + task_entities = session.query(( + "select id, name, parent_id from Task where parent_id in ({})" + ).format(self.join_query_keys(ftrack_ids))).all() + task_entitiy_by_parent_id = collections.defaultdict(list) + for task_entity in task_entities: + parent_id = task_entity["parent_id"] + task_entitiy_by_parent_id[parent_id].append(task_entity) + + output = [] + for ftrack_id, items in asset_doc_with_task_names_by_id.items(): + for item in items: + asset_doc, task_names = item + valid_task_entities = [] + for task_entity in task_entitiy_by_parent_id[ftrack_id]: + if task_entity["name"] in task_names: + valid_task_entities.append(task_entity) + if valid_task_entities: + output.append((asset_doc, valid_task_entities)) + return output + + +def register(session): + FillWorkfileAttributeAction(session).register() From 395d567aa2d7285e204ca6fb35a2344e0d7f2f94 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Mar 2022 16:07:08 +0100 Subject: [PATCH 280/302] OP-2813 - fix wrong parsing when short label is used --- openpype/lib/delivery.py | 2 +- tests/unit/openpype/lib/test_delivery.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index b9f3f0b106..78d743003b 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -36,7 +36,7 @@ def collect_frames(files): src_tail = collection.tail # version recognized as a collection - if re.match(".*([^a-zA-Z0-9]v%[0-9]+d).*", collection.format()): + if re.match(".*([a-zA-Z0-9]%[0-9]+d).*", collection.format()): continue for index in collection.indexes: diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py index de87f99d79..871ea95df7 100644 --- a/tests/unit/openpype/lib/test_delivery.py +++ b/tests/unit/openpype/lib/test_delivery.py @@ -47,6 +47,30 @@ def test_collect_frames_single_sequence(): assert ret == expected, "Not matching" +def test_collect_frames_single_sequence_shot(): + files = ["testing_sh010_workfileCompositing_v001.aep"] + ret = collect_frames(files) + + expected = { + "testing_sh010_workfileCompositing_v001.aep": None + } + + print(ret) + assert ret == expected, "Not matching" + + +def test_collect_frames_single_sequence_shot_with_frame(): + files = ["testing_sh010_workfileCompositing_000_v001.aep"] + ret = collect_frames(files) + + expected = { + "testing_sh010_workfileCompositing_000_v001.aep": "000" + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence_full_path(): files = ['C:/test_project/assets/locations/Town/work/compositing\\renders\\aftereffects\\test_project_TestAsset_compositing_v001\\TestAsset_renderCompositingMain_v001.mov'] # noqa: E501 ret = collect_frames(files) From 16f4ada2ad4772debe465231cfb60bf4c22b1f27 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 17:59:56 +0100 Subject: [PATCH 281/302] use 'roots' instead of 'roots_obj' --- openpype/pipeline/load/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index ae47cb9ce9..118f86a570 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -502,7 +502,7 @@ def get_representation_path_from_context(context): session_project = Session.get("AVALON_PROJECT") if project_doc and project_doc["name"] != session_project: anatomy = Anatomy(project_doc["name"]) - root = anatomy.roots_obj + root = anatomy.roots return get_representation_path(representation, root) From d080b17cce56f6d128d45b26f9224db9294dcd5f Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 17 Mar 2022 18:00:25 +0100 Subject: [PATCH 282/302] OP-2813 - fix wrong logging --- .../modules/deadline/plugins/publish/submit_publish_job.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 8c0d78cae5..06505b4b47 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -613,8 +613,9 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): # might be added explicitly before by publish_on_farm already_there = repre.get("files") == rep["files"] if already_there: + self.log.debug("repre {} already_there".format(repre)) break - self.log.debug("repre {} already_there".format(repre)) + if not already_there: representations.append(rep) From cd65332942ee14e9b47ee0608e24f5ae8c189aff Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 18:47:51 +0100 Subject: [PATCH 283/302] fixed filling of ftrack id --- .../event_sync_to_avalon.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py index 237bf9fd80..46c333c4c4 100644 --- a/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py +++ b/openpype/modules/ftrack/event_handlers_server/event_sync_to_avalon.py @@ -212,6 +212,9 @@ class SyncToAvalonEvent(BaseEvent): return self._avalon_ents_by_ftrack_id def handle_missing_ftrack_id(self, doc): + # TODO handling of missing ftrack id is primarily issue of editorial + # publishing it would be better to find out what causes that + # ftrack id is removed during the publishing ftrack_id = doc["data"].get("ftrackId") if ftrack_id is not None: return @@ -221,10 +224,17 @@ class SyncToAvalonEvent(BaseEvent): self.dbcon.update_one( {"type": "project"}, - {"$set": {"data.ftrackId": ftrack_id}} + {"$set": { + "data.ftrackId": ftrack_id, + "data.entityType": self.cur_project.entity_type + }} ) doc["data"]["ftrackId"] = ftrack_id + doc["data"]["entityType"] = self.cur_project.entity_type + self.log.info("Updated ftrack id of project \"{}\"".format( + self.cur_project["full_name"] + )) return if doc["type"] != "asset": @@ -238,6 +248,7 @@ class SyncToAvalonEvent(BaseEvent): "select id, link from TypedContext" " where project_id is \"{}\" and name is \"{}\"" ).format(self.cur_project["id"], doc["name"])).all() + self.log.info("Entities: {}".format(str(entities))) matching_entity = None for entity in entities: parents = [] @@ -257,9 +268,20 @@ class SyncToAvalonEvent(BaseEvent): ftrack_id = matching_entity["id"] self.dbcon.update_one( {"_id": doc["_id"]}, - {"$set": {"data.ftrackId": ftrack_id}} + {"$set": { + "data.ftrackId": ftrack_id, + "data.entityType": matching_entity.entity_type + }} ) + doc["data"]["ftrackId"] = ftrack_id + doc["data"]["entityType"] = matching_entity.entity_type + entity_path_items = [] + for item in entity["link"]: + entity_path_items.append(item["name"]) + self.log.info("Updated ftrack id of entity \"{}\"".format( + "/".join(entity_path_items) + )) self._avalon_ents_by_ftrack_id[ftrack_id] = doc @property From 4dd95fba6842d2b4c4556e2465cb2ef00f70cb1f Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 19:22:35 +0100 Subject: [PATCH 284/302] added job and report messages --- .../action_fill_workfile_attr.py | 319 ++++++++++++++---- 1 file changed, 262 insertions(+), 57 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py index a72b29bdbe..77f18c49c1 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py +++ b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py @@ -1,4 +1,9 @@ +import os +import sys +import json import collections +import tempfile +import datetime import ftrack_api @@ -13,6 +18,8 @@ from openpype.lib import ( from openpype_modules.ftrack.lib import BaseAction, statics_icon from openpype_modules.ftrack.lib.avalon_sync import create_chunks +NOT_SYNCHRONIZED_TITLE = "Not synchronized" + class FillWorkfileAttributeAction(BaseAction): """Action fill work filename into custom attribute on tasks. @@ -44,24 +51,24 @@ class FillWorkfileAttributeAction(BaseAction): return is_valid def launch(self, session, entities, event): - task_entities = [] - other_entities = [] + # Separate entities and get project entity project_entity = None - project_selected = False for entity in entities: if project_entity is None: project_entity = self.get_project_from_entity(entity) - - ent_type_low = entity.entity_type.lower() - if ent_type_low == "project": - project_selected = True break - elif ent_type_low == "task": - task_entities.append(entity) - else: - other_entities.append(entity) + if not project_entity: + return { + "message": ( + "Couldn't find project entity." + " Could be an issue with permissions." + ), + "success": False + } + # Get project settings and check if custom attribute where workfile + # should be set is defined. project_name = project_entity["full_name"] project_settings = get_project_settings(project_name) custom_attribute_key = ( @@ -77,12 +84,16 @@ class FillWorkfileAttributeAction(BaseAction): "message": "Custom attribute key is not set in settings" } + # Try to find the custom attribute + # - get Task type object id task_obj_type = session.query( "select id from ObjectType where name is \"Task\"" ).one() + # - get text custom attribute type text_type = session.query( "select id from CustomAttributeType where name is \"text\"" ).one() + # - find the attribute attr_conf = session.query( ( "select id, key from CustomAttributeConfiguration" @@ -101,33 +112,184 @@ class FillWorkfileAttributeAction(BaseAction): ).format(custom_attribute_key) } + # Store report information + report = collections.defaultdict(list) + user_entity = session.query( + "User where id is {}".format(event["source"]["user"]["id"]) + ).one() + job_entity = session.create("Job", { + "user": user_entity, + "status": "running", + "data": json.dumps({ + "description": "(0/3) Fill of workfiles started" + }) + }) + session.commit() + + try: + self.in_job_process( + session, + entities, + job_entity, + project_entity, + project_settings, + attr_conf, + report + ) + except Exception: + self.log.error( + "Fill of workfiles to custom attribute failed", exc_info=True + ) + session.rollback() + + description = "Fill of workfiles Failed (Download traceback)" + self.add_traceback_to_job( + job_entity, session, sys.exc_info(), description + ) + return { + "message": ( + "Fill of workfiles failed." + " Check job for more information" + ), + "success": False + } + + job_entity["status"] = "done" + job_entity["data"] = json.dumps({ + "description": "Fill of workfiles completed." + }) + session.commit() + if report: + temp_obj = tempfile.NamedTemporaryFile( + mode="w", + prefix="openpype_ftrack_", + suffix=".json", + delete=False + ) + temp_obj.close() + temp_filepath = temp_obj.name + with open(temp_filepath, "w") as temp_file: + json.dump(report, temp_file) + + component_name = "{}_{}".format( + "FillWorkfilesReport", + datetime.datetime.now().strftime("%y-%m-%d-%H%M") + ) + self.add_file_component_to_job( + job_entity, session, temp_filepath, component_name + ) + # Delete temp file + os.remove(temp_filepath) + self._show_report(event, report, project_name) + return { + "message": ( + "Fill of workfiles finished with few issues." + " Check job for more information" + ), + "success": True + } + + return { + "success": True, + "message": "Finished with filling of work filenames" + } + + def _show_report(self, event, report, project_name): + items = [] + title = "Fill workfiles report ({}):".format(project_name) + + for subtitle, lines in report.items(): + if items: + items.append({ + "type": "label", + "value": "---" + }) + items.append({ + "type": "label", + "value": "# {}".format(subtitle) + }) + items.append({ + "type": "label", + "value": '

    {}

    '.format("
    ".join(lines)) + }) + + self.show_interface( + items=items, + title=title, + event=event + ) + + def in_job_process( + self, + session, + entities, + job_entity, + project_entity, + project_settings, + attr_conf, + report + ): + task_entities = [] + other_entities = [] + project_selected = False + for entity in entities: + ent_type_low = entity.entity_type.lower() + if ent_type_low == "project": + project_selected = True + break + + elif ent_type_low == "task": + task_entities.append(entity) + else: + other_entities.append(entity) + + project_name = project_entity["full_name"] + + # Find matchin asset documents and map them by ftrack task entities + # - result stored to 'asset_docs_with_task_entities' is list with + # tuple `(asset document, [task entitis, ...])` dbcon = AvalonMongoDB() dbcon.Session["AVALON_PROJECT"] = project_name + # Quety all asset documents asset_docs = list(dbcon.find({"type": "asset"})) + job_entity["data"] = json.dumps({ + "description": "(1/3) Asset documents queried." + }) + session.commit() + + # When project is selected then we can query whole project if project_selected: - asset_docs_with_task_names = self._get_asset_docs_for_project( - session, project_entity, asset_docs + asset_docs_with_task_entities = self._get_asset_docs_for_project( + session, project_entity, asset_docs, report ) else: - asset_docs_with_task_names = self._get_tasks_for_selection( - session, other_entities, task_entities, asset_docs + asset_docs_with_task_entities = self._get_tasks_for_selection( + session, other_entities, task_entities, asset_docs, report ) + job_entity["data"] = json.dumps({ + "description": "(2/3) Queried related task entities." + }) + session.commit() + + # Keep placeholders in the template unfilled host_name = "{host}" + extension = "{ext}" project_doc = dbcon.find_one({"type": "project"}) project_settings = get_project_settings(project_name) anatomy = Anatomy(project_name) templates_by_key = {} operations = [] - for asset_doc, task_entities in asset_docs_with_task_names: + for asset_doc, task_entities in asset_docs_with_task_entities: for task_entity in task_entities: workfile_data = get_workdir_data( project_doc, asset_doc, task_entity["name"], host_name ) + # Use version 1 for each workfile workfile_data["version"] = 1 - workfile_data["ext"] = "{ext}" + workfile_data["ext"] = extension task_type = workfile_data["task"]["type"] template_key = get_workfile_template_key( @@ -166,22 +328,40 @@ class FillWorkfileAttributeAction(BaseAction): session.recorded_operations.push(op) session.commit() - return True + job_entity["data"] = json.dumps({ + "description": "(3/3) Set custom attribute values." + }) + session.commit() + + def _get_entity_path(self, entity): + path_items = [] + for item in entity["link"]: + if item["type"].lower() != "project": + path_items.append(item["name"]) + return "/".join(path_items) + + def _get_asset_docs_for_project( + self, session, project_entity, asset_docs, report + ): + asset_docs_task_names = {} - def _get_asset_docs_for_project(self, session, project_entity, asset_docs): - asset_docs_task_names = collections.defaultdict(list) for asset_doc in asset_docs: asset_data = asset_doc["data"] - asset_tasks = asset_data.get("tasks") ftrack_id = asset_data.get("ftrackId") - if not asset_tasks or not ftrack_id: + if not ftrack_id: + hierarchy = list(asset_data.get("parents") or []) + hierarchy.append(asset_doc["name"]) + path = "/".join(hierarchy) + report[NOT_SYNCHRONIZED_TITLE].append(path) continue - asset_docs_task_names[ftrack_id].append( - (asset_doc, list(asset_tasks.keys())) + + asset_tasks = asset_data.get("tasks") or {} + asset_docs_task_names[ftrack_id] = ( + asset_doc, list(asset_tasks.keys()) ) task_entities = session.query(( - "select id, name, parent_id from Task where project_id is {}" + "select id, name, parent_id, link from Task where project_id is {}" ).format(project_entity["id"])).all() task_entities_by_parent_id = collections.defaultdict(list) for task_entity in task_entities: @@ -189,21 +369,23 @@ class FillWorkfileAttributeAction(BaseAction): task_entities_by_parent_id[parent_id].append(task_entity) output = [] - for ftrack_id, items in asset_docs_task_names.items(): - for item in items: - asset_doc, task_names = item - valid_task_entities = [] - for task_entity in task_entities_by_parent_id[ftrack_id]: - if task_entity["name"] in task_names: - valid_task_entities.append(task_entity) + for ftrack_id, item in asset_docs_task_names.items(): + asset_doc, task_names = item + valid_task_entities = [] + for task_entity in task_entities_by_parent_id[ftrack_id]: + if task_entity["name"] in task_names: + valid_task_entities.append(task_entity) + else: + path = self._get_entity_path(task_entity) + report[NOT_SYNCHRONIZED_TITLE].append(path) - if valid_task_entities: - output.append((asset_doc, valid_task_entities)) + if valid_task_entities: + output.append((asset_doc, valid_task_entities)) return output def _get_tasks_for_selection( - self, session, other_entities, task_entities, asset_docs + self, session, other_entities, task_entities, asset_docs, report ): all_tasks = object() asset_docs_by_ftrack_id = {} @@ -216,13 +398,13 @@ class FillWorkfileAttributeAction(BaseAction): if ftrack_id: asset_docs_by_ftrack_id[ftrack_id] = asset_doc - missing_docs = set() + missing_doc_ftrack_ids = {} all_tasks_ids = set() task_names_by_ftrack_id = collections.defaultdict(list) for other_entity in other_entities: ftrack_id = other_entity["id"] if ftrack_id not in asset_docs_by_ftrack_id: - missing_docs.add(ftrack_id) + missing_doc_ftrack_ids[ftrack_id] = None continue all_tasks_ids.add(ftrack_id) task_names_by_ftrack_id[ftrack_id] = all_tasks @@ -230,21 +412,18 @@ class FillWorkfileAttributeAction(BaseAction): for task_entity in task_entities: parent_id = task_entity["parent_id"] if parent_id not in asset_docs_by_ftrack_id: - missing_docs.add(parent_id) + missing_doc_ftrack_ids[parent_id] = None continue if all_tasks_ids not in all_tasks_ids: task_names_by_ftrack_id[ftrack_id].append(task_entity["name"]) ftrack_ids = set() - asset_doc_with_task_names_by_id = collections.defaultdict(list) + asset_doc_with_task_names_by_id = {} for ftrack_id, task_names in task_names_by_ftrack_id.items(): asset_doc = asset_docs_by_ftrack_id[ftrack_id] asset_data = asset_doc["data"] - asset_tasks = asset_data.get("tasks") - if not asset_tasks: - # TODO add to report - continue + asset_tasks = asset_data.get("tasks") or {} if task_names is all_tasks: task_names = list(asset_tasks.keys()) @@ -253,15 +432,19 @@ class FillWorkfileAttributeAction(BaseAction): for task_name in task_names: if task_name in asset_tasks: new_task_names.append(task_name) - else: - # TODO add report - pass + continue + + if ftrack_id not in missing_doc_ftrack_ids: + missing_doc_ftrack_ids[ftrack_id] = [] + if missing_doc_ftrack_ids[ftrack_id] is not None: + missing_doc_ftrack_ids[ftrack_id].append(task_name) + task_names = new_task_names if task_names: ftrack_ids.add(ftrack_id) - asset_doc_with_task_names_by_id[ftrack_id].append( - (asset_doc, task_names) + asset_doc_with_task_names_by_id[ftrack_id] = ( + asset_doc, task_names ) task_entities = session.query(( @@ -273,15 +456,37 @@ class FillWorkfileAttributeAction(BaseAction): task_entitiy_by_parent_id[parent_id].append(task_entity) output = [] - for ftrack_id, items in asset_doc_with_task_names_by_id.items(): - for item in items: - asset_doc, task_names = item - valid_task_entities = [] - for task_entity in task_entitiy_by_parent_id[ftrack_id]: - if task_entity["name"] in task_names: - valid_task_entities.append(task_entity) - if valid_task_entities: - output.append((asset_doc, valid_task_entities)) + for ftrack_id, item in asset_doc_with_task_names_by_id.items(): + asset_doc, task_names = item + valid_task_entities = [] + for task_entity in task_entitiy_by_parent_id[ftrack_id]: + if task_entity["name"] in task_names: + valid_task_entities.append(task_entity) + else: + if ftrack_id not in missing_doc_ftrack_ids: + missing_doc_ftrack_ids[ftrack_id] = [] + if missing_doc_ftrack_ids[ftrack_id] is not None: + missing_doc_ftrack_ids[ftrack_id].append(task_name) + if valid_task_entities: + output.append((asset_doc, valid_task_entities)) + + # Store report information about not synchronized entities + if missing_doc_ftrack_ids: + missing_entities = session.query( + "select id, link from TypedContext where id in ({})".format( + self.join_query_keys(missing_doc_ftrack_ids.keys()) + ) + ).all() + for missing_entity in missing_entities: + path = self._get_entity_path(missing_entity) + task_names = missing_doc_ftrack_ids[missing_entity["id"]] + if task_names is None: + report[NOT_SYNCHRONIZED_TITLE].append(path) + else: + for task_name in task_names: + task_path = "/".join([path, task_name]) + report[NOT_SYNCHRONIZED_TITLE].append(task_path) + return output From fe8caa3b3aef5b78bf76fe7ff8fce5c37b92227a Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 19:24:11 +0100 Subject: [PATCH 285/302] fix app key --- .../ftrack/event_handlers_user/action_fill_workfile_attr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py index 77f18c49c1..3888379e04 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py +++ b/openpype/modules/ftrack/event_handlers_user/action_fill_workfile_attr.py @@ -274,7 +274,7 @@ class FillWorkfileAttributeAction(BaseAction): session.commit() # Keep placeholders in the template unfilled - host_name = "{host}" + host_name = "{app}" extension = "{ext}" project_doc = dbcon.find_one({"type": "project"}) project_settings = get_project_settings(project_name) From d3dc406b905f0554e867e5447e2f71ec8de85862 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 19:27:05 +0100 Subject: [PATCH 286/302] use get_workdir_data in wokrfiles tool --- openpype/tools/workfiles/app.py | 36 ++++++--------------------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index 63958ac57b..da5524331a 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -27,7 +27,7 @@ from openpype.lib import ( save_workfile_data_to_doc, get_workfile_template_key, create_workdir_extra_folders, - get_system_general_anatomy_data + get_workdir_data ) from openpype.lib.avalon_context import ( update_current_task, @@ -48,6 +48,7 @@ def build_workfile_data(session): # Set work file data for template formatting asset_name = session["AVALON_ASSET"] task_name = session["AVALON_TASK"] + host_name = session["AVALON_APP"] project_doc = io.find_one( {"type": "project"}, { @@ -63,42 +64,17 @@ def build_workfile_data(session): "name": asset_name }, { + "name": True, "data.tasks": True, "data.parents": True } ) - - task_type = asset_doc["data"]["tasks"].get(task_name, {}).get("type") - - project_task_types = project_doc["config"]["tasks"] - task_short = project_task_types.get(task_type, {}).get("short_name") - - asset_parents = asset_doc["data"]["parents"] - parent_name = project_doc["name"] - if asset_parents: - parent_name = asset_parents[-1] - - data = { - "project": { - "name": project_doc["name"], - "code": project_doc["data"].get("code") - }, - "asset": asset_name, - "task": { - "name": task_name, - "type": task_type, - "short": task_short, - }, - "parent": parent_name, + data = get_workdir_data(project_doc, asset_doc, task_name, host_name) + data.update({ "version": 1, - "user": getpass.getuser(), "comment": "", "ext": None - } - - # add system general settings anatomy data - system_general_data = get_system_general_anatomy_data() - data.update(system_general_data) + }) return data From cbb7db98f7a917bf30a0159a6f3ae548a6a8a906 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Thu, 17 Mar 2022 19:36:50 +0100 Subject: [PATCH 287/302] OPENPYPE_DEBUG can be set to 1 to log debug messages --- openpype/cli.py | 14 +++++++------- openpype/lib/log.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/cli.py b/openpype/cli.py index 155e07dea3..cbeb7fef9b 100644 --- a/openpype/cli.py +++ b/openpype/cli.py @@ -101,7 +101,7 @@ def eventserver(debug, on linux and window service). """ if debug: - os.environ['OPENPYPE_DEBUG'] = "3" + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands().launch_eventservercli( ftrack_url, @@ -128,7 +128,7 @@ def webpublisherwebserver(debug, executable, upload_dir, host=None, port=None): Expect "pype.club" user created on Ftrack. """ if debug: - os.environ['OPENPYPE_DEBUG'] = "3" + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands().launch_webpublisher_webservercli( upload_dir=upload_dir, @@ -176,7 +176,7 @@ def publish(debug, paths, targets, gui): More than one path is allowed. """ if debug: - os.environ['OPENPYPE_DEBUG'] = '3' + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands.publish(list(paths), targets, gui) @@ -195,7 +195,7 @@ def remotepublishfromapp(debug, project, path, host, user=None, targets=None): More than one path is allowed. """ if debug: - os.environ['OPENPYPE_DEBUG'] = '3' + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands.remotepublishfromapp( project, path, host, user, targets=targets ) @@ -215,7 +215,7 @@ def remotepublish(debug, project, path, user=None, targets=None): More than one path is allowed. """ if debug: - os.environ['OPENPYPE_DEBUG'] = '3' + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands.remotepublish(project, path, user, targets=targets) @@ -240,7 +240,7 @@ def texturecopy(debug, project, asset, path): Nothing is written to database. """ if debug: - os.environ['OPENPYPE_DEBUG'] = '3' + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands().texture_copy(project, asset, path) @@ -409,7 +409,7 @@ def syncserver(debug, active_site): var OPENPYPE_LOCAL_ID set to 'active_site'. """ if debug: - os.environ['OPENPYPE_DEBUG'] = '3' + os.environ["OPENPYPE_DEBUG"] = "1" PypeCommands().syncserver(active_site) diff --git a/openpype/lib/log.py b/openpype/lib/log.py index a42faef008..98a3bae8e6 100644 --- a/openpype/lib/log.py +++ b/openpype/lib/log.py @@ -227,7 +227,7 @@ class PypeLogger: logger = logging.getLogger(name or "__main__") - if cls.pype_debug > 1: + if cls.pype_debug > 0: logger.setLevel(logging.DEBUG) else: logger.setLevel(logging.INFO) From c1200c16d5900d3b23af6406b43ced45385e58cd Mon Sep 17 00:00:00 2001 From: OpenPype Date: Thu, 17 Mar 2022 23:00:09 +0000 Subject: [PATCH 288/302] [Automated] Bump version --- CHANGELOG.md | 11 ++++++++++- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1da69f13..78ebf8f164 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,21 @@ # Changelog -## [3.9.1-nightly.2](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.1-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) [Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...HEAD) **🚀 Enhancements** +- General: Change how OPENPYPE\_DEBUG value is handled [\#2907](https://github.com/pypeclub/OpenPype/pull/2907) +- nuke: imageio adding ocio config version 1.2 [\#2897](https://github.com/pypeclub/OpenPype/pull/2897) +- Flame: support for comment with xml attribute overrides [\#2892](https://github.com/pypeclub/OpenPype/pull/2892) - Nuke: ExtractReviewSlate can handle more codes and profiles [\#2879](https://github.com/pypeclub/OpenPype/pull/2879) - Flame: sequence used for reference video [\#2869](https://github.com/pypeclub/OpenPype/pull/2869) **🐛 Bug fixes** +- General: Fix use of Anatomy roots [\#2904](https://github.com/pypeclub/OpenPype/pull/2904) +- Fixing gap detection in extract review [\#2902](https://github.com/pypeclub/OpenPype/pull/2902) - Pyblish Pype - ensure current state is correct when entering new group order [\#2899](https://github.com/pypeclub/OpenPype/pull/2899) - SceneInventory: Fix import of load function [\#2894](https://github.com/pypeclub/OpenPype/pull/2894) - Harmony - fixed creator issue [\#2891](https://github.com/pypeclub/OpenPype/pull/2891) @@ -32,6 +37,10 @@ - AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845) +### 📖 Documentation + +- Documentation: Change Photoshop & AfterEffects plugin path [\#2878](https://github.com/pypeclub/OpenPype/pull/2878) + **🚀 Enhancements** - General: Subset name filtering in ExtractReview outpus [\#2872](https://github.com/pypeclub/OpenPype/pull/2872) diff --git a/openpype/version.py b/openpype/version.py index 5eca7c1d90..a62afd1953 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.1-nightly.2" +__version__ = "3.9.1-nightly.3" diff --git a/pyproject.toml b/pyproject.toml index af448ed24c..71c0af0b4f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.1-nightly.2" # OpenPype +version = "3.9.1-nightly.3" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From 10d9b42c74ff84e747e3f61c97499f29f33fb45c Mon Sep 17 00:00:00 2001 From: OpenPype Date: Thu, 17 Mar 2022 23:40:09 +0000 Subject: [PATCH 289/302] [Automated] Release --- CHANGELOG.md | 4 ++-- openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78ebf8f164..f3c7820d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [3.9.1-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD) +## [3.9.1](https://github.com/pypeclub/OpenPype/tree/3.9.1) (2022-03-17) -[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...HEAD) +[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.0...3.9.1) **🚀 Enhancements** diff --git a/openpype/version.py b/openpype/version.py index a62afd1953..1ef25e3f48 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.9.1-nightly.3" +__version__ = "3.9.1" diff --git a/pyproject.toml b/pyproject.toml index 71c0af0b4f..7c09495a99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.9.1-nightly.3" # OpenPype +version = "3.9.1" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From a912c4db80729ab2b87c4b6c5c07403254e82cba Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Mar 2022 09:45:04 +0100 Subject: [PATCH 290/302] update avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index 7753d15507..64491fbbcf 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 7753d15507afadc143b7d49db8fcfaa6a29fed91 +Subproject commit 64491fbbcf89ba2a0b3a20d67d7486c6142232b3 From 5d25de8997c46c46ac2c6bdfeb36cf9e032266ec Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 18 Mar 2022 11:00:26 +0100 Subject: [PATCH 291/302] OP-2813 - added documentation how to run test file in IDE --- tests/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/README.md b/tests/README.md index bb1cdbdef8..d0b537d425 100644 --- a/tests/README.md +++ b/tests/README.md @@ -21,3 +21,27 @@ Specific location could be provided to this command as an argument, either as ab (eg. `python ${OPENPYPE_ROOT}/start.py start.py runtests ../tests/integration`) will trigger only tests in `integration` folder. See `${OPENPYPE_ROOT}/cli.py:runtests` for other arguments. + +Run in IDE: +----------- +If you would prefer to run/debug single file dirrectly in IDE of your choice, you might encounter issues with imports. +It would manifest like `KeyError: 'OPENPYPE_DATABASE_NAME'`. That means you are importing module that depends on OP to be running, eg. all expected variables are set. + +In some cases your tests might be so localized, that you don't care about all env vars to be set properly. +In that case you might add this dummy configuration BEFORE any imports in your test file +``` +import os +os.environ["AVALON_MONGO"] = "mongodb://localhost:27017" +os.environ["OPENPYPE_MONGO"] = "mongodb://localhost:27017" +os.environ["AVALON_DB"] = "avalon" +os.environ["OPENPYPE_DATABASE_NAME"] = "openpype" +os.environ["AVALON_TIMEOUT"] = '3000' +os.environ["OPENPYPE_DEBUG"] = "3" +os.environ["AVALON_CONFIG"] = "pype" +os.environ["AVALON_ASSET"] = "Asset" +os.environ["AVALON_PROJECT"] = "test_project" +``` +(AVALON_ASSET and AVALON_PROJECT values should exist in your environment) + +This might be enough to run your test file separately. Do not commit this skeleton though. +Use only when you know what you are doing! \ No newline at end of file From c83202a023484aaa6a9a64aaab63ad879d71ded7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 18 Mar 2022 11:13:10 +0100 Subject: [PATCH 292/302] OP-2813 - changed logic of parsing frames from names Adhering to clique standard FRAMES patter, eg pattern is separated by . It seems that this is most widely used (according to Discord). --- openpype/lib/delivery.py | 18 ++++---------- tests/unit/openpype/lib/test_delivery.py | 30 +++++++++++++++++++++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index 78d743003b..03abe5802c 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -4,7 +4,6 @@ import shutil import glob import clique import collections -import re def collect_frames(files): @@ -14,31 +13,24 @@ def collect_frames(files): Uses clique as most precise solution, used when anatomy template that created files is not known. - Depends that version substring starts with 'v' with any number of - numeric characters after. + Assumption is that frames are separated by '.', negative frames are not + allowed. Args: files(list) or (set with single value): list of source paths Returns: (dict): {'/asset/subset_v001.0001.png': '0001', ....} """ - collections, remainder = clique.assemble(files, minimum_items=1) + patterns = [clique.PATTERNS["frames"]] + collections, remainder = clique.assemble(files, minimum_items=1, + patterns=patterns) - real_file_name = None sources_and_frames = {} - if len(files) == 1: - real_file_name = list(files)[0] - sources_and_frames[real_file_name] = None - if collections: for collection in collections: src_head = collection.head src_tail = collection.tail - # version recognized as a collection - if re.match(".*([a-zA-Z0-9]%[0-9]+d).*", collection.format()): - continue - for index in collection.indexes: src_frame = collection.format("{padding}") % index src_file_name = "{}{}{}".format(src_head, src_frame, diff --git a/tests/unit/openpype/lib/test_delivery.py b/tests/unit/openpype/lib/test_delivery.py index 871ea95df7..04a71655e3 100644 --- a/tests/unit/openpype/lib/test_delivery.py +++ b/tests/unit/openpype/lib/test_delivery.py @@ -47,6 +47,18 @@ def test_collect_frames_single_sequence(): assert ret == expected, "Not matching" +def test_collect_frames_single_sequence_negative(): + files = ["Asset_renderCompositingMain_v001.-0000.png"] + ret = collect_frames(files) + + expected = { + "Asset_renderCompositingMain_v001.-0000.png": None + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence_shot(): files = ["testing_sh010_workfileCompositing_v001.aep"] ret = collect_frames(files) @@ -59,12 +71,24 @@ def test_collect_frames_single_sequence_shot(): assert ret == expected, "Not matching" +def test_collect_frames_single_sequence_numbers(): + files = ["PRJ_204_430_0005_renderLayoutMain_v001.0001.exr"] + ret = collect_frames(files) + + expected = { + "PRJ_204_430_0005_renderLayoutMain_v001.0001.exr": "0001" + } + + print(ret) + assert ret == expected, "Not matching" + + def test_collect_frames_single_sequence_shot_with_frame(): files = ["testing_sh010_workfileCompositing_000_v001.aep"] ret = collect_frames(files) expected = { - "testing_sh010_workfileCompositing_000_v001.aep": "000" + "testing_sh010_workfileCompositing_000_v001.aep": None } print(ret) @@ -88,7 +112,7 @@ def test_collect_frames_single_sequence_different_format(): ret = collect_frames(files) expected = { - "Asset.v001.renderCompositingMain_0000.png": "0000" + "Asset.v001.renderCompositingMain_0000.png": None } print(ret) @@ -100,7 +124,7 @@ def test_collect_frames_single_sequence_withhout_version(): ret = collect_frames(files) expected = { - "pngv001.renderCompositingMain_0000.png": "0000" + "pngv001.renderCompositingMain_0000.png": None } print(ret) From 55087ec5b849eb27496ea72c06fbdf5f55cb057d Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 18 Mar 2022 12:27:15 +0100 Subject: [PATCH 293/302] OP-2813 - fix typo --- tests/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/README.md b/tests/README.md index d0b537d425..69828cdbc2 100644 --- a/tests/README.md +++ b/tests/README.md @@ -24,7 +24,7 @@ See `${OPENPYPE_ROOT}/cli.py:runtests` for other arguments. Run in IDE: ----------- -If you would prefer to run/debug single file dirrectly in IDE of your choice, you might encounter issues with imports. +If you prefer to run/debug single file directly in IDE of your choice, you might encounter issues with imports. It would manifest like `KeyError: 'OPENPYPE_DATABASE_NAME'`. That means you are importing module that depends on OP to be running, eg. all expected variables are set. In some cases your tests might be so localized, that you don't care about all env vars to be set properly. From 6eaf7017eb66d85ca0089a84dbd63ebd874cf9f1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 14:20:20 +0100 Subject: [PATCH 294/302] replaced 'format_template_with_optional_keys' with 'StringTemplate' --- .../plugins/publish/collect_texture.py | 17 ++++++++++------- .../tvpaint/plugins/load/load_workfile.py | 18 ++++++++++-------- openpype/lib/delivery.py | 15 ++++++++------- .../action_delete_old_versions.py | 14 ++++++-------- openpype/pipeline/load/utils.py | 9 +++++---- openpype/plugins/publish/integrate_new.py | 12 +++++++----- 6 files changed, 46 insertions(+), 39 deletions(-) diff --git a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py index ea0b6cdf41..c1c48ec72d 100644 --- a/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py +++ b/openpype/hosts/standalonepublisher/plugins/publish/collect_texture.py @@ -3,9 +3,10 @@ import re import pyblish.api import json -from avalon.api import format_template_with_optional_keys - -from openpype.lib import prepare_template_data +from openpype.lib import ( + prepare_template_data, + StringTemplate, +) class CollectTextures(pyblish.api.ContextPlugin): @@ -110,8 +111,9 @@ class CollectTextures(pyblish.api.ContextPlugin): formatting_data.update(explicit_data) fill_pairs = prepare_template_data(formatting_data) - workfile_subset = format_template_with_optional_keys( - fill_pairs, self.workfile_subset_template) + workfile_subset = StringTemplate.format_strict_template( + self.workfile_subset_template, fill_pairs + ) asset_build = self._get_asset_build( repre_file, @@ -201,8 +203,9 @@ class CollectTextures(pyblish.api.ContextPlugin): formatting_data.update(explicit_data) fill_pairs = prepare_template_data(formatting_data) - subset = format_template_with_optional_keys( - fill_pairs, self.texture_subset_template) + subset = StringTemplate.format_strict_template( + self.texture_subset_template, fill_pairs + ) asset_build = self._get_asset_build( repre_file, diff --git a/openpype/hosts/tvpaint/plugins/load/load_workfile.py b/openpype/hosts/tvpaint/plugins/load/load_workfile.py index 33e2a76cc9..11219320ca 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_workfile.py +++ b/openpype/hosts/tvpaint/plugins/load/load_workfile.py @@ -4,7 +4,8 @@ import os from avalon import api, io from openpype.lib import ( get_workfile_template_key_from_context, - get_workdir_data + get_workdir_data, + StringTemplate, ) from openpype.api import Anatomy from openpype.hosts.tvpaint.api import lib, pipeline, plugin @@ -69,7 +70,7 @@ class LoadWorkfile(plugin.Loader): data["root"] = anatomy.roots data["user"] = getpass.getuser() - template = anatomy.templates[template_key]["file"] + file_template = anatomy.templates[template_key]["file"] # Define saving file extension if current_file: @@ -81,11 +82,12 @@ class LoadWorkfile(plugin.Loader): data["ext"] = extension - work_root = api.format_template_with_optional_keys( - data, anatomy.templates[template_key]["folder"] + folder_template = anatomy.templates[template_key]["folder"] + work_root = StringTemplate.format_strict_template( + folder_template, data ) version = api.last_workfile_with_version( - work_root, template, data, host.file_extensions() + work_root, file_template, data, host.file_extensions() )[1] if version is None: @@ -95,8 +97,8 @@ class LoadWorkfile(plugin.Loader): data["version"] = version - path = os.path.join( - work_root, - api.format_template_with_optional_keys(data, template) + filename = StringTemplate.format_strict_template( + file_template, data ) + path = os.path.join(work_root, filename) host.save_file(path) diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py index 03abe5802c..ffcfe9fa4d 100644 --- a/openpype/lib/delivery.py +++ b/openpype/lib/delivery.py @@ -5,6 +5,11 @@ import glob import clique import collections +from .path_templates import ( + StringTemplate, + TemplateUnsolved, +) + def collect_frames(files): """ @@ -52,8 +57,6 @@ def sizeof_fmt(num, suffix='B'): def path_from_representation(representation, anatomy): - from avalon import pipeline # safer importing - try: template = representation["data"]["template"] @@ -63,12 +66,10 @@ def path_from_representation(representation, anatomy): try: context = representation["context"] context["root"] = anatomy.roots - path = pipeline.format_template_with_optional_keys( - context, template - ) - path = os.path.normpath(path.replace("/", "\\")) + path = StringTemplate.format_strict_template(template, context) + return os.path.normpath(path) - except KeyError: + except TemplateUnsolved: # Template references unavailable data return None diff --git a/openpype/modules/ftrack/event_handlers_user/action_delete_old_versions.py b/openpype/modules/ftrack/event_handlers_user/action_delete_old_versions.py index c66d1819ac..1b694e25f1 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_delete_old_versions.py +++ b/openpype/modules/ftrack/event_handlers_user/action_delete_old_versions.py @@ -5,11 +5,11 @@ import uuid import clique from pymongo import UpdateOne -from openpype_modules.ftrack.lib import BaseAction, statics_icon from avalon.api import AvalonMongoDB -from openpype.api import Anatomy -import avalon.pipeline +from openpype.api import Anatomy +from openpype.lib import StringTemplate, TemplateUnsolved +from openpype_modules.ftrack.lib import BaseAction, statics_icon class DeleteOldVersions(BaseAction): @@ -563,18 +563,16 @@ class DeleteOldVersions(BaseAction): try: context = representation["context"] context["root"] = anatomy.roots - path = avalon.pipeline.format_template_with_optional_keys( - context, template - ) + path = StringTemplate.format_strict_template(template, context) if "frame" in context: context["frame"] = self.sequence_splitter sequence_path = os.path.normpath( - avalon.pipeline.format_template_with_optional_keys( + StringTemplate.format_strict_template( context, template ) ) - except KeyError: + except (KeyError, TemplateUnsolved): # Template references unavailable data return (None, None) diff --git a/openpype/pipeline/load/utils.py b/openpype/pipeline/load/utils.py index 118f86a570..6d32c11cd7 100644 --- a/openpype/pipeline/load/utils.py +++ b/openpype/pipeline/load/utils.py @@ -525,7 +525,7 @@ def get_representation_path(representation, root=None, dbcon=None): """ - from openpype.lib import StringTemplate + from openpype.lib import StringTemplate, TemplateUnsolved if dbcon is None: dbcon = io @@ -542,13 +542,14 @@ def get_representation_path(representation, root=None, dbcon=None): try: context = representation["context"] context["root"] = root - template_obj = StringTemplate(template) - path = str(template_obj.format(context)) + path = StringTemplate.format_strict_template( + template, context + ) # Force replacing backslashes with forward slashed if not on # windows if platform.system().lower() != "windows": path = path.replace("\\", "/") - except KeyError: + except (TemplateUnsolved, KeyError): # Template references unavailable data return None diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index e8dab089af..6ca6125cb2 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -12,14 +12,15 @@ import shutil from pymongo import DeleteOne, InsertOne import pyblish.api from avalon import io -from avalon.api import format_template_with_optional_keys import openpype.api from datetime import datetime # from pype.modules import ModulesManager from openpype.lib.profiles_filtering import filter_profiles from openpype.lib import ( prepare_template_data, - create_hard_link + create_hard_link, + StringTemplate, + TemplateUnsolved ) # this is needed until speedcopy for linux is fixed @@ -854,9 +855,10 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): fill_pairs = prepare_template_data(fill_pairs) try: - filled_template = \ - format_template_with_optional_keys(fill_pairs, template) - except KeyError: + filled_template = StringTemplate.format_strict_template( + template, fill_pairs + ) + except (KeyError, TemplateUnsolved): keys = [] if fill_pairs: keys = fill_pairs.keys() From e961144969dccb207d6d7e7e2d270a7b5b45fbec Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 14:55:53 +0100 Subject: [PATCH 295/302] moved functions to get last workfile into avalon context lib functions --- openpype/lib/avalon_context.py | 126 ++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 3 deletions(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 26beba41ee..0b1d09908c 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -16,6 +16,7 @@ from openpype.settings import ( from .anatomy import Anatomy from .profiles_filtering import filter_profiles from .events import emit_event +from .path_templates import StringTemplate # avalon module is not imported at the top # - may not be in path at the time of pype.lib initialization @@ -1735,8 +1736,6 @@ def get_custom_workfile_template_by_context( context. (Existence of formatted path is not validated.) """ - from openpype.lib import filter_profiles - if anatomy is None: anatomy = Anatomy(project_doc["name"]) @@ -1759,7 +1758,9 @@ def get_custom_workfile_template_by_context( # there are some anatomy template strings if matching_item: template = matching_item["path"][platform.system().lower()] - return template.format(**anatomy_context_data) + return StringTemplate.format_strict_template( + template, anatomy_context_data + ) return None @@ -1847,3 +1848,122 @@ def get_custom_workfile_template(template_profiles): io.Session["AVALON_TASK"], io ) + + +def get_last_workfile_with_version( + workdir, file_template, fill_data, extensions +): + """Return last workfile version. + + Args: + workdir(str): Path to dir where workfiles are stored. + file_template(str): Template of file name. + fill_data(dict): Data for filling template. + extensions(list, tuple): All allowed file extensions of workfile. + + Returns: + tuple: Last workfile with version if there is any otherwise + returns (None, None). + """ + if not os.path.exists(workdir): + return None, None + + # Fast match on extension + filenames = [ + filename + for filename in os.listdir(workdir) + if os.path.splitext(filename)[1] in extensions + ] + + # Build template without optionals, version to digits only regex + # and comment to any definable value. + _ext = [] + for ext in extensions: + if not ext.startswith("."): + ext = "." + ext + # Escape dot for regex + ext = "\\" + ext + _ext.append(ext) + ext_expression = "(?:" + "|".join(_ext) + ")" + + # Replace `.{ext}` with `{ext}` so we are sure there is not dot at the end + file_template = re.sub(r"\.?{ext}", ext_expression, file_template) + # Replace optional keys with optional content regex + file_template = re.sub(r"<.*?>", r".*?", file_template) + # Replace `{version}` with group regex + file_template = re.sub(r"{version.*?}", r"([0-9]+)", file_template) + file_template = re.sub(r"{comment.*?}", r".+?", file_template) + filename = StringTemplate.format_strict_template(file_template, fill_data) + + # Match with ignore case on Windows due to the Windows + # OS not being case-sensitive. This avoids later running + # into the error that the file did exist if it existed + # with a different upper/lower-case. + kwargs = {} + if platform.system().lower() == "windows": + kwargs["flags"] = re.IGNORECASE + + # Get highest version among existing matching files + version = None + output_filenames = [] + for filename in sorted(filenames): + match = re.match(file_template, filename, **kwargs) + if not match: + continue + + file_version = int(match.group(1)) + if version is None or file_version > version: + output_filenames[:] = [] + version = file_version + + if file_version == version: + output_filenames.append(filename) + + output_filename = None + if output_filenames: + if len(output_filenames) == 1: + output_filename = output_filenames[0] + else: + last_time = None + for _output_filename in output_filenames: + full_path = os.path.join(workdir, _output_filename) + mod_time = os.path.getmtime(full_path) + if last_time is None or last_time < mod_time: + output_filename = _output_filename + last_time = mod_time + + return output_filename, version + + +def get_last_workfile( + workdir, file_template, fill_data, extensions, full_path=False +): + """Return last workfile filename. + + Returns file with version 1 if there is not workfile yet. + + Args: + workdir(str): Path to dir where workfiles are stored. + file_template(str): Template of file name. + fill_data(dict): Data for filling template. + extensions(list, tuple): All allowed file extensions of workfile. + full_path(bool): Full path to file is returned if set to True. + + Returns: + str: Last or first workfile as filename of full path to filename. + """ + filename, version = get_last_workfile_with_version( + workdir, file_template, fill_data, extensions + ) + if filename is None: + data = copy.deepcopy(fill_data) + data["version"] = 1 + data.pop("comment", None) + if not data.get("ext"): + data["ext"] = extensions[0] + filename = StringTemplate.format_strict_template(file_template, data) + + if full_path: + return os.path.normpath(os.path.join(workdir, filename)) + + return filename From 65bc619bcb1238ef917060c46e31f771dec6d9c7 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 14:57:02 +0100 Subject: [PATCH 296/302] use moved workfile functions --- openpype/hosts/tvpaint/plugins/load/load_workfile.py | 7 +++---- openpype/lib/__init__.py | 4 ++++ openpype/lib/applications.py | 5 +++-- openpype/tools/workfiles/app.py | 8 ++++---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/tvpaint/plugins/load/load_workfile.py b/openpype/hosts/tvpaint/plugins/load/load_workfile.py index 11219320ca..d224cfc390 100644 --- a/openpype/hosts/tvpaint/plugins/load/load_workfile.py +++ b/openpype/hosts/tvpaint/plugins/load/load_workfile.py @@ -1,11 +1,11 @@ -import getpass import os from avalon import api, io from openpype.lib import ( + StringTemplate, get_workfile_template_key_from_context, get_workdir_data, - StringTemplate, + get_last_workfile_with_version, ) from openpype.api import Anatomy from openpype.hosts.tvpaint.api import lib, pipeline, plugin @@ -68,7 +68,6 @@ class LoadWorkfile(plugin.Loader): data = get_workdir_data(project_doc, asset_doc, task_name, host_name) data["root"] = anatomy.roots - data["user"] = getpass.getuser() file_template = anatomy.templates[template_key]["file"] @@ -86,7 +85,7 @@ class LoadWorkfile(plugin.Loader): work_root = StringTemplate.format_strict_template( folder_template, data ) - version = api.last_workfile_with_version( + version = get_last_workfile_with_version( work_root, file_template, data, host.file_extensions() )[1] diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index b8502ae718..1ebafbb2d2 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -114,6 +114,8 @@ from .avalon_context import ( get_workdir_data, get_workdir, get_workdir_with_workdir_data, + get_last_workfile_with_version, + get_last_workfile, create_workfile_doc, save_workfile_data_to_doc, @@ -263,6 +265,8 @@ __all__ = [ "get_workdir_data", "get_workdir", "get_workdir_with_workdir_data", + "get_last_workfile_with_version", + "get_last_workfile", "create_workfile_doc", "save_workfile_data_to_doc", diff --git a/openpype/lib/applications.py b/openpype/lib/applications.py index ef175ac89a..557c016d74 100644 --- a/openpype/lib/applications.py +++ b/openpype/lib/applications.py @@ -28,7 +28,8 @@ from .local_settings import get_openpype_username from .avalon_context import ( get_workdir_data, get_workdir_with_workdir_data, - get_workfile_template_key + get_workfile_template_key, + get_last_workfile ) from .python_module_tools import ( @@ -1609,7 +1610,7 @@ def _prepare_last_workfile(data, workdir): "ext": extensions[0] }) - last_workfile_path = avalon.api.last_workfile( + last_workfile_path = get_last_workfile( workdir, file_template, workdir_data, extensions, True ) diff --git a/openpype/tools/workfiles/app.py b/openpype/tools/workfiles/app.py index da5524331a..713992bc4b 100644 --- a/openpype/tools/workfiles/app.py +++ b/openpype/tools/workfiles/app.py @@ -2,7 +2,6 @@ import sys import os import re import copy -import getpass import shutil import logging import datetime @@ -27,7 +26,8 @@ from openpype.lib import ( save_workfile_data_to_doc, get_workfile_template_key, create_workdir_extra_folders, - get_workdir_data + get_workdir_data, + get_last_workfile_with_version ) from openpype.lib.avalon_context import ( update_current_task, @@ -441,7 +441,7 @@ class NameWindow(QtWidgets.QDialog): data["ext"] = data["ext"][1:] - version = api.last_workfile_with_version( + version = get_last_workfile_with_version( self.root, template, data, extensions )[1] @@ -469,7 +469,7 @@ class NameWindow(QtWidgets.QDialog): # Log warning if idx == 0: log.warning(( - "BUG: Function `last_workfile_with_version` " + "BUG: Function `get_last_workfile_with_version` " "didn't return last version." )) # Raise exception if even 100 version fallback didn't help From 4a8a7b86889d4af5dd9662b1331320a89c674c94 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 15:34:30 +0100 Subject: [PATCH 297/302] add headless argument --- .../deadline/repository/custom/plugins/GlobalJobPreLoad.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py index 82c2494e7a..eeb1f7744c 100644 --- a/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py +++ b/openpype/modules/deadline/repository/custom/plugins/GlobalJobPreLoad.py @@ -46,6 +46,7 @@ def inject_openpype_environment(deadlinePlugin): args = [ openpype_app, + "--headless", 'extractenvironments', export_url ] From 10c7fb21e48cdb281102068a6c3e2acf49feb1af Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 15:34:42 +0100 Subject: [PATCH 298/302] use headless in submit publish job --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 06505b4b47..fad4d14ea0 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -236,6 +236,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): environment["OPENPYPE_MONGO"] = mongo_url args = [ + "--headless", 'publish', roothless_metadata_path, "--targets", "deadline", From 4f643a2928bc2c49f96c5724d10e63cff254ce7b Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Mar 2022 16:46:40 +0100 Subject: [PATCH 299/302] Only raise minor version if `Bump Minor` label is found --- tools/ci_tools.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/ci_tools.py b/tools/ci_tools.py index aeb367af38..3e1e3d8d02 100644 --- a/tools/ci_tools.py +++ b/tools/ci_tools.py @@ -8,8 +8,12 @@ import os def get_release_type_github(Log, github_token): # print(Log) - minor_labels = ["type: feature", "type: deprecated"] - patch_labels = ["type: enhancement", "type: bug"] + minor_labels = ["Bump Minor"] + # patch_labels = [ + # "type: enhancement", + # "type: bug", + # "type: deprecated", + # "type: Feature"] g = Github(github_token) repo = g.get_repo("pypeclub/OpenPype") @@ -28,9 +32,12 @@ def get_release_type_github(Log, github_token): if any(label in labels for label in minor_labels): return "minor" - - if any(label in labels for label in patch_labels): + else return "patch" + + #TODO: if all is working fine, this part can be cleaned up eventually + # if any(label in labels for label in patch_labels): + # return "patch" return None From f804b5f7e193e174c2cee885d14d3459ae52fbd9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Mar 2022 16:56:36 +0100 Subject: [PATCH 300/302] fix typo --- tools/ci_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci_tools.py b/tools/ci_tools.py index 3e1e3d8d02..5a28d3fd66 100644 --- a/tools/ci_tools.py +++ b/tools/ci_tools.py @@ -32,7 +32,7 @@ def get_release_type_github(Log, github_token): if any(label in labels for label in minor_labels): return "minor" - else + else: return "patch" #TODO: if all is working fine, this part can be cleaned up eventually From 952fc093682685a1893a0cb7615eb2e6ab197071 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 18 Mar 2022 16:57:07 +0100 Subject: [PATCH 301/302] fix hound nitpicking --- tools/ci_tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci_tools.py b/tools/ci_tools.py index 5a28d3fd66..4c59cd6af6 100644 --- a/tools/ci_tools.py +++ b/tools/ci_tools.py @@ -35,7 +35,7 @@ def get_release_type_github(Log, github_token): else: return "patch" - #TODO: if all is working fine, this part can be cleaned up eventually + # TODO: if all is working fine, this part can be cleaned up eventually # if any(label in labels for label in patch_labels): # return "patch" From 32bf6cb3e0d2f44a9378fbba1954fe99d6338fe1 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Fri, 18 Mar 2022 18:02:34 +0100 Subject: [PATCH 302/302] fix last workfile --- openpype/lib/avalon_context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/lib/avalon_context.py b/openpype/lib/avalon_context.py index 0b1d09908c..8e9fff5f67 100644 --- a/openpype/lib/avalon_context.py +++ b/openpype/lib/avalon_context.py @@ -1893,7 +1893,9 @@ def get_last_workfile_with_version( # Replace `{version}` with group regex file_template = re.sub(r"{version.*?}", r"([0-9]+)", file_template) file_template = re.sub(r"{comment.*?}", r".+?", file_template) - filename = StringTemplate.format_strict_template(file_template, fill_data) + file_template = StringTemplate.format_strict_template( + file_template, fill_data + ) # Match with ignore case on Windows due to the Windows # OS not being case-sensitive. This avoids later running